web-dev-qa-db-fra.com

Supprime les lignes contenant des cellules vides d'un pandas DataFrame

J'ai un pd.DataFrame qui a été créé en analysant des feuilles de calcul Excel. Une colonne dont les cellules sont vides. Par exemple, ci-dessous est la sortie pour la fréquence de cette colonne, les enregistrements 32320 ont des valeurs manquantes pour le locataire.

   In [67]: value_counts(Tenant,normalize=False)
   Out[67]:
                              32320
   Thunderhead                8170
   Big Data Others            5700
   Cloud Cruiser              5700
   Partnerpedia               5700
   Comcast                    5700
   SDP                        5700
   Agora                      5700
   dtype: int64

J'essaie de supprimer des lignes où le locataire est manquant, mais l'option isnull ne reconnaît pas les valeurs manquantes.

   In [71]: df['Tenant'].isnull().sum()
   Out[71]: 0

La colonne a le type de données "Object". Que se passe-t-il dans ce cas? Comment puis-je supprimer des enregistrements où le locataire est manquant?

51
Amrita Sawant

Les pandas reconnaîtront une valeur comme nulle s'il s'agit d'un objet np.nan, qui s'imprimera sous la forme NaN dans le DataFrame. Vos valeurs manquantes sont probablement des chaînes vides que Pandas ne reconnaît pas comme nulles. Pour résoudre ce problème, vous pouvez convertir les objets vides (ou le contenu de vos cellules vides) en objets np.nan à l'aide de replace(), puis appeler dropna() sur votre DataFrame pour supprimer les lignes avec des locataires nuls.

Pour démontrer, nous créons un DataFrame avec des valeurs aléatoires et des chaînes vides dans une colonne Tenants:

>>> import pandas as pd
>>> import numpy as np
>>> 
>>> df = pd.DataFrame(np.random.randn(10, 2), columns=list('AB'))
>>> df['Tenant'] = np.random.choice(['Babar', 'Rataxes', ''], 10)
>>> print df

          A         B   Tenant
0 -0.588412 -1.179306    Babar
1 -0.008562  0.725239         
2  0.282146  0.421721  Rataxes
3  0.627611 -0.661126    Babar
4  0.805304 -0.834214         
5 -0.514568  1.890647    Babar
6 -1.188436  0.294792  Rataxes
7  1.471766 -0.267807    Babar
8 -1.730745  1.358165  Rataxes
9  0.066946  0.375640         

Nous remplaçons maintenant les chaînes vides de la colonne Tenants par les objets np.nan, comme suit:

>>> df['Tenant'].replace('', np.nan, inplace=True)
>>> print df

          A         B   Tenant
0 -0.588412 -1.179306    Babar
1 -0.008562  0.725239      NaN
2  0.282146  0.421721  Rataxes
3  0.627611 -0.661126    Babar
4  0.805304 -0.834214      NaN
5 -0.514568  1.890647    Babar
6 -1.188436  0.294792  Rataxes
7  1.471766 -0.267807    Babar
8 -1.730745  1.358165  Rataxes
9  0.066946  0.375640      NaN

Nous pouvons maintenant supprimer les valeurs NULL:

>>> df.dropna(subset=['Tenant'], inplace=True)
>>> print df

          A         B   Tenant
0 -0.588412 -1.179306    Babar
2  0.282146  0.421721  Rataxes
3  0.627611 -0.661126    Babar
5 -0.514568  1.890647    Babar
6 -1.188436  0.294792  Rataxes
7  1.471766 -0.267807    Babar
8 -1.730745  1.358165  Rataxes
101
McMath

value_counts omet NaN par défaut, vous avez donc probablement affaire à "".

Donc, vous pouvez simplement les filtrer comme

filter = df["Tenant"] != ""
dfNew = df[filter]
25
Bob Haffner

il y a une situation où la cellule a un espace blanc, vous ne pouvez pas le voir, utilisez

df['col'].replace('  ', np.nan, inplace=True)

remplacer l'espace blanc par NaN,

ensuite

df= df.dropna(subset=['col'])
7
Learn

Pythonic + Pandorable: df[df['col'].astype(bool)]

Les chaînes vides sont faussaires, ce qui signifie que vous pouvez filtrer les valeurs booléennes comme ceci:

df = pd.DataFrame({
    'A': range(5),
    'B': ['foo', '', 'bar', '', 'xyz']
})
df
   A    B
0  0  foo
1  1     
2  2  bar
3  3     
4  4  xyz
df['B'].astype(bool)                                                                                                                      
0     True
1    False
2     True
3    False
4     True
Name: B, dtype: bool

df[df['B'].astype(bool)]                                                                                                                  
   A    B
0  0  foo
2  2  bar
4  4  xyz

Si votre objectif est de supprimer non seulement les chaînes vides, mais également les chaînes ne contenant que des espaces, utilisez str.strip auparavant:

df[df['B'].str.strip().astype(bool)]
   A    B
0  0  foo
2  2  bar
4  4  xyz

Plus rapide que vous ne le pensez

.astype est une opération vectorisée, plus rapide que toutes les options présentées jusqu'à présent. Du moins d'après mes tests. YMMV.

Voici une comparaison de temps, j'ai jeté dans d'autres méthodes que je pourrais penser.

enter image description here

Code de référence, pour référence:

import pandas as pd
import perfplot

df1 = pd.DataFrame({
    'A': range(5),
    'B': ['foo', '', 'bar', '', 'xyz']
})

perfplot.show(
    setup=lambda n: pd.concat([df1] * n, ignore_index=True),
    kernels=[
        lambda df: df[df['B'].astype(bool)],
        lambda df: df[df['B'] != ''],
        lambda df: df[df['B'].replace('', np.nan).notna()],  # optimized 1-col
        lambda df: df.replace({'B': {'': np.nan}}).dropna(subset=['B']),  
    ],
    labels=['astype', "!= ''", "replace + notna", "replace + dropna", ],
    n_range=[2**k for k in range(1, 15)],
    xlabel='N',
    logx=True,
    logy=True,
    equality_check=pd.DataFrame.equals)
5
cs95

Vous pouvez utiliser cette variante:

import pandas as pd
vals = {
    'name' : ['n1', 'n2', 'n3', 'n4', 'n5', 'n6', 'n7'],
    'gender' : ['m', 'f', 'f', 'f',  'f', 'c', 'c'],
    'age' : [39, 12, 27, 13, 36, 29, 10],
    'education' : ['ma', None, 'school', None, 'ba', None, None]
}
df_vals = pd.DataFrame(vals) #converting dict to dataframe

Cela produira (** - mettant en évidence uniquement les lignes souhaitées):

   age education gender name
0   39        ma      m   n1 **
1   12      None      f   n2    
2   27    school      f   n3 **
3   13      None      f   n4
4   36        ba      f   n5 **
5   29      None      c   n6
6   10      None      c   n7

Donc, pour supprimer tout ce qui n'a pas de valeur 'éducation', utilisez le code ci-dessous:

df_vals = df_vals[~df_vals['education'].isnull()] 

('~' indiquant NON)

Résultat:

   age education gender name
0   39        ma      m   n1
2   27    school      f   n3
4   36        ba      f   n5
0
Amir F