Löschen Sie Zeilen mit leeren Zellen aus einem Pandas DataFrame

87

Ich habe eine pd.DataFrame, die durch Parsen einiger Excel-Tabellen erstellt wurde. Eine Spalte enthält leere Zellen. Im Folgenden finden Sie beispielsweise die Ausgabe für die Häufigkeit dieser Spalte. 32320 Datensätze haben fehlende Werte für Tenant .

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

Ich versuche, Zeilen zu löschen, in denen Tenant fehlt, die .isnull()Option erkennt jedoch die fehlenden Werte nicht.

>>> df['Tenant'].isnull().sum()
    0

Die Spalte hat den Datentyp "Objekt". Was passiert in diesem Fall? Wie kann ich Datensätze löschen, bei denen der Mieter fehlt?

Amrita Sawant
quelle

Antworten:

174

Pandas erkennen einen Wert als null, wenn es sich um ein np.nanObjekt handelt, das wie NaNim DataFrame gedruckt wird. Ihre fehlenden Werte sind wahrscheinlich leere Zeichenfolgen, die Pandas nicht als null erkennt. Um dies zu beheben, können Sie die leeren Stiche (oder was auch immer sich in Ihren leeren Zellen befindet) np.nanmithilfe von Objekten in Objekte konvertieren replace()und dann dropna()Ihren DataFrame aufrufen, um Zeilen mit null Mandanten zu löschen.

Zur Demonstration erstellen wir einen DataFrame mit einigen zufälligen Werten und einigen leeren Zeichenfolgen in einer TenantsSpalte:

>>> 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         

Jetzt ersetzen wir alle leeren Zeichenfolgen in der TenantsSpalte durch np.nanObjekte wie folgt:

>>> 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

Jetzt können wir die Nullwerte löschen:

>>> 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
McMath
quelle
Vielen Dank, ich werde es versuchen und zurückkommen!
Amrita Sawant
2
@mcmath, ein bisschen neugierig. Warum importierst du numpy und verwendest es, np.nanwenn du kannst pd.np.nan?
propjk007
3
@ Propjk007, wie bei vielen Dingen im Leben, gibt es viele Möglichkeiten, viele Dinge zu tun
Andrew
Aus meinen Tests geht hervor , dass dies df[df['Tenant'].astype(bool)](vorausgesetzt, keine Leerzeichen - nur leere Zeichenfolge) schneller ist alsdf.replace('', np.nan).dropna(subset=['Tenant'])
cs95
42

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

Leere Zeichenfolgen sind falsch, was bedeutet, dass Sie nach folgenden Bool-Werten filtern können:

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

Wenn Sie nicht nur leere Zeichenfolgen entfernen möchten, sondern auch Zeichenfolgen, die nur Leerzeichen enthalten, verwenden Sie str.stripFolgendes:

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

Schneller als du denkst

.astypeist eine vektorisierte Operation, dies ist schneller als jede bisher vorgestellte Option. Zumindest aus meinen Tests. YMMV.

Hier ist ein Zeitvergleich, den ich in einige andere Methoden geworfen habe, die mir einfallen könnten.

Geben Sie hier die Bildbeschreibung ein

Benchmarking-Code als Referenz:

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)
cs95
quelle
33

value_counts lässt NaN standardmäßig weg, sodass Sie höchstwahrscheinlich mit "" zu tun haben.

Sie können sie also einfach wie herausfiltern

filter = df["Tenant"] != ""
dfNew = df[filter]
Bob Haffner
quelle
1
@ Bobs Lösung hat bei mir nicht funktioniert. df.dropna (subset = ['Tenant'], inplace = True) funktioniert.
Amrita Sawant
1
Das tut mir leid. Ich dachte du hast es mit "" s zu tun. Sie sollten Ihre Lösung als Antwort veröffentlichen
Bob Haffner
8

Es gibt eine Situation, in der die Zelle einen Leerraum hat, den Sie nicht sehen können

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

um dann Leerzeichen als NaN zu ersetzen

df= df.dropna(subset=['col'])
Lernen
quelle
4

Sie können diese Variante verwenden:

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

Dies wird ausgegeben (** - nur gewünschte Zeilen hervorheben):

   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

Verwenden Sie den folgenden Code, um alles zu löschen, was keinen Bildungswert hat:

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

('~' bedeutet NICHT)

Ergebnis:

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