【问题标题】:What is the performance impact of non-unique indexes in pandas?pandas 中非唯一索引的性能影响是什么?
【发布时间】:2013-05-13 15:01:44
【问题描述】:

从 pandas 文档中,我收集到唯一值索引可以提高某些操作的效率,并且偶尔可以容忍非唯一索引。

从外部看,非唯一索引似乎不会以任何方式被利用。例如,下面的ix 查询速度很慢,似乎正在扫描整个数据帧

In [23]: import numpy as np
In [24]: import pandas as pd
In [25]: x = np.random.randint(0, 10**7, 10**7)
In [26]: df1 = pd.DataFrame({'x':x})
In [27]: df2 = df1.set_index('x', drop=False)
In [28]: %timeit df2.ix[0]
1 loops, best of 3: 402 ms per loop
In [29]: %timeit df1.ix[0]
10000 loops, best of 3: 123 us per loop

(我意识到两个 ix 查询不会返回相同的内容——这只是一个示例,在非唯一索引上调用 ix 似乎要慢得多)

有什么方法可以让 pandas 使用更快的查找方法,例如对非唯一和/或排序索引进行二分查找?

【问题讨论】:

    标签: python performance indexing pandas binary-search


    【解决方案1】:

    当索引唯一时,pandas 使用哈希表将键映射到值 O(1)。当索引非唯一且已排序时,pandas 使用二分查找 O(logN),当索引为随机排序时,pandas 需要检查索引中的所有键 O(N)。

    你可以调用sort_index方法:

    import numpy as np
    import pandas as pd
    x = np.random.randint(0, 200, 10**6)
    df1 = pd.DataFrame({'x':x})
    df2 = df1.set_index('x', drop=False)
    df3 = df2.sort_index()
    %timeit df1.loc[100]
    %timeit df2.loc[100]
    %timeit df3.loc[100]
    

    结果:

    10000 loops, best of 3: 71.2 µs per loop
    10 loops, best of 3: 38.9 ms per loop
    10000 loops, best of 3: 134 µs per loop
    

    【讨论】:

    • 我不明白最后的时间安排。 df3 应该更快?
    • @lucid_dreamer 我也很困惑,但是 df1 使用从 0 到 len(df1) - 1 的默认索引并且是唯一的,所以 df1.loc[] 使用哈希表。 df2 将索引设置为不唯一且未排序的“x”,因此它进行线性扫描,O(N)。 df3 与 df2 相同,但已排序且仍不唯一,因此它执行二进制搜索。
    • 那么为什么df2的线性扫描更快?
    • 我不明白为什么 pandas 会在这里切换到二进制搜索。对于多图,索引仍然可以在 O(1+R) 中完成,而不是 O(logN + R)(其中 R 是返回的结果数。
    • 这个时间比较实际上很容易产生误导,因为第一个语句df1.loc[100] 的作用与其他两个完全不同,即使用隐式创建的RangeIndex 检索第 100 行,而其他两个检索x == 100 的所有行。
    【解决方案2】:

    @HYRY said it well,但没有什么比带有时间的彩色图表更能说明问题了。

    绘图是使用perfplot 生成的。代码,供您参考:

    import pandas as pd
    import perfplot
    
    _rnd = np.random.RandomState(42)
    
    def make_data(n):    
        x = _rnd.randint(0, 200, n)
        df1 = pd.DataFrame({'x':x})
        df2 = df1.set_index('x', drop=False)
        df3 = df2.sort_index()
    
        return df1, df2, df3
    
    perfplot.show(
        setup=lambda n: make_data(n),
        kernels=[
            lambda dfs: dfs[0].loc[100],
            lambda dfs: dfs[1].loc[100],        
            lambda dfs: dfs[2].loc[100],
        ],
        labels=['Unique index', 'Non-unique, unsorted index', 'Non-unique, sorted index'],
        n_range=[2 ** k for k in range(8, 23)],
        xlabel='N',
        logx=True,
        logy=True,
        equality_check=False)
    

    【讨论】:

    • 我没有看到您实际在哪里为操作计时,并且通常在为 pandas 操作计时时遇到问题。
    • @young_souvlaki 没看懂,代码在图下的答案中内联,需要安装perfplot库。对于正在测试的实际方法,检查 make_data 函数,然后检查 kernels arg 到 perfplot.show
    • 啊,perfplot 正在计时。
    猜你喜欢
    • 1970-01-01
    • 2011-04-17
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多