我正在使用 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
所以我的问题是:是什么原因导致此警告?如何人工诱导?
最佳答案
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
docs , 支持 this timing test I once did似乎表明处理未排序的索引会导致速度下降——索引是 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))]
如果你的索引不是唯一的,先添加一个cumcount
ed级别,
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)
关于python - 是什么导致 Pandas 出现 "indexing past lexsort depth"警告?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54307300/