web-dev-qa-db-fra.com

Échantillonnage aléatoire stratifié avec BigQuery?

Comment puis-je faire un échantillonnage stratifié sur BigQuery?

Par exemple, nous voulons un échantillon stratifié proportionnel de 10% utilisant le category_id comme strates. Nous avons jusqu'à 11000 id_catégories dans certaines de nos tables.

10
Felipe Hoffa

Avec #standardSQL, définissons notre table et quelques statistiques dessus:

WITH table AS (
  SELECT *, subreddit category
  FROM `fh-bigquery.reddit_comments.2018_09` a
), table_stats AS (
  SELECT *, SUM(c) OVER() total 
  FROM (
    SELECT category, COUNT(*) c 
    FROM table
    GROUP BY 1 
    HAVING c>1000000)
)

Dans cette configuration:

  • subreddit sera notre catégorie
  • nous voulons seulement des subreddits avec plus de 1000000 commentaires

Donc, si nous voulons 1% de chaque catégorie dans notre échantillon:

SELECT COUNT(*) samples, category, ROUND(100*COUNT(*)/MAX(c),2) percentage
FROM (
  SELECT id, category, c  
  FROM table a
  JOIN table_stats b
  USING(category)
  WHERE Rand()< 1/100 
)
GROUP BY 2

enter image description here

Ou disons que nous voulons ~ 80 000 échantillons - mais choisis proportionnellement dans toutes les catégories:

SELECT COUNT(*) samples, category, ROUND(100*COUNT(*)/MAX(c),2) percentage
FROM (
  SELECT id, category, c  
  FROM table a
  JOIN table_stats b
  USING(category)
  WHERE Rand()< 80000/total
)
GROUP BY 2

enter image description here

Maintenant, si vous voulez obtenir le même nombre d'échantillons de chaque groupe (disons, 20 000):

SELECT COUNT(*) samples, category, ROUND(100*COUNT(*)/MAX(c),2) percentage
FROM (
  SELECT id, category, c  
  FROM table a
  JOIN table_stats b
  USING(category)
  WHERE Rand()< 20000/c
)
GROUP BY 2

enter image description here

Si vous voulez exactement 20 000 éléments de chaque catégorie:

SELECT ARRAY_LENGTH(cat_samples) samples, category, ROUND(100*ARRAY_LENGTH(cat_samples)/c,2) percentage
FROM (
  SELECT ARRAY_AGG(a ORDER BY Rand() LIMIT 20000) cat_samples, category, ANY_VALUE(c) c
  FROM table a
  JOIN table_stats b
  USING(category)
  GROUP BY category
)

enter image description here

Si vous voulez exactement 2% de chaque groupe:

SELECT COUNT(*) samples, sample.category, ROUND(100*COUNT(*)/ANY_VALUE(c),2) percentage
FROM (
  SELECT ARRAY_AGG(a ORDER BY Rand()) cat_samples, category, ANY_VALUE(c) c
  FROM table a
  JOIN table_stats b
  USING(category)
  GROUP BY category
), UNNEST(cat_samples) sample WITH OFFSET off
WHERE off<0.02*c
GROUP BY 2

enter image description here


Si cette dernière approche est ce que vous voulez, vous remarquerez peut-être qu'elle échoue lorsque vous souhaitez réellement extraire des données. Une première LIMIT similaire à la plus grande taille de groupe s'assurera que nous ne trions pas plus de données que nécessaire:

SELECT sample.*
FROM (
  SELECT ARRAY_AGG(a ORDER BY Rand() LIMIT 105000) cat_samples, category, ANY_VALUE(c) c
  FROM table a
  JOIN table_stats b
  USING(category)
  GROUP BY category
), UNNEST(cat_samples) sample WITH OFFSET off
WHERE off<0.02*c
11
Felipe Hoffa

Je pense que la façon la plus simple d'obtenir un échantillon stratifié proportionnel est de classer les données par catégories et de faire un "nième" échantillon des données. Pour un échantillon de 10%, vous voulez toutes les 10 lignes.

Cela ressemble à ceci:

select t.*
from (select t.*,
             row_number() over (order by category order by Rand()) as seqnum
      from t
     ) t
where seqnum % 10 = 1;

Remarque: cela ne garantit pas que toutes les catégories figureront dans l'échantillon final. Une catégorie de moins de 10 lignes peut ne pas apparaître.

Si vous voulez des échantillons de taille égale, alors commandez dans chaque catégorie et prenez simplement un nombre fixe:

select t.*
from (select t.*,
             row_number() over (partition by category order by Rand()) as seqnum
      from t
     ) t
where seqnum <= 100;

Remarque: cela ne garantit pas qu'il existe 100 lignes dans chaque catégorie. Il prend toutes les lignes pour les catégories plus petites et un échantillon aléatoire de plus grandes.

Ces deux méthodes sont très pratiques. Ils peuvent travailler avec plusieurs dimensions en même temps. Le premier a une fonctionnalité particulièrement agréable qui peut également fonctionner avec des dimensions numériques.

4
Gordon Linoff