A B
0 0.00 514.51
1 0.75 514.51
2 1.10 514.42
3 3.52 514.41
4 5.59 514.43
5 6.52 514.43
6 7.45 514.42
7 5.53 514.42
8 4.53 514.36
9 3.61 514.38
10 1.55 514.36
我想选择所有行,直到第一个A value
更大为棕色6。
输出应为:
A B
0 0.00 514.51
1 0.75 514.51
2 1.10 514.42
3 3.52 514.41
4 5.59 514.43
哪个是最快的迭代模式?
我试过了:
def first(g):
if g.A.ge(45.0).any():
return g[cond].iloc[0]
df.apply(first)
答案 0 :(得分:3)
使用cumprod
df[df.A.lt(6).cumprod().astype(bool)]
Out[303]:
A B
0 0.00 514.51
1 0.75 514.51
2 1.10 514.42
3 3.52 514.41
4 5.59 514.43
答案 1 :(得分:3)
您还可以使用iloc
和next
df.iloc[:next(idx for idx in df.index if df.iloc[idx, 0] > 6)]
关于@jezrael对时间的评论,这里有关于时间安排的图表
Method1 : df.iloc[:next(idx for idx in df.index if df.iloc[idx, 0] > 6)]
Method2 : df[df.A.lt(6).cumprod().astype(bool)]
Method3 : df.loc[:df.A.ge(4.50).idxmax()]
这样
基本上M2和M3彼此非常接近,稍微偏爱M2,因为df增长超过100k行+。到目前为止,M1是大型dfs的最低性能,但在非常小的dfs中速度更快。
基本上,速度取决于:第一次出现的位置,以及df
的大小。在这里,我已经设置了一个固定的第一次出现在开头附近,在不同的位置会很有趣:)我可以稍后添加
答案 2 :(得分:2)
使用loc
和idxmax
通过布尔掩码选择所有行首先True
:
df = df.loc[:df.A.ge(4.50).idxmax()]
print (df)
A B
0 0.00 514.51
1 0.75 514.51
2 1.10 514.42
3 3.52 514.41
4 5.59 514.43
<强>详情:
print (df.A.ge(4.50))
0 False
1 False
2 False
3 False
4 True
5 True
6 True
7 True
8 True
9 False
10 False
Name: A, dtype: bool
print (df.A.ge(4.50).idxmax())
4
有很多很好的解决方案,所以我对时间很好奇:
它实际上取决于第一个值的位置,所以我将第一个值设置为索引值的一半(在实际数据中它应该是不同的):
df = pd.DataFrame({'A':np.random.rand(10000)})
df.loc[5000, 'A'] = 10
#print (df)
In [66]: %timeit df[df.A.lt(6).cumprod().astype(bool)]
831 µs ± 31.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [67]: %timeit df.loc[:df.A.ge(4.50).idxmax()]
502 µs ± 4.36 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [68]: %timeit df.iloc[:next(idx for idx in df.index if df.iloc[idx, 0] > 6)]
67.7 ms ± 1.23 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [69]: %%timeit
...: result = df
...: for i, a in enumerate(df['A']):
...: if a >= 6:
...: result = df.iloc[:i+1,:]
...: break
...:
845 µs ± 8.93 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
答案 3 :(得分:0)
更新:通过更快的回答修订
对于简短的数据帧,或者如果答案在数据框的早期,您可以使用迭代到第一个匹配然后中断的Python for
循环获得快速结果(请参阅下面的for_loop()
函数)。但是,如果您需要扫描数百或数千行来查找匹配项,那么使用矢量化函数可能会更快,即使它们在数据帧的整个长度上进行一次或两次评估也是如此。
其他人提出了一些很好的矢量化操作,但我在下面添加的nonzero()
函数看起来是迄今为止最快的。
一些能完成这项工作的职能:
def nonzero():
# one-liner if you know there are matches:
# df.iloc[:(df.A >= 6).nonzero()[0][0],:]
indexes = (df.A.values >= 6).nonzero()[0]
if len(indexes) > 0:
return df.iloc[:indexes[0],:]
else:
return df
def for_loop():
result = df
for i, a in enumerate(df['A']):
if a >= 6:
result = df.iloc[:i,:]
break
return result
def idxmax():
return df.loc[:df.A.ge(4.50).idxmax()]
def cumprod():
return df[df.A.lt(6).cumprod().astype(bool)]
def next_idx():
return df.iloc[:next(idx for idx in df.index if df.iloc[idx, 0] > 6)]
def test_it(func, reps):
dur = timeit.timeit(stmt=func+'()', setup='from __main__ import df, '+func, number=reps)
print('{}: {}'.format(func, dur))
使用小型数据框进行测试:
df = pd.DataFrame.from_records([
[0.00, 514.51],
[0.75, 514.51],
[1.10, 514.42],
[3.52, 514.41],
[5.59, 514.43],
[6.52, 514.43],
[7.45, 514.42],
[5.53, 514.42],
[4.53, 514.36],
[3.61, 514.38],
[1.55, 514.36]
], columns = ['A','B'])
for func in ['nonzero', 'for_loop', 'idxmax', 'cumprod', 'next_idx']:
test_it(func, 10000)
# nonzero: 1.28068804741
# for_loop: 1.22211813927
# idxmax: 3.8852930069
# cumprod: 6.28086519241
# next_idx: 1.78734588623
这是一个具有更大数据帧的测试,其中第一个匹配是1,000,000行中的600,000行。我遗漏了for_loop
和next_idx
,因为他们花了一分钟时间进行此测试。
df = pd.DataFrame({'A':pd.np.arange(0,10,0.000001), 'B':514.51})
for func in ['nonzero', 'idxmax', 'cumprod']:
test_it(func, 100)
# nonzero: 3.25263190269
# idxmax: 9.08449816704
# cumprod: 24.7965559959
因此看起来具有短路功能的Python for
循环对于小型数据帧来说可能是最快的,但对于大型数据帧,通过矢量化操作测试每一行然后找到偏移量会更快匹配行(例如,通过nonzero()
函数)。