2
votes

Remplacer la valeur NaN par une médiane?

J'essaie donc d'utiliser Pandas pour remplacer toutes les valeurs NaN dans une table par la médiane sur une plage particulière. Je travaille avec un plus grand ensemble de données mais par exemple

df[["Val","Hour"]].mask(df['Val'].isna(), df_val.iloc[df.Hour], inplace=True)

df.where(df['Val'].notna(), other=df_val[df.Hour],axis = 0)

df["Val"] = np.where(df['Val'].notna(), df['Val'], df_val(df.Hour))

df.replace({"Val":{np.nan:df_val[df.Hour]}, "Dist":{np.nan:df_dist[df.Hour]}})

Les données

Ce que je veux faire, c'est remplacer les valeurs NaN pour Val et Dist par la valeur médiane de chaque heure pour cette colonne. J'ai réussi à obtenir les valeurs médianes dans un tableau de référence séparé:

df.set_index('Date', inplace=True)
df = df.assign(Hour = lambda x : x.index.hour)
df_val = df[["Val", "Hour"]].groupby("Hour").median()
df_dist = df[["Dist", "Hour"]].groupby("Hour").median()

Valeurs médianes

Mais maintenant, j'ai essayé toutes les commandes ci-dessous sous diverses formes et je ne peux pas savoir comment remplir les valeurs NaN.

np.random.seed(0)
rng = pd.date_range('2020-09-24', periods=20, freq='0.2H')
df = pd.DataFrame({ 'Date': rng, 'Val': np.random.randn(len(rng)), 'Dist' :np.random.randn(len(rng)) }) 
df.Dist[df.Dist<=-0.6] = np.nan
df.Val[df.Val<=-0.5] = np.nan


0 commentaires

3 Réponses :


3
votes

Vous pouvez utiliser groupby.transform et fillna:

                  Date       Val      Dist
0  2020-09-24 00:00:00  1.764052  0.864436
1  2020-09-24 00:12:00  0.400157  0.653619
2  2020-09-24 00:24:00  0.978738  0.864436
3  2020-09-24 00:36:00  2.240893  0.864436
4  2020-09-24 00:48:00  1.867558  2.269755
5  2020-09-24 01:00:00  0.153690  0.757559
6  2020-09-24 01:12:00  0.950088  0.045759
7  2020-09-24 01:24:00 -0.151357 -0.187184
8  2020-09-24 01:36:00 -0.103219  1.532779
9  2020-09-24 01:48:00  0.410599  1.469359
10 2020-09-24 02:00:00  0.144044  0.154947
11 2020-09-24 02:12:00  1.454274  0.378163
12 2020-09-24 02:24:00  0.761038  0.154947
13 2020-09-24 02:36:00  0.121675  0.154947
14 2020-09-24 02:48:00  0.443863 -0.347912
15 2020-09-24 03:00:00  0.333674  0.156349
16 2020-09-24 03:12:00  1.494079  1.230291
17 2020-09-24 03:24:00 -0.205158  1.202380
18 2020-09-24 03:36:00  0.313068 -0.387327
19 2020-09-24 03:48:00  0.323371 -0.302303

Production:

cols = ['Val','Dist']
df[cols] =  df[cols].fillna(df.groupby(df.Date.dt.floor('H'))
                              [cols].transform('median')
                           )


3 commentaires

Une solution très succincte, pourriez-vous expliquer / lier des détails sur, comment vous pouvez utiliser le groupby() puis transform() avec juste un espace entre les deux?


Aussi, pourquoi .transform('median') fonctionne-t-il mieux que .median ?


.median donne une valeur par groupe, vous obtiendrez donc une trame / série de données d'une longueur égale au nombre de groupes. transform remplit à nouveau les valeurs dans les groupes, de sorte que vous recevrez une trame de données / une série avec le même index que la trame de données d'origine. Étant donné que vous attribuez de nouveau à votre trame de données d'origine, la transform fonctionne mieux.



1
votes

Vous pouvez utiliser une opération groupby -> transform , tout en utilisant également la classe pd.Grouper pour effectuer la conversion horaire. Cela créera essentiellement un dataframe avec la même forme que votre original avec les médianes horaires. Une fois que vous avez cela, vous pouvez directement utiliser DataFrame.fillna

hourly_medians = df.groupby(pd.Grouper(key="Date", freq="H")).transform("median")
out = df.fillna(hourly_medians)

print(out)

                  Date       Val      Dist
0  2020-09-24 00:00:00  1.764052  0.864436
1  2020-09-24 00:12:00  0.400157  0.653619
2  2020-09-24 00:24:00  0.978738  0.864436
3  2020-09-24 00:36:00  2.240893  0.864436
4  2020-09-24 00:48:00  1.867558  2.269755
5  2020-09-24 01:00:00  0.153690  0.757559
6  2020-09-24 01:12:00  0.950088  0.045759
7  2020-09-24 01:24:00 -0.151357 -0.187184
8  2020-09-24 01:36:00 -0.103219  1.532779
9  2020-09-24 01:48:00  0.410599  1.469359
10 2020-09-24 02:00:00  0.144044  0.154947
11 2020-09-24 02:12:00  1.454274  0.378163
12 2020-09-24 02:24:00  0.761038  0.154947
13 2020-09-24 02:36:00  0.121675  0.154947
14 2020-09-24 02:48:00  0.443863 -0.347912
15 2020-09-24 03:00:00  0.333674  0.156349
16 2020-09-24 03:12:00  1.494079  1.230291
17 2020-09-24 03:24:00 -0.205158  1.202380
18 2020-09-24 03:36:00  0.313068 -0.387327
19 2020-09-24 03:48:00  0.323371 -0.302303


0 commentaires

0
votes

En utilisant ce que vous avez fait, je ferais ceci:

df.Val = df.Val.fillna(df.Hour.map(df_val.squeeze()))
df.Dist = df.Val.fillna(df.Hour.map(df_dist.squeeze()))


4 commentaires

Est-ce que certains de mes where() ou replace() fonctionneraient si j'avais d'abord pressé mes valeurs médianes?


Je ne pense pas, car ici df_val[df.Hour] vous passez une colonne entière des valeurs à df_val , ce qui devrait générer une erreur


.fillna () s'attend à obtenir un scalaire, un dict ou une série que .map () ne le transmet pas, donc cela ne semble pas fonctionner


df.Hour.map(df_dist.squeeze()) est une série, donc cela fonctionne réellement