如何基于条件表达式从pandas DataFrame中删除行

sjs*_*sjs 255 python pandas

我有一个pandas DataFrame,我想删除它中的行,其中特定列中的字符串长度大于2.我知道我可以df.dropna()用来摆脱包含any的行NaN,但我没有看到如何基于条件表达式删除行.

这个问题的答案似乎与我想要的非常接近 - 似乎我应该能够做到这样的事情:

df[(len(df['column name']) < 2)]
Run Code Online (Sandbox Code Playgroud)

但我得到错误:

KeyError: u'no item named False'
Run Code Online (Sandbox Code Playgroud)

谁能告诉我我做错了什么?

Use*_*ser 674

要直接回答这个问题的标题(我理解的不一定是OP的问题,但可以帮助其他用户遇到这个问题),一种方法是使用drop方法:

df = df.drop(some labels)

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

要删除列"得分"<50的所有行:

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

就地版(正如评论中所指出)

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

多种条件

(参见布尔索引)

运营商是:|for or,&for and~for not.必须使用括号对这些进行分组.

删除列"得分"<50和> 20的所有行

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

  • 我只想说,drop函数支持inplace替换.IE中.你的解决方案与df.drop相同(df [df.score <50] .index,inplace = True).然而不知道"索引"的伎俩.帮助了我很多 (28认同)
  • 只是想指出在使用这个索引技巧之前,你需要确保你的索引值是唯一的(或者调用`reset_index()`).当从我的数据帧中删除许多行的方式时,我发现了这个问题. (8认同)
  • 如何删除列类型为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)`但似乎什么都没有用?任何人都可以建议.谢谢!@用户 (3认同)
  • 这是一个老问题,但是...@aquatically-challenged-fish 比这个问题快得多。请注意,您计算 `df[(df.score &lt; 50) &amp; (df.score &gt; 20)]` 作为答案的一部分。如果你颠倒这个来做`df = df[(df.score &gt;= 50) | (df.score &lt;= 20)]` 你会更快地得到答案。 (2认同)
  • @Nguaial 不,它们不一样。他们是相反的。因此,不要像上面建议的那样丢弃,我建议保留,如 `df = df[...` 而不是 `df = df.drop(...` (2认同)
  • `reset_index(inplace=True)` 解决了我的行被无故删除的问题。谢谢,@Jay! (2认同)

Bre*_*arn 144

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

df[df['column name'].map(len) < 2]
Run Code Online (Sandbox Code Playgroud)

  • 如果有人需要更复杂的比较,可以始终使用lambda.`df [df ['column name'].map(lambda x:str(x)!=".")]` (10认同)
  • 我想出了一种使用列表理解的方法:`df [[(len(x)<2)for d in df ['column name']]]`但你的更好.谢谢你的帮助! (3认同)
  • 我会在最后添加一个 `.copy()`,以防你以后想编辑这个数据框(例如,分配新列会引发“一个值正在尝试在来自 DataFrame 的切片的副本上设置“ 警告。 (3认同)

Kab*_*ard 90

我正在寻找一个解决方案,我偶然发现了一个明显的方法,即只过滤数据帧并分配回原始数据帧,

df = df[df.score > 50]
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果仍然存在对原始未过滤数据帧的引用,则过滤后的版本是原始数据帧的视图(也称为原始数据帧的切片),如果稍后需要修改过滤数据帧(例如例如,添加一列)。在这种情况下,人们可能想要制作一份显式副本,如 df=df[mask].copy() 所示。以下是通过发出警告来显示该问题的代码示例: df = pd.DataFrame([(1,0),(2,3)]); df1 = df; df = df[df[0]&gt;1]; df['b'] = '某个值'; (10认同)

WeN*_*Ben 10

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

df[df['column name'].str.len().lt(2)]
Run Code Online (Sandbox Code Playgroud)


Zak*_*kir 7

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

假设您要删除所有带有负值的行。一种班轮解决方案是:-

df = df[(df > 0).all(axis=1)]
Run Code Online (Sandbox Code Playgroud)

逐步说明:-

让我们生成一个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
Run Code Online (Sandbox Code Playgroud)

设条件为删除负片。满足条件的布尔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
Run Code Online (Sandbox Code Playgroud)

满足条件的所有行的布尔系列 注意,如果该行中的任何元素失败,则该行被标记为false

(df > 0).all(axis=1)
0     True
1    False
2     True
3    False
4    False
dtype: bool
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

您可以分配回DF实际删除 VS 过滤器上面做ING
df = df[(df > 0).all(axis=1)]

可以很容易地扩展它以过滤出包含NaN的行(非数字项):
df = df[(~df.isnull()).all(axis=1)]

对于以下情况,也可以简化此操作:删除E列为负的所有行

df = df[(df.E>0)]
Run Code Online (Sandbox Code Playgroud)

我想以一些分析统计数据结尾,说明为什么@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)
Run Code Online (Sandbox Code Playgroud)

列基本上是Series一个NumPy数组,即可以免费索引的列。对于对基础内存组织如何发挥执行速度感兴趣的人们,这里有一个关于加速熊猫的好链接


小智 5

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

text_data = df['name'].tolist()
Run Code Online (Sandbox Code Playgroud)

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

text_length = pd.Series([func(t) for t in text_data])
Run Code Online (Sandbox Code Playgroud)

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

text_length = pd.Series([len(t.split()) for t in text_data])
Run Code Online (Sandbox Code Playgroud)

现在在数据框中添加一个额外的列,其中包含上述系列:

df = df.assign(text_length = text_length .values)
Run Code Online (Sandbox Code Playgroud)

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

df = df[df.text_length  >  10]
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)