在 BigQuery 中对固定数量的行进行高效采样

Ted*_*Ted 2 random google-bigquery

我有一个大小为 N 的大型数据集,并且想要获得大小为 n 的(均匀)随机样本。这个问题提供了两种可能的解决方案:

\n\n
SELECT foo FROM mytable WHERE RAND() < n/N\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x86\x92 这很快,但没有给我精确的 n 行(仅大约)。

\n\n
SELECT foo, RAND() as r FROM mytable ORDER BY r LIMIT n\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x86\x92 这需要对 N 行进行排序,这似乎不必要且浪费(特别是如果 n << N)。

\n\n

有没有一种解决方案可以结合两者的优点?我想我可以使用第一个解决方案来选择 2n 行,然后对这个较小的数据集进行排序,但它有点难看并且不能保证工作,所以我想知道是否有更好的选择。

\n

enl*_*lin 6

我使用 BigQuery 标准 SQL 与示例数据集(137,826,763 行)比较了两个查询执行时间,并获取了大小为n的列的natality样本。执行查询时不使用缓存结果。source_year

\n\n

查询1:

\n\n
SELECT source_year\nFROM `bigquery-public-data.samples.natality`\nWHERE RAND() < n/137826763\n
Run Code Online (Sandbox Code Playgroud)\n\n

查询2:

\n\n
SELECT source_year, rand() AS r\nFROM `bigquery-public-data.samples.natality`\nORDER BY r\nLIMIT n\n
Run Code Online (Sandbox Code Playgroud)\n\n

结果:

\n\n
n        Query1   Query2\n1000     ~2.5s    ~2.5s\n10000    ~3s      ~3s\n100000   ~3s      ~4s\n1000000  ~4.5s    ~15s\n
Run Code Online (Sandbox Code Playgroud)\n\n

对于n <= 10 5,差异约为1 秒,对于n >= 10 6,执行时间差异显着。原因似乎是当 LIMIT 添加到查询中时,ORDER BY 在多个工作线程上运行。请参阅Mikhail Berlyant 提供的原始答案。

\n\n

我认为您建议将两个查询结合起来可能是一个可能的解决方案。因此我比较了组合查询的执行时间:

\n\n

新查询:

\n\n
SELECT source_year,rand() AS r\nFROM (\n  SELECT source_year\n  FROM `bigquery-public-data.samples.natality`\n  WHERE RAND() < 2*n/137826763)\nORDER BY r\nLIMIT n\n
Run Code Online (Sandbox Code Playgroud)\n\n

结果:

\n\n
n       Query1    New Query\n1000    ~2.5s     ~3s\n10000   ~3s       ~3s\n100000  ~3s       ~3s\n1000000 ~4.5s     ~6s\n
Run Code Online (Sandbox Code Playgroud)\n\n

这种情况下的执行时间在n <= 10 6的情况下变化<=1.5s。最好在子查询中选择n+some_rows行而不是2n行,其中some_rows是一个足够大的常量,足以获取超过n的行行。

\n\n

关于您所说的 \xe2\x80\x9cnot 保证工作\xe2\x80\x9d,我理解您担心新查询不会 \xe2\x80\x99t 准确检索n行。在这种情况下,如果some_rows足够大,那么子查询中总是会得到多于n行的数据。因此,查询将恰好返回n行。

\n\n

总而言之,组合查询不如 Query1 快,但它精确获取n行,并且比 Query2 快。因此,它可能是均匀随机样本的解决方案。我想指出的是,如果未指定 ORDER BY,则 BigQuery 输出是不确定的,这意味着每次执行查询时可能会收到不同的结果。如果您尝试在不使用缓存结果的情况下多次执行以下查询,您将得到不同的结果。

\n\n
SELECT *\nFROM `bigquery-samples.wikipedia_benchmark.Wiki1B`\nLIMIT 5\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,取决于您想要样本的随机程度,这可能是更好的解决方案。

\n