Comment gérer les valeurs manquantes dans des séquences d'une certaine longueur à partir d'un dataframe pandas?

vestland

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-02pour 2017-01-06que 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 applycolonne 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 Bde 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.dropnan'a pas d'argument spécifique pour cela).


Ce que j'ai essayé:

1. pandas.DataFrame.dropna

Je pensais que l'argument threshserait 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)]
ALollz

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 dfsection de vos complications pour la sortie du code ci-dessous.

Explication:

  • Nous créons un autre df où les NaNvaleurs sont attribuées à zéro et chaque valeur finie est affectée à 1 (si votre initiale dfa 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 NaNvaleurs 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 NaNvaleurs 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.

modifier le
0

laisse moi dire quelques mots

0commentaires
connexionAprès avoir participé à la revue

Articles connexes

TOP liste

chaudétiquette

Archive