我想知道在pandas中是否有一种有效的方法:给定一个数据帧,第一行小于给定值是多少?例如,给定:
addr
0 4196656
1 4197034
2 4197075
3 4197082
4 4197134
第一个小于4197080的值是多少?我希望它只返回4197075行。 一个解决方案是首先过滤4197080,然后取最后一行,但这看起来是一个非常慢的O(N)操作(首先构建一个数据帧,然后取最后一行),而二进制搜索需要O (logN)的
df.addr[ df.addr < 4197080].tail(1)
我计时了,创建df.addr[ df.addr < 4197080]
或多或少与df.addr[ df.addr < 4197080].tail(1)
相同,强烈暗示它在内部构建整个df。
num = np.random.randint(0, 10**8, 10**6)
num.sort()
df = pd.DataFrame({'addr':num})
df = df.set_index('addr', drop=False)
df = df.sort_index()
获得第一个较小的值非常慢:
%timeit df.addr[ df.addr < 57830391].tail(1)
100 loops, best of 3: 7.9 ms per loop
使用lt改进了一点:
%timeit df.lt(57830391)[-1:]
1000 loops, best of 3: 853 µs per loop
但仍然远不及二元搜索那么快:
%timeit bisect(num, 57830391, 0, len(num))
100000 loops, best of 3: 6.53 µs per loop
还有更好的方法吗?
答案 0 :(得分:7)
这需要0.14.0
请注意,框架未排序。
In [16]: s = df['addr']
找出低于要求的最大值
In [18]: %timeit s[s<5783091]
100 loops, best of 3: 9.01 ms per loop
In [19]: %timeit s[s<5783091].nlargest(1)
100 loops, best of 3: 11 ms per loop
所以这比实际执行完全排序然后索引要快。
.copy
是为了避免偏向原地排序。
In [32]: x = np.random.randint(0, 10**8, 10**6)
In [33]: def f(x):
....: x.copy().sort()
....:
In [35]: %timeit f(x)
10 loops, best of 3: 67.2 ms per loop
如果您只是搜索ALREADY SORTED系列,请使用searchsorted
。请注意,您必须使用numpy版本(例如,在.values
上运行。系列版本将在0.14.1中定义)
In [41]: %timeit s.values.searchsorted(5783091)
100000 loops, best of 3: 2.5 µs per loop