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]}})
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()
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
3 Réponses :
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') )
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.
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
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()))
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