Pandas:使用小于给定值的元素获得第一行的有效方法

时间:2014-06-17 12:55:37

标签: python pandas

我想知道在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

还有更好的方法吗?

1 个答案:

答案 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