熊猫系列的循环表现

时间:2019-10-29 03:32:41

标签: python pandas performance

我很好奇这样一个事实,当将函数应用于for循环中的pd.Series的每个元素时,执行时间看起来比O(N)快得多。

考虑下面的一个函数,该函数将数字按位旋转,但是代码本身在这里并不重要。

def rotate(x: np.uint32) -> np.uint32:
    return np.uint32(x >> 1) | np.uint32((x & 1) << 31)

在for循环中执行此代码1000次时,只需按照预期执行1000次。

x = np.random.randint(2 ** 32 - 1, dtype=np.uint32)

%timeit rotate(x)
# 13 µs ± 807 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%%timeit
for i in range(1000):
    rotate(x)
# 9.61 ms ± 255 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

但是,当我在大小为1000的Series上的for循环中应用此代码时,它的运行速度明显加快。

s = pd.Series(np.random.randint(2 ** 32 - 1, size=1000, dtype=np.uint32))

%%timeit
for x in s:
    rotate(x)
# 2.08 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

我对使这种情况发生的机制感到好奇吗?

1 个答案:

答案 0 :(得分:1)

请注意,在第一个循环中,您实际上并没有使用迭代器的下一个值。以下是更好的比较:

...: %%timeit 
...: for i in range(1000):
...:     rotate(i) 
...:                                                                                                 
1.46 ms ± 71.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

...: %%timeit 
...: for x in s: 
...:     rotate(x) 
...:                                                                                                
1.6 ms ± 66.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

毫不奇怪,它们的表现大致相同。

在您的原始示例中,通过使用在外部声明的变量x,解释器需要使用LOAD_GLOBAL 2 (x)加载该变量,而如果您仅使用值i,则解释器可以只需致电LOAD_FAST 0 (i),顾名思义,这会更快。