Comment sélectionner des lignes avec un ou plusieurs null dans un DataFrame pandas sans lister explicitement les colonnes?

234

J'ai une trame de données avec ~ 300K lignes et ~ 40 colonnes. Je veux savoir si des lignes contiennent des valeurs nulles - et mettre ces lignes «nulles» dans une trame de données distincte afin de pouvoir les explorer facilement.

Je peux créer un masque explicitement:

mask = False
for col in df.columns: 
    mask = mask | df[col].isnull()
dfnulls = df[mask]

Ou je peux faire quelque chose comme:

df.ix[df.index[(df.T == np.nan).sum() > 1]]

Existe-t-il une façon plus élégante de le faire (localiser les lignes contenant des valeurs nulles)?

Sélecteur de niveau
la source

Réponses:

384

[Mis à jour pour s'adapter au moderne pandas, qui a isnullcomme méthode deDataFrame s ..]

Vous pouvez utiliser isnullet anypour construire une série booléenne et l'utiliser pour indexer dans votre cadre:

>>> df = pd.DataFrame([range(3), [0, np.NaN, 0], [0, 0, np.NaN], range(3), range(3)])
>>> df.isnull()
       0      1      2
0  False  False  False
1  False   True  False
2  False  False   True
3  False  False  False
4  False  False  False
>>> df.isnull().any(axis=1)
0    False
1     True
2     True
3    False
4    False
dtype: bool
>>> df[df.isnull().any(axis=1)]
   0   1   2
1  0 NaN   0
2  0   0 NaN

[Pour les plus âgés pandas :]

Vous pouvez utiliser la fonction isnullau lieu de la méthode:

In [56]: df = pd.DataFrame([range(3), [0, np.NaN, 0], [0, 0, np.NaN], range(3), range(3)])

In [57]: df
Out[57]: 
   0   1   2
0  0   1   2
1  0 NaN   0
2  0   0 NaN
3  0   1   2
4  0   1   2

In [58]: pd.isnull(df)
Out[58]: 
       0      1      2
0  False  False  False
1  False   True  False
2  False  False   True
3  False  False  False
4  False  False  False

In [59]: pd.isnull(df).any(axis=1)
Out[59]: 
0    False
1     True
2     True
3    False
4    False

conduisant au plutôt compact:

In [60]: df[pd.isnull(df).any(axis=1)]
Out[60]: 
   0   1   2
1  0 NaN   0
2  0   0 NaN
DSM
la source
75
def nans(df): return df[df.isnull().any(axis=1)]

alors quand vous en avez besoin, vous pouvez taper:

nans(your_dataframe)
Roko Mijic
la source
1
df[df.isnull().any(axis=1)]fonctionne mais jette UserWarning: Boolean Series key will be reindexed to match DataFrame index.. Comment peut-on réécrire cela de manière plus explicite et d'une manière qui ne déclenche pas ce message d'avertissement?
Vishal
3
@vishal Je pense que tout ce que vous auriez à faire serait d'ajouter loc comme ceci; df.loc[df.isnull().any(axis=1)]
James Draper
0

.any()et .all()sont parfaits pour les cas extrêmes, mais pas lorsque vous recherchez un nombre spécifique de valeurs nulles. Voici un moyen extrêmement simple de faire ce que je crois que vous demandez. C'est assez verbeux, mais fonctionnel.

import pandas as pd
import numpy as np

# Some test data frame
df = pd.DataFrame({'num_legs':          [2, 4,      np.nan, 0, np.nan],
                   'num_wings':         [2, 0,      np.nan, 0, 9],
                   'num_specimen_seen': [10, np.nan, 1,     8, np.nan]})

# Helper : Gets NaNs for some row
def row_nan_sums(df):
    sums = []
    for row in df.values:
        sum = 0
        for el in row:
            if el != el: # np.nan is never equal to itself. This is "hacky", but complete.
                sum+=1
        sums.append(sum)
    return sums

# Returns a list of indices for rows with k+ NaNs
def query_k_plus_sums(df, k):
    sums = row_nan_sums(df)
    indices = []
    i = 0
    for sum in sums:
        if (sum >= k):
            indices.append(i)
        i += 1
    return indices

# test
print(df)
print(query_k_plus_sums(df, 2))

Production

   num_legs  num_wings  num_specimen_seen
0       2.0        2.0               10.0
1       4.0        0.0                NaN
2       NaN        NaN                1.0
3       0.0        0.0                8.0
4       NaN        9.0                NaN
[2, 4]

Ensuite, si vous êtes comme moi et que vous souhaitez effacer ces lignes, écrivez simplement ceci:

# drop the rows from the data frame
df.drop(query_k_plus_sums(df, 2),inplace=True)
# Reshuffle up data (if you don't do this, the indices won't reset)
df = df.sample(frac=1).reset_index(drop=True)
# print data frame
print(df)

Production:

   num_legs  num_wings  num_specimen_seen
0       4.0        0.0                NaN
1       0.0        0.0                8.0
2       2.0        2.0               10.0
Ryan Cocuzzo
la source