最快的迭代超过df直到达到条件

时间:2018-05-29 19:31:33

标签: python pandas

            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)

4 个答案:

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

您还可以使用ilocnext

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()]

这样

times

基本上M2和M3彼此非常接近,稍微偏爱M2,因为df增长超过100k行+。到目前为止,M1是大型dfs的最低性能,但在非常小的dfs中速度更快。

基本上,速度取决于:第一次出现的位置,以及df的大小。在这里,我已经设置了一个固定的第一次出现在开头附近,在不同的位置会很有趣:)我可以稍后添加

答案 2 :(得分:2)

使用locidxmax通过布尔掩码选择所有行首先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_loopnext_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()函数)。