是什么导致熊猫中的“索引超过 lexsort 深度”警告?

我正在使用 df.loc[(key1, key2)]索引一个大型多索引熊猫 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

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

39668 次浏览

根据 熊猫高级索引(多索引排序)

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

还有:

即使没有对数据进行排序,索引仍然可以工作,但是效率相当低。它还将返回数据的副本,而不是视图:

根据它们,您可能需要确保索引被正确排序。

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

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


实际上,我已经在我的文章 在熊猫 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

我曾经做过的计时测试支持的 医生似乎表明,处理未排序的索引会减慢速度ーー当索引可能/应该是 O (1)时,索引是 O (N)时间。

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

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)

系列与数据帧输出: 我也有同样的问题,有时 df.loc[(index1, index2)]的输出是一个系列,有时是一个数据帧。我发现这是由重复的索引引起的。如果数据帧有一些重复的索引,那么 df.loc[(index1, index2)]的输出就是一个数据帧,否则就是一个序列。