【问题标题】:What causes "indexing past lexsort depth" warning in Pandas?是什么导致 Pandas 中出现“索引过去的 lexsort 深度”警告?
【发布时间】:2019-06-15 20:47:18
【问题描述】:

我正在使用df.loc[(key1, key2)] 索引一个大型多索引 Pandas df。有时我会得到一个系列(如预期的那样),但有时我会得到一个数据框。我正在尝试隔离导致后者的情况,但到目前为止,我所看到的只是它与获得PerformanceWarning: indexing past lexsort depth may impact performance 警告相关。

我想复制它以在此处发布,但我无法生成另一个给我同样警告的案例。这是我的尝试:

def random_dates(start, end, n=10):
    start_u = start.value//10**9
    end_u = end.value//10**9
    return pd.to_datetime(np.random.randint(start_u, end_u, n), unit='s')

np.random.seed(0)
df = pd.DataFrame(np.random.random(3255000).reshape(465000,7))  # same shape as my data
df['date'] = random_dates(pd.to_datetime('1990-01-01'), pd.to_datetime('2018-01-01'), 465000)
df = df.set_index([0, 'date'])
df = df.sort_values(by=[3])  # unsort indices, just in case
df.index.lexsort_depth
> 0
df.index.is_monotonic
> False
df.loc[(0.9987185534991936, pd.to_datetime('2012-04-16 07:04:34'))]
# no warning

所以我的问题是:是什么导致了这个警告?如何人工诱导?

【问题讨论】:

  • 你读过吗:pandas.pydata.org/pandas-docs/stable/… 只是检查
  • 是的 - 我试图按索引取消排序,所以我按列排序
  • 不管怎样,很明显索引没有排序——数字是随机生成的。

标签: python pandas


【解决方案1】:

根据pandas advanced indexing (Sorting a Multiindex)

在更高维度的对象上,如果其他轴具有 MultiIndex,您可以按级别对它们进行排序

还有:

即使数据未排序,索引也会起作用,但效率会相当低(并显示 PerformanceWarning)。它还将返回数据的副本而不是视图:

根据他们的说法,您可能需要确保索引正确排序。

【讨论】:

    【解决方案2】:

    TL;DR:您的索引未排序,这会严重影响性能。

    使用 df.sort_index() 对 DataFrame 的索引进行排序以解决警告并提高性能。


    我实际上已经在我的文章中详细描述了这个问题:Select rows in pandas MultiIndex DataFrame(在“问题 3”下)。

    为了重现,

    mux = pd.MultiIndex.from_arrays([
        list('aaaabbbbbccddddd'),
        list('tuvwtuvwtuvwtuvw')
    ], names=['one', 'two'])
    
    df = pd.DataFrame({'col': np.arange(len(mux))}, mux)
    
             col
    one two     
    a   t      0
        u      1
        v      2
        w      3
    b   t      4
        u      5
        v      6
        w      7
        t      8
    c   u      9
        v     10
    d   w     11
        t     12
        u     13
        v     14
        w     15
    

    您会注意到第二级没有正确排序。

    现在,尝试索引特定的横截面:

    df.loc[pd.IndexSlice[('c', 'u')]]
    PerformanceWarning: indexing past lexsort depth may impact performance.
      # encoding: utf-8
    
             col
    one two     
    c   u      9
    

    您会看到与xs 相同的行为:

    df.xs(('c', 'u'), axis=0)
    PerformanceWarning: indexing past lexsort depth may impact performance.
      self.interact()
    
             col
    one two     
    c   u      9
    

    this timing test I once did 支持的docs 似乎表明处理未排序的索引会导致速度变慢 - 索引是 O(N) 时间,而它可能/应该是 O(1)。

    如果你在切片之前对索引进行排序,你会注意到不同之处:

    df2 = df.sort_index()
    df2.loc[pd.IndexSlice[('c', 'u')]]
    
             col
    one two     
    c   u      9
    
    
    %timeit df.loc[pd.IndexSlice[('c', 'u')]]
    %timeit df2.loc[pd.IndexSlice[('c', 'u')]]
    
    802 µs ± 12.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    648 µs ± 20.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    最后,如果您想知道索引是否已排序,请检查MultiIndex.is_lexsorted

    df.index.is_lexsorted()
    # False
    
    df2.index.is_lexsorted()
    # True
    

    至于您关于如何引发这种行为的问题,只需排列索引就足够了。如果您的索引是唯一的,则此方法有效:

    df2 = df.loc[pd.MultiIndex.from_tuples(np.random.permutation(df2.index))]
    

    如果您的索引不是唯一的,请先添加cumcounted 级别,

    df.set_index(
        df.groupby(level=list(range(len(df.index.levels)))).cumcount(), append=True) 
    df2 = df.loc[pd.MultiIndex.from_tuples(np.random.permutation(df2.index))]
    df2 = df2.reset_index(level=-1, drop=True)
    

    【讨论】:

    • 精湛的答案(一如既往!)唯一我仍然不太确定的是在什么条件下locing 一行会返回一个系列而不是一个 df (这是最初导致我对此)。使用我的数据,loc 返回一个系列,但始终使用您的示例返回一个 df。
    • @JoshFriedlander 啊哈,我想通了。问题是df 有重复的索引条目!试试这个:df[~df.index.duplicated()].loc[('c', 'u')]。希望能解释一下。如果索引包含重复条目,则默认行为是返回一个 DataFrame,其中所有行都与该索引值匹配。如果只有一行匹配,结果将是一个单行的DataFrame。
    • 是的,这似乎是正确的!绝对值得赏金:)
    • @JoshFriedlander 谢谢!不过,小事一桩,最好等到赏金期结束后 6 天再授予它。在那个时候,问题和答案通常都会被很多人看到并投赞成票。 ;)
    • 我在使用多索引 ~30M 行 df 时表现不佳。索引排序后,性能提升非常显着。谢谢!
    猜你喜欢
    • 2019-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-25
    • 1970-01-01
    • 2014-04-24
    • 2021-10-30
    相关资源
    最近更新 更多