L'essence:
Si une colonne contient une séquence de plus de, disons, 5 valeurs manquantes, je voudrais supprimer les index correspondants de cette trame de données. Donc dans un dataframe comme ci-dessous ...
A B
2017-01-01 -0.0053 -0.0062
2017-01-02 NaN 0.0016
2017-01-03 NaN 0.0043
2017-01-04 NaN -0.0077
2017-01-05 NaN -0.0070
2017-01-06 NaN 0.0058
2017-01-07 0.0024 -0.0074
2017-01-08 0.0018 0.0086
2017-01-09 0.0020 0.0012
2017-01-10 -0.0031 -0.0020
2017-01-11 0.0027 NaN
2017-01-12 -0.0050 NaN
2017-01-13 -0.0063 NaN
2017-01-14 0.0066 0.0095
2017-01-15 0.0039 0.0028
... Je voudrais supprimer les index 2017-01-02
pour 2017-01-06
que la sortie souhaitée ressemble à ceci:
A B
2017-01-01 -0.0053 -0.0062
2017-01-07 0.0024 -0.0074
2017-01-08 0.0018 0.0086
2017-01-09 0.0020 0.0012
2017-01-10 -0.0031 -0.0020
2017-01-11 0.0027 NaN
2017-01-12 -0.0050 NaN
2017-01-13 -0.0063 NaN
2017-01-14 0.0066 0.0095
2017-01-15 0.0039 0.0028
Comment puis-je faire cela efficacement?
Les détails:
Voici un extrait de code pour reproduire le dataframe:
# imports
import pandas as pd
import numpy as np
np.random.seed(1234)
# Reproducible data sample
def df_sample(rows, names):
''' Function to create data sample with random returns
Parameters
==========
rows : number of rows in the dataframe
names: list of names to represent assets
Example
=======
>>> returns(rows = 2, names = ['A', 'B'])
A B
2017-01-01 0.0027 0.0075
2017-01-02 -0.0050 -0.0024
'''
listVars= names
rng = pd.date_range('1/1/2017', periods=rows, freq='D')
df_temp = pd.DataFrame(np.random.randint(-100,100,size=(rows, len(listVars))), columns=listVars)
df_temp = df_temp.set_index(rng)
df_temp = df_temp / 10000
return df_temp
df = df_sample(15,list('AB'))
Complications dont j'ai connaissance
Si le dataframe devait avoir des index qui se chevauchent avec des valeurs manquantes dans plusieurs colonnes comme ceci:
A B
2017-01-01 -0.0053 -0.0062
2017-01-02 NaN 0.0016
2017-01-03 NaN 0.0043
2017-01-04 NaN NaN
2017-01-05 NaN NaN
2017-01-06 NaN NaN
2017-01-07 0.0024 NaN
2017-01-08 0.0018 NaN
2017-01-09 0.0020 0.0012
2017-01-10 NaN -0.0020
... alors je suppose que toute solution utilisant apply
colonne par colonne rendrait une trame de données temporaire comme celle-ci ...
A B
2017-01-01 -0.0053 -0.0062
2017-01-07 0.0024 NaN
2017-01-08 0.0018 NaN
2017-01-09 0.0020 0.0012
2017-01-10 NaN -0.0020
... et puis éventuellement ignorer les index manquants d'origine pour column B
de 2017-01-04
à 2017-01-08
. C'est peut-être juste quelque chose qu'il faudrait accepter. Mais idéalement, la solution devrait reconnaître que ces index représentent à l'origine 5 valeurs manquantes séquentiellement, et supprimer ces index également, de sorte que la trame de données résultante ressemble à ceci:
A B
2017-01-01 -0.0053 -0.0062
2017-01-09 0.0020 0.0012
2017-01-10 NaN -0.0020
(Mais qu'en est-il du dernier NaN là-bas? Je le ferais simplement fill forward
. Mais faire la même chose avec chaque valeur manquante serait aller trop loin.)
Donc je suppose que c'est potentiellement un problème beaucoup plus complexe que je ne le soupçonnais au départ (et c'est peut-être aussi la raison pour laquelle la fonction pandas.DataFrame.dropna
n'a pas d'argument spécifique pour cela).
Ce que j'ai essayé:
1. pandas.DataFrame.dropna
Je pensais que l'argument thresh
serait un moyen d'utiliser pandas.DataFrame.dropna , mais selon la documentation, cet argument définit un seuil pour les valeurs existantes plutôt que manquantes :
thresh: int, par défaut Aucun
valeur int: nécessite que de nombreuses valeurs non-NA
2. Définition et recherche de modèles de nan colonne par colonne
Voici une solution possible basée sur les réponses suggérées ici . Il vous oblige cependant à définir que vous recherchez 5 et seulement 5 valeurs manquantes dans une séquence. Pour compléter la solution, je devrais également trouver l'union des index sur toutes les listes qui représentent les index des séquences manquantes pour toutes les colonnes, puis sous-définir le dataframe correspondant à cela.
Merci pour toutes autres suggestions!
Voici le tout pour un copier-coller facile:
import pandas as pd
import numpy as np
np.random.seed(1234)
# Reproducible data sample
def df_sample(rows, names):
''' Function to create data sample with random returns
Parameters
==========
rows : number of rows in the dataframe
names: list of names to represent assets
Example
=======
>>> returns(rows = 2, names = ['A', 'B'])
A B
2017-01-01 0.0027 0.0075
2017-01-02 -0.0050 -0.0024
'''
listVars= names
rng = pd.date_range('1/1/2017', periods=rows, freq='D')
df_temp = pd.DataFrame(np.random.randint(-100,100,size=(rows, len(listVars))), columns=listVars)
df_temp = df_temp.set_index(rng)
df_temp = df_temp / 10000
return df_temp
df = df_sample(15,list('AB'))
df['A'][1:6] = np.nan
df['B'][3:8] = np.nan
dfi = df
# convert to boolean values
df = dfi
df = df.isnull()
# specify pattern
pattern = [True,True, True, True, True]
# prepare for a for loop
idx = []
# loop through all columns and identify sequence of missing values
for col in df:
df_temp = df[col].to_frame()
matched = df_temp.rolling(len(pattern)).apply(lambda x: all(np.equal(x, pattern)))
matched = matched.sum(axis = 1).astype(bool)
idx_matched = np.where(matched)[0]
subset = [range(match-len(pattern)+1, match+1) for match in idx_matched]
result = pd.concat([df.iloc[subs,:] for subs in subset], axis = 0).index
idx.append(result)
print(idx)
Sortie (index pour nan séquences colonne par colonne):
[DatetimeIndex(['2017-01-02', '2017-01-03', '2017-01-04', '2017-01-05','2017-01-06'],
dtype='datetime64[ns]', freq=None),
DatetimeIndex(['2017-01-04', '2017-01-05', '2017-01-06', '2017-01-07', '2017-01-08'],
dtype='datetime64[ns]', freq=None)]
Cela devrait le résoudre pour vous. Il ne supprime pas les lignes jusqu'à la fin, donc il résoudra correctement les multiples colonnes comme vous le souhaitez dans le deuxième scénario. J'ai utilisé la df
section de vos complications pour la sortie du code ci-dessous.
Explication:
Nous créons un autre df où les NaN
valeurs sont attribuées à zéro et chaque valeur finie est affectée à 1 (si votre initiale df
a des valeurs nulles, vous devrez d'abord les mapper à tout autre nombre de ce mannequin df2
, puis .fillna(0).astype('bool')
)
Le regroupement par la somme cumulée de chaque colonne nous permet de trouver où il y a plus de 5 NaN
valeurs consécutives . La comparaison avec le df d'origine garantit alors que nous ne capturons pas la première valeur non nulle.
Le masque est créé à la fin pour toute ligne qui aurait dû être supprimée, vous le résolvez donc correctement pour plusieurs colonnes avec des NaN
valeurs qui se chevauchent .
Voici le code:
import pandas as pd
import numpy as np
## If the initial df contains values of 0 do this instead of the first line below
#df2 = df.copy()
#df2[df2==0] = 0.01
#df2 = df2.fillna(0).astype('bool').cumsum()
# Min number of consecutive NaN values to begin dropping
n_cons = 5
df2 = df.fillna(0).astype('bool').cumsum()
for col in df2.columns:
df2[col] = df2.groupby(col)[col].transform(lambda x: np.size(x) > n_cons)
df2[col] = df2[col] & df[col].isnull()
mask = df2.any(axis=1)
df[~mask]
# A B
#2017-01-01 -0.0053 -0.0062
#2017-01-09 0.0020 0.0012
#2017-01-10 NaN -0.0020
Cet article est collecté sur Internet, veuillez indiquer la source lors de la réimpression.
En cas d'infraction, veuillez [email protected] Supprimer.
laisse moi dire quelques mots