根据涉及len(string)的条件表达式从pandas DataFrame删除行,从而给出KeyError


303

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

我希望能够做到这一点(根据此答案):

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

但我只是得到错误:

KeyError: u'no item named False'

我究竟做错了什么?

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

Answers:


168

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

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

3
我想出了一种使用列表理解的方法:df[[(len(x) < 2) for x in df['column name']]]但是您的列表要好得多。谢谢你的帮助!
sjs 2012年

13
如果有人需要更复杂的比较,则始终可以使用lambda。 df[df['column name'].map(lambda x: str(x)!=".")]
4lberto

1
出于某种原因,除了@ 4lberto发布的选项之外,其他选项对我都不起作用。我正在使用pandas 0.23.4python 3.6
goelakash

1
我想补充一个.copy()在最后你想,万一以后编辑这个数据框(例如,分配新列将提高“的值是要试图从一个数据帧切片的副本设置”的警告。
PlasmaBinturong

806

要直接回答该问题的原始标题“如何基于条件表达式从pandas DataFrame中删除行”(我理解这不一定是OP的问题,但可以帮助其他用户遇到此问题),一种方法是使用该的方法:

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)


32
我只想说明一下,drop函数支持就地替换。即 您的解决方案与df.drop(df [df.score <50] .index,inplace = True)相同。然而,不知道“索引”的技巧。对我有很大帮助
Quickbeam2k1

9
只是要指出,在使用此索引技巧之前,需要确保索引值是唯一的(或调用reset_index())。当从数据框中删除通往多行的方法时,我发现了这种困难的方法。
杰伊

3
如何删除列类型为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)但是似乎没有任何效果?任何人都可以建议。谢谢!@User
PyRsquared

1
这是一个古老的问题,但是... @ aquatically-challenged-fish比这快得多。请注意,您将计算df[(df.score < 50) & (df.score > 20)]作为答案的一部分。如果您撤消此操作df = df[(df.score >= 50) | (df.score <= 20)],则会更快地得到答案。
Roobie Nuby

1
@RoobieNuby-它们不是相同的条件。
Nguai al

106

您可以将分配给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)

如何检查多列使用或条件?
Piyush S. Wanare


9

我将扩展@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,以实际删除 vs 上面完成的过滤
df = df[(df > 0).all(axis=1)]

可以很容易地扩展它以过滤出包含NaN的行(非数字项):
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)

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


6

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

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

3

如果要基于某些复杂的条件在列值上删除数据帧的行,则以上述方式编写代码可能会很复杂。我有以下始终有效的简单解决方案。让我们假设您要删除带有“ header”的列,因此首先在列表中获取该列。

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.