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.
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égorieDonc, 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
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
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
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
)
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
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
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.