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

时间:2018-02-21 21:18:31

标签: random google-bigquery

我有一个大小为N的大型数据集,并希望得到一个大小为n的(统一)随机样本。 This question提供了两种可能的解决方案:

SELECT foo FROM mytable WHERE RAND() < n/N

→这很快,但不能给我正好n行(只是大约)。

SELECT foo, RAND() as r FROM mytable ORDER BY r LIMIT n

→这需要对N行进行排序,这似乎是不必要和浪费的(特别是如果n&lt;&lt; N)。

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

1 个答案:

答案 0 :(得分:4)

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

查询1:

SELECT source_year
FROM `bigquery-public-data.samples.natality`
WHERE RAND() < n/137826763

QUERY2:

SELECT source_year, rand() AS r
FROM `bigquery-public-data.samples.natality`
ORDER BY r
LIMIT n

结果:

n        Query1   Query2
1000     ~2.5s    ~2.5s
10000    ~3s      ~3s
100000   ~3s      ~4s
1000000  ~4.5s    ~15s

对于 n&lt; = 10 5 ,差异为 ~1s n&gt; = 10 6 执行时间差别很大。原因似乎是当LIMIT被添加到查询中时,ORDER BY会在多个worker上运行。查看Mikhail Berlyant提供的原始answer

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

新查询:

SELECT source_year,rand() AS r
FROM (
  SELECT source_year
  FROM `bigquery-public-data.samples.natality`
  WHERE RAND() < 2*n/137826763)
ORDER BY r
LIMIT n

结果:

n       Query1    New Query
1000    ~2.5s     ~3s
10000   ~3s       ~3s
100000  ~3s       ~3s
1000000 ~4.5s     ~6s

对于 n&lt; = 10 6 ,此情况下的执行时间在&lt; = 1.5s 中有所不同。在子查询中选择 n + some_rows 行而不是 2n 行是个好主意,其中 some_rows 是一个足够大的常数超过 n 行。

关于你所说的“不能保证工作”,我知道你担心新查询不会完全检索 n 行。在这种情况下,如果 some_rows 足够大,则子查询中的行总是超过 n 行。因此,查询将完全返回 n 行。

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

SELECT *
FROM `bigquery-samples.wikipedia_benchmark.Wiki1B`
LIMIT 5

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