我有一个熊猫DataFrame,我想从它删除行,其中字符串的长度在特定列大于2。
我希望能够做到这一点(根据这个答案):
df[(len(df['column name']) < 2)]
但是我得到了一个错误:
KeyError: u'no item named False'
我做错了什么?
(注意:我知道我可以使用df.dropna()来去除包含任何NaN的行,但我没有看到如何基于条件表达式删除行。)
我有一个熊猫DataFrame,我想从它删除行,其中字符串的长度在特定列大于2。
我希望能够做到这一点(根据这个答案):
df[(len(df['column name']) < 2)]
但是我得到了一个错误:
KeyError: u'no item named False'
我做错了什么?
(注意:我知道我可以使用df.dropna()来去除包含任何NaN的行,但我没有看到如何基于条件表达式删除行。)
当你做len(df['列名'])时,你只得到一个数字,即DataFrame中的行数(即列本身的长度)。如果你想对列中的每个元素应用len,使用df['列名'].map(len)。所以尝试
df[df['column name'].map(len) < 2]
为了直接回答这个问题的原始标题“如何根据条件表达式从pandas DataFrame中删除行”(我理解这不一定是OP的问题,但可以帮助其他用户遇到这个问题),一种方法是使用drop方法:
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)
多个条件
(见布尔索引)
操作符为:|表示或,&表示与,~表示非。这些一定是 用圆括号分组。
删除列“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)
如果你想要根据列值上的一些复杂条件来删除数据帧的行,那么按照上面所示的方式来写会很复杂。我有以下简单的解决方案,它总是有效的。让我们假设你想要删除带有“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的通用解决方案,以提供一个免费的替代方案。这是为那些根据问题标题(不是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来实际删除和过滤 Df = Df [(Df > 0).all(轴=1)]
这可以很容易地扩展到过滤包含NaN(非数字条目)的行:- Df = Df [(~ Df .isnull())).all(轴=1)]
这也可以简化为以下情况:删除列E为负的所有行
df = df[(df.E>0)]
我想以一些分析数据来结束,为什么@User的drop solution比基于原始列的过滤要慢:-
%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)
列基本上是一个系列,即NumPy数组,它可以没有任何代价被索引。对于那些对底层内存组织如何影响执行速度感兴趣的人来说,这里有一个关于加速Pandas的链接: