Pandas:为什么系列索引使用。loc需要100倍的时间在第一次运行时计时呢?

2024-04-24 21:02:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在用.loc剪切一个相当大的pandas系列(~5M),我在检查时间以优化代码时偶然发现了一些奇怪的行为。你知道吗

奇怪的是,像series_object.loc[some_indexes]这样的第一次切片尝试要比下面的尝试花费100倍的时间。你知道吗

当我尝试timeit时,它并没有反映出这种行为,但是当使用“time”检查部分圈数时,我们可以看到第一圈所花的时间比下面的要长得多。你知道吗

  1. .loc正在使用某种缓存吗?如果是这样的话,垃圾收集如何不影响这一点呢?你知道吗
  2. timeit是否在禁用垃圾回收器的情况下进行缓存,而不是按照设想的那样运行?你知道吗
  3. 当我的应用程序在实时环境中运行时,我应该相信我的应用程序在生产环境中需要多长时间?你知道吗

我在windowslinux机器上使用了不同版本的python (3.6, 3.7 and 2.7)进行了尝试,结果总是一样的。你知道吗

提前谢谢你的帮助。这件事已经让我头痛了一个星期了,我不想再怀疑了%timeit:)

复制:

将以下代码保存到python文件中,例如:test_loc_times.py

    import pandas as pd
    import numpy as np
    import timeit
    import time, gc

    def get_data():
        ids = np.arange(size_bigseries)
        big_series = pd.Series(index=ids, data=np.random.rand(len(ids)), name='{} elements series'.format(len(ids)))
        small_slice = np.arange(size_slice)
        return big_series, small_slice

    # Method to test: a simple pandas slicing with .loc
    def basic_loc_indexing(pd_series, slice_ids):
        return pd_series.loc[slice_ids].dropna()

    # method to time it
    def timing_it(func, n, *args):
        gcold = gc.isenabled()
        gc.disable()
        times = []
        for i in range(n):
            s = time.time()
            func(*args)
            times.append((time.time()-s)*1000)

        if gcold:
            gc.enable()

        return times

    if __name__ == '__main__':
        import sys
        n_tries = int(sys.argv[1]) if len(sys.argv)>1 and sys.argv[1] is not None else 1000
        size_bigseries = int(sys.argv[2]) if len(sys.argv)>2 and sys.argv[2] is not None else 5000000 #5M
        size_slice =  int(sys.argv[3]) if  len(sys.argv)>3 and sys.argv[3] is not None else 100 #5M
        #1: timeit()
        big_series, small_slice = get_data()
        time_with_timeit = timeit.timeit('basic_loc_indexing(big_series, small_slice)',"gc.disable(); from __main__ import basic_loc_indexing, big_series, small_slice",number=n_tries)
        print("using timeit: {:.6f}ms".format(time_with_timeit/n_tries*1000))
        del big_series, small_slice

        #2: time()
        big_series, small_slice = get_data()
        time_with_time = timing_it(basic_loc_indexing, n_tries, big_series, small_slice)
        print("using time: {:.6f}ms".format(np.mean(time_with_time)))
        print('head detail: {}\n'.format(time_with_time[:5]))

试用:

python test_loc_times.py 1000 5000000 100

这将运行timeittime1000圈,从5M切片100元素熊猫系列. 你知道吗

你可以自己尝试其他的值,第一次运行总是需要更长的时间。你知道吗

标准输出:

>>> using timeit: 0.789754ms
>>> using time: 0.829869ms
>>> head detail: [145.02716064453125, 0.7691383361816406, 0.7028579711914062, 0.5738735198974609, 0.6380081176757812]

奇怪吧?你知道吗


Tags: importidstimewithnpsysslicegc
1条回答
网友
1楼 · 发布于 2024-04-24 21:02:45

此代码可能不是幂等的(具有影响其执行的副作用)。你知道吗

timeit将首先运行代码一次,以测量时间并推断出应该使用的循环和运行次数。如果您的代码不是幂等的(有副作用,比如兑现),那么第一次运行(没有记录)将更长,随后的运行(更快的运行)将被测量和报告。你知道吗

您可以查看可以传递给timeitsee the doc)的参数,以指定循环数并放弃初始运行。你知道吗

另请注意(摘自上面链接的文档):

The times reported by %timeit will be slightly higher than those reported by the timeit.py script when variables are accessed. This is due to the fact that %timeit executes the statement in the namespace of the shell, compared with timeit.py, which uses a single setup statement to import function or create variables. Generally, the bias does not matter as long as results from timeit.py are not mixed with those from %timeit.

编辑:忽略了您将跑步次数传递给timeit的事实。在这种情况下,只有后面的部分我的答案适用,但你看到的数字似乎指向另一个问题。。。你知道吗

相关问题 更多 >