如何根据条件表达式从熊猫数据帧删除行

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

我希望能够做到这一点(每这个答案):

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

但是我得到了一个错误:

KeyError: u'no item named False'

我做错了什么?

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

1343379 次浏览

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

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

要直接回答这个问题的原标题“如何基于条件表达式从pandas数据框架中删除行”;(我知道这不一定是OP的问题,但可以帮助其他用户遇到这个问题)一种方法是使用下降方法:

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

例子

删除列“score”为<的所有行;50:

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

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

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

多个条件

(见# EYZ0)

操作符是:|用于or&用于and~用于not。这些一定是

删除列“score”为<的所有行;50 >20.

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

你可以将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)

在pandas中,你可以对你的边界执行str.len,并使用布尔结果来过滤它。

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

如果你想要根据列值上的一些复杂条件来删除数据帧的行,那么按照上面所示的方式来写会很复杂。我有以下简单的解决方案,它总是有效的。让我们假设你想要删除带有“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

我将扩展@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

# EYZ0

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

# EYZ0

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 过滤器 # EYZ0 < / p > 这可以很容易地扩展为过滤包含NaN s(非数字项)的行:-
# EYZ0 < / p >

这也可以简化为以下情况:删除列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数组,它可以被索引而不需要任何代价。对于那些对底层内存组织如何影响执行速度感兴趣的人来说,这里有一个很棒的有关加速熊猫的连结: