【问题标题】:Drop rows containing empty cells from a pandas DataFrame从 pandas DataFrame 中删除包含空单元格的行
【发布时间】:2015-06-01 13:39:36
【问题描述】:

我有一个pd.DataFrame,它是通过解析一些 excel 电子表格创建的。其中有一列有空单元格。例如,下面是该列频率的输出,32320 条记录缺少 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

我正在尝试删除缺少租户的行,但是 .isnull() 选项无法识别缺失值。

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

该列的数据类型为“对象”。在这种情况下发生了什么?如何删除缺少 Tenant 的记录?

【问题讨论】:

标签: python pandas dataframe drop


【解决方案1】:

如果您不关心丢失文件所在的列,考虑到数据框的名称为 New 并且想要将新数据框分配给相同的变量,只需运行

New = New.drop_duplicates()

如果您特别想删除 Tenant 列中空值的行,这将完成工作

New = New[New.Tenant != '']

这也可用于删除具有特定值的行 - 只需将字符串更改为所需的值。

注意:如果不是空字符串,而是NaN,那么

New = New.dropna(subset=['Tenant'])

【讨论】:

    【解决方案2】:

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

    空字符串是假的,这意味着您可以像这样过滤布尔值:

    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
    

    如果您的目标不仅是删除空字符串,还要删除仅包含空格的字符串,请事先使用str.strip

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

    比你想象的要快

    .astype 是矢量化操作,这比目前提供的每个选项都快。至少,从我的测试来看。 YMMV。

    这是一个时间比较,我已经抛出了一些我能想到的其他方法。

    基准代码,供参考:

    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)
    

    【讨论】:

    • 请注意,当使用astype(bool) 进行转换时,即使字符串“False”的计算结果为 True。这对这个用例有好处。
    • 陷阱:pd.DataFrame({'a': [np.NaN]}).a.astype(bool) 返回 True。
    • bool(math.nan) 也是如此,所以这是正确的 Pythonic 行为。如果 math.nan 需要对其存在或不存在进行显式测试,那么 np.NaN 也应该如此。
    • @chsymann 问题是关于空字符串的。如果你需要删除 nans,dropna 是迄今为止这样做的惯用方法
    【解决方案3】:

    有一种情况,单元格有空格,看不到,用

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

    将空格替换为NaN,然后​​

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

    【讨论】:

      【解决方案4】:

      如果一个值是 np.nan 对象,Pandas 会将其识别为 null,这将在 DataFrame 中打印为 NaN。您的缺失值可能是空字符串,Pandas 不会将其识别为 null。要解决此问题,您可以使用 replace() 将空字符串(或空单元格中的任何内容)转换为 np.nan 对象,然后在 DataFrame 上调用 dropna() 以删除包含空租户的行。

      为了演示,我们在 Tenants 列中创建了一个包含一些随机值和一些空字符串的 DataFrame:

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

      现在我们将Tenants 列中的所有空字符串替换为np.nan 对象,如下所示:

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

      现在我们可以删除空值了:

      >>> 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,有点好奇。为什么要导入 numpy 并使用 np.nan,而你可以做到 pd.np.nan
      • @propjk007,就像生活中的很多事情一样,有很多方法可以做很多事情
      • 从我的tests 看来,df[df['Tenant'].astype(bool)](假设没有空格字符——仅限空字符串)比df.replace('', np.nan).dropna(subset=['Tenant'])
      • @propjk007 pandas.np 模块已弃用,将在未来版本中从 pandas 中删除。直接导入 numpy 而不是 pd.np.nan
      【解决方案5】:

      您可以使用这种变体:

      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
      

      这将输出(** - 仅突出显示所需的行):

         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
      

      因此,要删除没有“教育”价值的所有内容,请使用以下代码:

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

      ('~'表示NOT)

      结果:

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

      【讨论】:

        【解决方案6】:

        value_counts 默认省略 NaN,因此您最有可能处理 ""。

        所以你可以像过滤掉它们

        filter = df["Tenant"] != ""
        dfNew = df[filter]
        

        【讨论】:

        • @Bobs 解决方案对我不起作用。 df.dropna(subset=['tenant'], inplace=True) 有效。
        • 很抱歉。我以为你在处理“”s。您应该发布您的解决方案作为答案
        猜你喜欢
        • 1970-01-01
        • 2017-11-20
        • 2021-06-26
        • 1970-01-01
        • 1970-01-01
        • 2014-04-04
        • 2023-02-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多