【发布时间】:2017-03-31 16:51:43
【问题描述】:
我有以下字符串:
brasília
我需要转换为:
brasilia
没有 ´ 口音!
我可以在 BigQuery 上做什么?
谢谢!
【问题讨论】:
-
刚刚宣布用于 StandardSQL:normalize
标签: google-bigquery
我有以下字符串:
brasília
我需要转换为:
brasilia
没有 ´ 口音!
我可以在 BigQuery 上做什么?
谢谢!
【问题讨论】:
标签: google-bigquery
试试下面的快速简单的选项:
#standardSQL
WITH lookups AS (
SELECT
'ç,æ,œ,á,é,í,ó,ú,à,è,ì,ò,ù,ä,ë,ï,ö,ü,ÿ,â,ê,î,ô,û,å,ø,Ø,Å,Á,À,Â,Ä,È,É,Ê,Ë,Í,Î,Ï,Ì,Ò,Ó,Ô,Ö,Ú,Ù,Û,Ü,Ÿ,Ç,Æ,Œ,ñ' AS accents,
'c,ae,oe,a,e,i,o,u,a,e,i,o,u,a,e,i,o,u,y,a,e,i,o,u,a,o,O,A,A,A,A,A,E,E,E,E,I,I,I,I,O,O,O,O,U,U,U,U,Y,C,AE,OE,n' AS latins
),
pairs AS (
SELECT accent, latin FROM lookups,
UNNEST(SPLIT(accents)) AS accent WITH OFFSET AS p1,
UNNEST(SPLIT(latins)) AS latin WITH OFFSET AS p2
WHERE p1 = p2
),
yourTableWithWords AS (
SELECT word FROM UNNEST(
SPLIT('brasília,ångström,aperçu,barège, beau idéal, belle époque, béguin, bête noire, bêtise, Bichon Frisé, blasé, blessèd, bobèche, boîte, bombé, Bön, Boötes, boutonnière, bric-à-brac, Brontë Beyoncé,El Niño')
) AS word
)
SELECT
word AS word_with_accent,
(SELECT STRING_AGG(IFNULL(latin, char), '')
FROM UNNEST(SPLIT(word, '')) char
LEFT JOIN pairs
ON char = accent) AS word_without_accent
FROM yourTableWithWords
输出是
word_with_accent word_without_accent
blessèd blessed
El Niño El Nino
belle époque belle epoque
boîte boite
Boötes Bootes
blasé blase
ångström angstrom
bobèche bobeche
barège barege
bric-à-brac bric-a-brac
bête noire bete noire
Bichon Frisé Bichon Frise
Brontë Beyoncé Bronte Beyonce
bêtise betise
beau idéal beau ideal
bombé bombe
brasília brasilia
boutonnière boutonniere
aperçu apercu
béguin beguin
Bön Bon
更新
下面是如何将此逻辑打包到 SQL UDF 中 - 所以可以调用 accent2latin(word) 来制造“魔法”
#standardSQL
CREATE TEMP FUNCTION accent2latin(word STRING) AS
((
WITH lookups AS (
SELECT
'ç,æ,œ,á,é,í,ó,ú,à,è,ì,ò,ù,ä,ë,ï,ö,ü,ÿ,â,ê,î,ô,û,å,ø,Ø,Å,Á,À,Â,Ä,È,É,Ê,Ë,Í,Î,Ï,Ì,Ò,Ó,Ô,Ö,Ú,Ù,Û,Ü,Ÿ,Ç,Æ,Œ,ñ' AS accents,
'c,ae,oe,a,e,i,o,u,a,e,i,o,u,a,e,i,o,u,y,a,e,i,o,u,a,o,O,A,A,A,A,A,E,E,E,E,I,I,I,I,O,O,O,O,U,U,U,U,Y,C,AE,OE,n' AS latins
),
pairs AS (
SELECT accent, latin FROM lookups,
UNNEST(SPLIT(accents)) AS accent WITH OFFSET AS p1,
UNNEST(SPLIT(latins)) AS latin WITH OFFSET AS p2
WHERE p1 = p2
)
SELECT STRING_AGG(IFNULL(latin, char), '')
FROM UNNEST(SPLIT(word, '')) char
LEFT JOIN pairs
ON char = accent
));
WITH yourTableWithWords AS (
SELECT word FROM UNNEST(
SPLIT('brasília,ångström,aperçu,barège, beau idéal, belle époque, béguin, bête noire, bêtise, Bichon Frisé, blasé, blessèd, bobèche, boîte, bombé, Bön, Boötes, boutonnière, bric-à-brac, Brontë Beyoncé,El Niño')
) AS word
)
SELECT
word AS word_with_accent,
accent2latin(word) AS word_without_accent
FROM yourTableWithWords
【讨论】:
值得一提的是,您要求的是unicode text normalization 的简化案例。许多语言在其标准库中都有此功能(例如,Java)。一种好的方法是插入您已经规范化的文本 BigQuery。如果这不起作用 - 例如,因为您需要保留原始文本并且担心点击 BigQuery's row size limit - 那么您需要在查询中动态进行规范化。
一些数据库实现了各种完整性的 Unicode 规范化(例如,PostgreSQL's unaccent method、PrestoDB's normalize method)以用于查询。不幸的是,BigQuery 不是其中之一。在撰写本文时,BigQuery 中没有文本规范化功能。这个答案的实现有点像“滚动你自己的非口音”。当 BigQuery 发布官方功能时,每个人都应该使用它!
假设您需要在查询中进行规范化(Google 还没有为此提供功能),这些是一些合理的选择。
NORMALIZE
Google 现在推出了NORMALIZE 功能。 (感谢 cmets 中的 @WillianFuks 进行标记!)现在这是文本规范化的明显选择。例如:
SELECT REGEXP_REPLACE(NORMALIZE(text, NFD), r"\pM", '') FROM yourtable;
简要说明了它的工作原理以及为什么在 cmets 中需要调用 REGEXP_REPLACE。
我留下了其他方法供参考。
REGEXP_REPLACE 和 REPLACE
我使用REGEXP_REPLACE 在旧版 SQL 中实现了仅小写的文本规范化。 (标准 SQL 中的类比是不言而喻的。)我使用以下查询在一个 28M 行的大表中对平均长度约为 1K 的文本字段进行了一些测试:
SELECT id, text FROM
(SELECT
id,
CASE
WHEN REGEXP_CONTAINS(LOWER(text), r"[àáâäåæçèéêëìíîïòóôöøùúûüÿœ]") THEN
REGEXP_REPLACE(
REGEXP_REPLACE(
REGEXP_REPLACE(
REGEXP_REPLACE(
REGEXP_REPLACE(
REPLACE(REPLACE(REPLACE(REPLACE(LOWER(text), 'œ', 'ce'), 'ÿ', 'y'), 'ç', 'c'), 'æ', 'ae'),
r"[ùúûü]", 'u'),
r"[òóôöø]", 'o'),
r"[ìíîï]", 'i'),
r"[èéêë]", 'e'),
r"[àáâäå]", 'a')
ELSE
LOWER(text)
END AS text
FROM
yourtable ORDER BY id LIMIT 10);
对比:
WITH lookups AS (
SELECT
'ç,æ,œ,á,é,í,ó,ú,à,è,ì,ò,ù,ä,ë,ï,ö,ü,ÿ,â,ê,î,ô,û,å,ø,ñ' AS accents,
'c,ae,oe,a,e,i,o,u,a,e,i,o,u,a,e,i,o,u,y,a,e,i,o,u,a,o,n' AS latins
),
pairs AS (
SELECT accent, latin FROM lookups,
UNNEST(SPLIT(accents)) AS accent WITH OFFSET AS p1,
UNNEST(SPLIT(latins)) AS latin WITH OFFSET AS p2
WHERE p1 = p2
)
SELECT foo FROM (
SELECT
id,
(SELECT STRING_AGG(IFNULL(latin, char), '') AS foo FROM UNNEST(SPLIT(LOWER(text), '')) char LEFT JOIN pairs ON char=accent) AS foo
FROM
yourtable ORDER BY id LIMIT 10);
平均而言,REGEXP_REPLACE 实现的运行时间约为 2.9 秒;基于数组的实现运行了大约 12.5 秒。
REGEXP_REPLACE
让我想到这个问题的是一个搜索用例。对于这个用例,我可以规范化我的语料库文本,使其看起来更像我的查询,或者我可以“非规范化”我的查询,使其看起来更像我的文本。以上描述了第一种方法的实现。这描述了第二个的实现。
搜索单个词时,可以使用REGEXP_MATCH 匹配函数,只需使用以下模式更新查询:
a -> [aàáaâäãåā]
e -> [eèéêëēėę]
i -> [iîïíīįì]
o -> [oôöòóøōõ]
u -> [uûüùúū]
y -> [yÿ]
s -> [sßśš]
l -> [lł]
z -> [zžźż]
c -> [cçćč]
n -> [nñń]
æ -> (?:æ|ae)
œ -> (?:œ|ce)
所以查询“hello”看起来像这样,作为一个正则表达式:
r"h[eèéêëēėę][lł][lł][oôöòóøōõ]"
将单词转换成这个正则表达式在任何语言中都应该是相当简单的。这不是已发布问题的解决方案——“如何在 BigQuery 中删除重音符号?” -- 而是一个相关用例的解决方案,它可能将人们(比如我!)带到了这个页面。
【讨论】:
NORMALIZE函数时,你是不是直接调用NORMALIZE?默认情况下,该函数将从“变音符号”字符中分解“字母”字符以创建重音字符的标准化序列表示,但不会删除变音符号。如果要删除变音符号,则需要执行另一个步骤;像REGEXP_REPLACE(s, r"[^\pL\pN\pP\pS\pZ]", '') 或REGEXP_REPLACE(s, r"[\pM]", '') 这样的东西可能会成功。 (有关此处使用的 unicode 字符类,请参阅 here。)
我喜欢this answer 的解释。您可以使用:
REGEXP_REPLACE(NORMALIZE(text, NFD), r'\pM', '')
举个简单的例子:
WITH data AS(
SELECT 'brasília / paçoca' AS text
)
SELECT
REGEXP_REPLACE(NORMALIZE(text, NFD), r'\pM', '') RemovedDiacritics
FROM data
巴西利亚/帕科卡
更新
有了新的字符串函数Translate,做起来就简单多了:
WITH data AS(
SELECT 'brasília / paçoca' AS text
)
SELECT
translate(text, "ŠŽšžŸÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðñòóôõöùúûüýÿ", "SZszYAAAAAACEEEEIIIIDNOOOOOUUUUYaaaaaaceeeeiiiidnooooouuuuyy") as RemovedDiacritics
FROM data
巴西利亚/帕科卡
【讨论】:
translate 现在应该成为公认的答案。
您可以致电REPLACE() 或REGEXP_REPLACE()。你可以在Remove accents/diacritics in a string in JavaScript找到一些正则表达式。
或者,您可以使用javascript UDF,但我希望它会更慢。
【讨论】: