【问题标题】:How to delete rows from a pandas DataFrame based on a conditional expression [duplicate]如何根据条件表达式从熊猫数据框中删除行[重复]
【发布时间】:2021-07-18 17:08:47
【问题描述】:

我有一个 pandas DataFrame,我想从中删除特定列中字符串长度大于 2 的行。

我希望能够做到这一点(根据this answer):

df[(len(df['column name']) < 2)]

但我只是得到错误:

KeyError: u'no item named False'

我做错了什么?

(注意:我知道我可以使用 df.dropna() 删除包含任何 NaN 的行,但我没有看到如何根据条件表达式删除行。)

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    当您执行len(df['column name']) 时,您只会得到一个数字,即 DataFrame 中的行数(即列本身的长度)。如果要将len 应用于列中的每个元素,请使用df['column name'].map(len)。所以试试

    df[df['column name'].map(len) < 2]
    

    【讨论】:

    • 我想出了一种使用列表理解的方法:df[[(len(x) &lt; 2) for x in df['column name']]] 但你的更好。感谢您的帮助!
    • 如果有人需要更复杂的比较,总是可以使用 lambda。 df[df['column name'].map(lambda x: str(x)!=".")]
    • 由于某种原因,除了@4lberto 发布的选项外,其他选项都没有对我有用。我在 pandas 0.23.4 和 python 3.6
    • 我会在最后添加一个.copy(),以防您以后想编辑此数据框(例如,分配新列会引发“A value is trying to be set on a copy of来自 DataFrame 的切片”警告。
    【解决方案2】:

    直接回答这个问题的原标题“如何根据条件表达式从熊猫数据框中删除行”(我理解这不一定是 OP 的问题,但可以帮助其他用户遇到这个问题)一种方法来做到这一点就是使用drop方法:

    df = df.drop(some labels)
    df = df.drop(df[<some boolean condition>].index)
    

    示例

    删除列'score'

    df = df.drop(df[df.score < 50].index)
    

    就地版本(如 cmets 中指出的那样)

    df.drop(df[df.score < 50].index, inplace=True)
    

    多个条件

    (见Boolean Indexing

    运算符是:| 对应于or&amp; 对应于and~ 对应于not。这些必须 使用括号分组。

    删除列'score' 20 的所有行

    df = df.drop(df[(df.score < 50) & (df.score > 20)].index)
    

    【讨论】:

    • 我只想说,drop 功能支持就地替换。 IE,。您的解决方案与 df.drop(df[df.score
    • 只想指出,在使用此索引技巧之前,您需要确保您的索引值是唯一的(或调用reset_index())。当从我的数据框中删除多行的方式时,我发现这一点很困难。
    • 如何删除列类型为 str 的所有行?我只想保留列表列类型。我试过test = df.drop(df[df['col1'].dtype == str].index),但我得到错误KeyError: False我也试过df.drop(df[df.col1.dtype == str].index)df.drop(df[type(df.cleaned_norm_email) == str].index),但似乎没有任何效果?任何人都可以建议。谢谢! @用户
    • 这是一个老问题,但是...@aquatically-challenged-fish 比这个要快得多。请注意,您计算 df[(df.score &lt; 50) &amp; (df.score &gt; 20)] 作为答案的一部分。如果你反过来做df = df[(df.score &gt;= 50) | (df.score &lt;= 20)],你会更快得到答案。
    • @Nguaial 不,它们不一样。他们是相反的。因此,我建议不要像上面建议的那样放弃,而是像 df = df[... 那样保留而不是 df = df.drop(...
    【解决方案3】:

    您可以将DataFrame 分配给其自身的过滤版本:

    df = df[df.score > 50]
    

    这比drop快:

    %%timeit
    test = pd.DataFrame({'x': np.random.randn(int(1e6))})
    test = test[test.x < 0]
    # 54.5 ms ± 2.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    %%timeit
    test = pd.DataFrame({'x': np.random.randn(int(1e6))})
    test.drop(test[test.x > 0].index, inplace=True)
    # 201 ms ± 17.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    %%timeit
    test = pd.DataFrame({'x': np.random.randn(int(1e6))})
    test = test.drop(test[test.x > 0].index)
    # 194 ms ± 7.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

    【讨论】:

    • 如何使用或条件检查多列?
    • 请注意,如果周围仍然存在对原始未过滤数据帧的引用,则过滤后的版本是原始数据帧的视图(也称为切片),如果过滤后的数据帧将导致麻烦稍后需要修改(例如添加一列)。在这种情况下,可能需要进行显式复制,如 df=df[mask].copy()。这是通过发出警告来显示问题的代码示例: df = pd.DataFrame([(1,0),(2,3)]); df1 = df; df = df[df[0]>1]; df['b'] = '某个值';
    【解决方案4】:

    在 pandas 中,您可以使用边界执行 str.len 并使用布尔结果对其进行过滤。

    df[df['column name'].str.len().lt(2)]
    

    【讨论】:

      【解决方案5】:

      如果您想根据列值上的某些复杂条件删除数据框行,那么以上述方式编写可能会很复杂。我有以下更简单的解决方案,它总是有效的。让我们假设您要删除带有“标题”的列,因此首先将该列放入列表中。

      text_data = df['name'].tolist()
      

      现在对列表的每个元素应用一些函数并将其放入熊猫系列中:

      text_length = pd.Series([func(t) for t in text_data])
      

      就我而言,我只是想获取令牌的数量:

      text_length = pd.Series([len(t.split()) for t in text_data])
      

      现在在数据框中添加一个带有上述系列的额外列:

      df = df.assign(text_length = text_length .values)
      

      现在我们可以在新列上应用条件,例如:

      df = df[df.text_length  >  10]
      
      def pass_filter(df, label, length, pass_type):
      
          text_data = df[label].tolist()
      
          text_length = pd.Series([len(t.split()) for t in text_data])
      
          df = df.assign(text_length = text_length .values)
      
          if pass_type == 'high':
              df = df[df.text_length  >  length]
      
          if pass_type == 'low':
              df = df[df.text_length  <  length]
      
          df = df.drop(columns=['text_length'])
      
          return df
      

      【讨论】:

        【解决方案6】:

        我将扩展@User 的通用解决方案以提供drop 免费替代方案。这是针对根据问题标题(不是 OP 的问题)在此处指导的人

        假设您要删除所有具有负值的行。一种衬垫解决方案是:-

        df = df[(df > 0).all(axis=1)]
        

        分步说明:--

        让我们生成一个 5x5 随机正态分布数据框

        np.random.seed(0)
        df = pd.DataFrame(np.random.randn(5,5), columns=list('ABCDE'))
              A         B         C         D         E
        0  1.764052  0.400157  0.978738  2.240893  1.867558
        1 -0.977278  0.950088 -0.151357 -0.103219  0.410599
        2  0.144044  1.454274  0.761038  0.121675  0.443863
        3  0.333674  1.494079 -0.205158  0.313068 -0.854096
        4 -2.552990  0.653619  0.864436 -0.742165  2.269755
        

        让条件是删除否定。满足条件的布尔 df:-

        df > 0
              A     B      C      D      E
        0   True  True   True   True   True
        1  False  True  False  False   True
        2   True  True   True   True   True
        3   True  True  False   True  False
        4  False  True   True  False   True
        

        满足条件的所有行的布尔序列请注意,如果行中的任何元素不符合条件,则该行被标记为 false

        (df > 0).all(axis=1)
        0     True
        1    False
        2     True
        3    False
        4    False
        dtype: bool
        

        最后根据条件从数据框中过滤掉行

        df[(df > 0).all(axis=1)]
              A         B         C         D         E
        0  1.764052  0.400157  0.978738  2.240893  1.867558
        2  0.144044  1.454274  0.761038  0.121675  0.443863
        

        您可以将其分配回 df 以实际 deletefilter 在上面完成
        df = df[(df &gt; 0).all(axis=1)]

        这可以很容易地扩展为过滤掉包含 NaN s(非数字条目)的行:-
        df = df[(~df.isnull()).all(axis=1)]

        这也可以简化为以下情况:删除 E 列为负数的所有行

        df = df[(df.E>0)]
        

        我想以一些分析统计信息作为结尾,说明为什么 @User 的 drop 解决方案比基于原始列的过滤慢:-

        %timeit df_new = df[(df.E>0)]
        345 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
        %timeit dft.drop(dft[dft.E < 0].index, inplace=True)
        890 µs ± 94.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
        

        一列基本上是一个SeriesNumPy 数组,它可以被索引而不需要任何成本。对于那些对底层内存组织如何影响执行速度感兴趣的人来说,这里是一个很棒的Link on Speeding up Pandas

        【讨论】:

          猜你喜欢
          • 2017-10-04
          • 2021-01-07
          • 2019-04-19
          • 1970-01-01
          • 2015-10-15
          • 2018-02-05
          相关资源
          最近更新 更多