0

我有一个存储在 BigQuery 中的约 4.7M 文档表。有些是纯文本,有些是 HTML。它们大约有 2k 个令牌,变化很大。我主要使用 DataPrep 进行处理。

我想提取这些标记并计算TF-IDF值。

令牌计数

更耗时的步骤之一是采取以下措施:

id, document
1, "foo bar foo baz"
2, "foo bar bar qux"

并将其变成这样:

id, word, count
1, foo, 2
1, bar, 1
1, baz, 1
2, foo, 1
2, bar, 2
2, qux, 1

一种方法是:

  1. 文件上的提取列表由{alphanum-underscore}+ id, wordlist 1, ["foo", "bar", "foo", "baz"] 2, ["foo", "bar", "bar", "qux"]
  2. 展平单词表 id, word 1, foo 1, bar 1, foo 1, baz 2, foo 2, bar 2, bar 2, qux
  3. 按组聚合:id、word、值:count() id, word, count 1, foo, 2 1, bar, 1 1, baz, 1 2, foo, 1 2, bar, 2 2, qux, 1

但是,第 2 步和第 3 步非常慢,尤其是对于大型文档。

理想情况下,我将能够拥有一个转换["foo", "bar", "foo", "baz"]{"foo":2, "bar":1, "baz":1}. 这不需要 flatten-then-group 操作来提取计数,并且随后的 flatten 会更小(因为它在唯一的术语而不是每个术语上运行)。

但是,我还没有想出在 DataPrep 中做到这一点的任何方法。:-/

有什么更有效的方法来做到这一点?

HTML 到纯文本

我的源数据是纯文本和 html 的组合。在 370 万份文档中,只有大约 80 万份有明文可用。

我想以某种合理的方式将 html 转换为纯文本(例如,相当于 Nokogiri #content),这样我就可以对结果进行令牌提取。

我可以启动一个集群,提取bq queryhtml,用 nokogiri 处理它,然后将它输出到一个已处理的表。但这有点复杂,需要大量的 i/o。

有没有更简单/更有效的方法来做到这一点?

4

1 回答 1

3

我认为您可以在 BigQuery 中完成所有操作
下面应该会给您一个良好的开端
在每个文档和整个语料库中都有词频
并且 html 以及只是数字的词被剥离
您现在可以在此处添加任何额外的处理,包括 TF-以色列国防军

#standardSQL
WITH removed_html AS (
  SELECT id, REGEXP_REPLACE(document, r'<[^>]*>', ' ') AS document
  FROM `yourTable`
),
words_in_documents AS (
  SELECT id, 
    ARRAY(
      SELECT AS STRUCT word, COUNT(1) AS cnt 
      FROM UNNEST(REGEXP_EXTRACT_ALL(document, r'[\w_]+')) AS word 
      GROUP BY word
      HAVING NOT REGEXP_CONTAINS(word, r'^\d+$')
    ) AS words
  FROM removed_html
),
words_in_corpus AS (
  SELECT word, SUM(cnt) AS cnt
  FROM words_in_documents, UNNEST(words) AS words
  GROUP BY word
)
SELECT * 
FROM words_in_corpus

您可以使用问题中的虚拟数据来测试/玩这个

#standardSQL
WITH `yourTable` AS (
  SELECT 1 AS id, "foo bar, foo baz" AS document UNION ALL
  SELECT 2, "foo bar bar qux" UNION ALL
  SELECT 3, '''
<h5 id="last_value">LAST_VALUE</h5>
<pre class="codehilite"><code>LAST_VALUE (value_expression [{RESPECT | IGNORE} NULLS])</code></pre>
  '''
),
removed_html AS (
  SELECT id, REGEXP_REPLACE(document, r'<[^>]*>', ' ') AS document
  FROM `yourTable`
),
words_in_documents AS (
  SELECT id, 
    ARRAY(
      SELECT AS STRUCT word, COUNT(1) AS cnt 
      FROM UNNEST(REGEXP_EXTRACT_ALL(document, r'[\w_]+')) AS word 
      GROUP BY word
      HAVING NOT REGEXP_CONTAINS(word, r'^\d+$')
    ) AS words
  FROM removed_html
),
words_in_corpus AS (
  SELECT word, SUM(cnt) AS cnt
  FROM words_in_documents, UNNEST(words) AS words
  GROUP BY word
)
SELECT * 
FROM words_in_corpus
ORDER BY cnt DESC
于 2017-10-09T15:29:31.700 回答