Exemples de comment créer un échantillon aléatoire en utilisant un réservoir avec pandas en python:
Créer une liste de dataframes
Pour créer un échantillon à partir d'une dataframe, une solution simple consiste à utiliser la fonction pandas sample() (voir l'article précédent: How to select randomly (sample) the rows of a dataframe using pandas in python: ). Cependant, si vous avez beaucoup de données, cette approache ne marche pas et il faut alors alors créer un échantillon aléatoire avec un réservoir (voir l'article de wikipedia sur le sujet Reservoir sampling ).
Supposons que nos données sont stockees dans une liste de fichiers (dataframes ici):
import pandas as pd
import numpy as np
import random
list_of_files = []
nb_files = 200
for f in range(nb_files):
data = np.random.randn(100000)
df = pd.DataFrame(data=data,columns=['x'])
list_of_files.append(df)
Créer un échantillon aléatoire avec un réservoir avec pandas
Étape 1 : créez un échantillon avec un réservoir de taille k (avec une valeur par défaut = 9999.0) :
k = 10000
res = np.full((k,1), -9999.0)
res_df = pd.DataFrame(data=res,columns=['x'])
Étape 2 : sélectionnez un fichier ("dataframe" ici)
df = list_of_files[0]
Etape 3 : ajouter une colonne nommée 'i' correspondant à l'indice:
i_start = 1
file_nb_rows = df.shape[0]
col = np.arange(0,file_nb_rows)
col = col + i_start
df['i'] = col
print(df)
donne par exemple
x i
0 1.031251 1
1 -0.337949 2
2 0.191543 3
3 1.026738 4
4 0.402292 5
... ... ...
99995 1.226392 99996
99996 -0.777223 99997
99997 -0.185092 99998
99998 -0.861963 99999
99999 0.993446 100000
Étape 4 : ajoutez une nouvelle colonne appelée 'j' : l'algorithme génère alors un nombre aléatoire j compris entre 0 et i
def myfunc(i):
return random.randrange(0,i)
df['j'] = df['i'].apply(myfunc)
print(df)
donne par exemple
x i j
0 1.031251 1 0
1 -0.337949 2 1
2 0.191543 3 0
3 1.026738 4 3
4 0.402292 5 4
... ... ... ...
99995 1.226392 99996 43950
99996 -0.777223 99997 10858
99997 -0.185092 99998 75163
99998 -0.861963 99999 632
99999 0.993446 100000 92049
Étape 5 : sélectionnez uniquement les lignes avec j < k :
df = df[ df['j'] < k ]
Étape 6 : définissez la colonne j comme index de la dataframe
df = df.set_index('j')
Etape 7 : remplacez les lignes du réservoir par les indices de df correspondant :
res_df.loc[df.index, :] = df[:]
print( res_df )
donne alors par exemple comme réservoir
x
0 -1.596012
1 0.720636
2 -1.125773
3 0.234868
4 -0.141145
... ...
9995 1.158669
9996 -1.503172
9997 0.216314
9998 -0.243413
9999 1.908080
Étape 8 : mettre à jour i_start
i_start += file_nb_rows
Enfin, combinons toutes les étapes et créeons une boucle sur les fichiers :
k = 10000
res = np.full((k,1), -9999.0)
res_df = pd.DataFrame(data=res,columns=['x'])
i_start = 1
for df in list_of_files:
file_nb_rows = df.shape[0]
col = np.arange(0,file_nb_rows)
col = col + i_start
df['i'] = col
def myfunc(i):
return random.randrange(0,i)
df['j'] = df['i'].apply(myfunc)
df = df[ df['j'] < k ]
df = df.set_index('j')
res_df.loc[df.index, :] = df[:]
i_start += file_nb_rows
On obtient alors l'échantillon :
x
0 1.934902
1 1.882526
2 0.019944
3 1.217078
4 -0.320754
... ...
9995 -0.080966
9996 3.036373
9997 0.876503
9998 -0.152433
9999 0.932511
Done !
Créer un échantillon aléatoire pondéré avec un réservoir avec pandas
Dans l'exemple précédent, chaque ligne a la même probabilité d'être sélectionnée. Pour mettre en place un échantillonnage aléatoire pondéré, il existe plusieurs solutions (voir par exemple le document de recherche suivant: Weighted random sampling with a reservoir que l'on va implementer ci-dessous):
Voici un exemple de mise en œuvre d'un échantillonnage aléatoire pondéré :
Étape 1 : créer de fichiers (dataframes) de données :
import pandas as pd
import numpy as np
import random
data = np.random.uniform(0,100,100000)
df = pd.DataFrame(data=data,columns=['x'])
df.hist()
Étape 2 : Attribuez un poids à chaque ligne ( w=x**2 par exemple) :
Notez que les lignes avec le plus grand w ont une probabilité plus élevée d'être sélectionnées.
def weights(i):
return i**2
df['w'] = df['x'].apply(weights)
print(df)
donne par exemple
x w
0 87.345129 7629.171630
1 1.802819 3.250155
2 18.481825 341.577844
3 85.596719 7326.798226
4 51.299716 2631.660854
... ... ...
99995 5.409944 29.267489
99996 96.474256 9307.281975
99997 89.549894 8019.183497
99998 95.647101 9148.367968
99999 66.882506 4473.269595
Étape 3:
list_of_files = []
nb_files = 100
def weights(i):
return i**2
for f in range(nb_files):
data = np.random.uniform(0,100,100000)
df = pd.DataFrame(data=data,columns=['x'])
df['w'] = df['x'].apply(weights)
list_of_files.append(df)
print(list_of_files[1])
Étape 4:
k = 10000
res = np.full((k,4), -9999.0)
res_df = pd.DataFrame(data=res,columns=['x', 'w', 'ui', 'ki'])
Iterate over each dataframes:
for df in list_of_files:
file_nb_rows = df.shape[0]
col = np.random.uniform(0,1,file_nb_rows)
df['ui'] = col
def myfunc(c):
return c['ui']**(1.0/c['w'])
df['ki'] = df.apply(myfunc, axis=1)
#print(df)
for index, row in df.iterrows():
if res_df[ res_df['ki'] < 0.0 ].shape[0] > 0:
fillv_idx = res_df[ res_df['ki'] < 0.0 ].index[0]
res_df.iloc[fillv_idx,:] = row
else:
idxmin = res_df[['ki']].idxmin()
if row['ki'] > res_df['ki'].iloc[idxmin[0]]:
res_df.iloc[idxmin[0]] = row
print( res_df )
donne par exemple
x w ui ki
0 53.454493 2857.382844 0.998571 0.999999
1 89.968302 8094.295344 0.989442 0.999999
2 70.193962 4927.192322 0.947424 0.999989
3 91.303779 8336.380103 0.919453 0.999990
4 67.163507 4510.936621 0.937989 0.999986
... ... ... ... ...
9995 71.289684 5082.219003 0.929686 0.999986
9996 21.750293 473.075265 0.998853 0.999998
9997 85.458925 7303.227943 0.921676 0.999989
9998 76.983555 5926.467703 0.973879 0.999996
9999 85.240184 7265.888955 0.922595 0.999989