pandas 是否有有效的方法来获取带有条件的尾行
is there efficient way for pandas to get tail rows with a condition
我想获取带有条件的尾行
例如:
我想从列 'A' 中获取所有负尾行,例如:
test = pd.DataFrame({'A':[-8, -9, -10, 1, 2, 3, 0, -1,-2,-3]})
我希望 'method' 获得像这样的新框架:
A
0 -1
1 -2
2 -3
注意,尾部有多少'negative'个数字是不确定的。所以我不能 运行 test.tail(3)
看起来 pandas 提供的 'tail()' 函数只能 运行 给定的数字。
但是我的输入数据框可能太大了,我不想运行一个简单的循环来一个一个地检查
有没有聪明的方法来做到这一点?
尾巴有什么用?看来你只需要负数
test.query("A < 0")
更新:找到符号变化的地方,拆分数组并选择最后一个
split_points = (test.A.shift(1)<0) == (test.A<0)
np.split(test, split_points.loc[lambda x: x==False].index.tolist())[-1]
输出:
A
7 -1
8 -2
9 -3
这是你想要的吗?
test = pd.DataFrame({'A':[-8, -9, -10, 1, 2, 3, 0, -1,-2,-3]})
test = test.iloc[::-1]
test.loc[test.index.max():test[test['A'].ge(0)].index[0]+1]
输出:
A
9 -3
8 -2
7 -1
编辑,如果你想把它恢复成原来的顺序:
test.loc[test.index.max():test[test['A'].ge(0)].index[0]+1].iloc[::-1]
A
7 -1
8 -2
9 -3
如果您需要从 0 开始的索引,也可以选择 .reset_index(drop=True)
。
只需分享一张比较以上两个给定答案的性能图片
感谢 Patry 和 Macro
我改进了上面的测试,又做了一轮测试,因为我觉得旧的'testing sample'尺寸太小了,担心%%time
测量可能不准确。
我的新测试使用了一个非常大的头部数字,大小为 10000000,尾部有 3 个负数
因此新测试可以证明整个数据帧大小如何影响整体性能。
代码如下:
%%time
arr = np.arange(1,10000000,1)
arr = np.concatenate((arr, [-2,-3,-4]))
test = pd.DataFrame({'A':arr})
test = test.iloc[::-1]
test.loc[test.index.max():test[test['A'].ge(0)].index[0]+1].iloc[::-1]
%%time
arr = np.arange(1,10000000,1)
arr = np.concatenate((arr, [-2,-3,-4]))
test = pd.DataFrame({'A':arr})
split_points = (test.A.shift(1)<0) == (test.A<0)
np.split(test, split_points.loc[lambda x: x==False].index.tolist())[-1]
由于系统影响,我测试了10次,以上2种方法执行起来非常相似。在大约 50% 的情况下,Patryk 的代码甚至执行得更快
查看下面这张图片
我想获取带有条件的尾行
例如: 我想从列 'A' 中获取所有负尾行,例如:
test = pd.DataFrame({'A':[-8, -9, -10, 1, 2, 3, 0, -1,-2,-3]})
我希望 'method' 获得像这样的新框架:
A
0 -1
1 -2
2 -3
注意,尾部有多少'negative'个数字是不确定的。所以我不能 运行 test.tail(3)
看起来 pandas 提供的 'tail()' 函数只能 运行 给定的数字。
但是我的输入数据框可能太大了,我不想运行一个简单的循环来一个一个地检查
有没有聪明的方法来做到这一点?
尾巴有什么用?看来你只需要负数
test.query("A < 0")
更新:找到符号变化的地方,拆分数组并选择最后一个
split_points = (test.A.shift(1)<0) == (test.A<0)
np.split(test, split_points.loc[lambda x: x==False].index.tolist())[-1]
输出:
A
7 -1
8 -2
9 -3
这是你想要的吗?
test = pd.DataFrame({'A':[-8, -9, -10, 1, 2, 3, 0, -1,-2,-3]})
test = test.iloc[::-1]
test.loc[test.index.max():test[test['A'].ge(0)].index[0]+1]
输出:
A
9 -3
8 -2
7 -1
编辑,如果你想把它恢复成原来的顺序:
test.loc[test.index.max():test[test['A'].ge(0)].index[0]+1].iloc[::-1]
A
7 -1
8 -2
9 -3
如果您需要从 0 开始的索引,也可以选择 .reset_index(drop=True)
。
只需分享一张比较以上两个给定答案的性能图片
感谢 Patry 和 Macro
我改进了上面的测试,又做了一轮测试,因为我觉得旧的'testing sample'尺寸太小了,担心%%time
测量可能不准确。
我的新测试使用了一个非常大的头部数字,大小为 10000000,尾部有 3 个负数
因此新测试可以证明整个数据帧大小如何影响整体性能。
代码如下:
%%time
arr = np.arange(1,10000000,1)
arr = np.concatenate((arr, [-2,-3,-4]))
test = pd.DataFrame({'A':arr})
test = test.iloc[::-1]
test.loc[test.index.max():test[test['A'].ge(0)].index[0]+1].iloc[::-1]
%%time
arr = np.arange(1,10000000,1)
arr = np.concatenate((arr, [-2,-3,-4]))
test = pd.DataFrame({'A':arr})
split_points = (test.A.shift(1)<0) == (test.A<0)
np.split(test, split_points.loc[lambda x: x==False].index.tolist())[-1]
由于系统影响,我测试了10次,以上2种方法执行起来非常相似。在大约 50% 的情况下,Patryk 的代码甚至执行得更快
查看下面这张图片