python - 更改多索引级别:“ValueError:在级别 0,代码最大值 >= 级别长度”
问题描述
我很难更改一些将 a 的'date'
部分调整MultiIndex
为这样的代码(依赖于该部分位于位置 0MonthEnd
的事实):'date'
offset = pd.offsets.MonthEnd()
df.index.set_levels(df.index.levels[0] + offset, level=0, inplace=True)
该inplace
论点被标记为已弃用pandas=1.2.1
(出于充分的理由,我完全赞成)。
在重构代码时,我想我还想使用命名级别 ( 'date'
),而不是int
级别 ( 0
),以便于阅读和维护。所以,我写道:
level = 'date'
mi = df.index
df = df.set_index(mi.set_level(mi.unique(level) + offset, level=level)
这工作得很好,直到它遇到一个df
是另一个副本的副本,其中包含MultiIndex
.
考虑以下设置作为一个最小示例:
def get_example_df(notbefore=None):
np.random.seed(0)
n = 3
dates = pd.date_range('2000', freq='MS', periods=n)
names = list('ab')
df = pd.DataFrame(
np.random.randint(10, size=n * len(names)),
columns=['x'],
index=pd.MultiIndex.from_product([dates, names],
names=('date', 'name'))
)
if notbefore:
dates = df.index.get_level_values('date')
df = df.loc[dates >= notbefore]
return df
level = 'date'
offset = pd.offsets.MonthEnd()
没有截断,一切都很好:
>>> df = get_example_df()
>>> df
x
date name
2000-01-01 a 5
b 0
2000-02-01 a 3
b 3
2000-03-01 a 7
b 9
# note:
>>> df.index.codes[0]
array([0, 0, 1, 1, 2, 2], dtype=int8)
>>> mi = df.index
>>> df.set_index(mi.set_levels(mi.unique(level) + offset, level=level))
x
date name
2000-01-31 a 5
b 0
2000-02-29 a 3
b 3
2000-03-31 a 7
b 9
但是,当MultiIndex
是一个视图(因为notbefore
不是None
)时,它会变得相当糟糕:
>>> df = get_example_df(notbefore='2000-02-15')
>>> df
x
date name
2000-03-01 a 7
b 9
>>> mi = df.index
>>> df.set_index(mi.set_levels(mi.unique(level) + offset, level=level))
...
ValueError: On level 0, code max (2) >= length of level (1). NOTE: this index is in an inconsistent state
事实证明,问题在于当被截断mi.codes[0]
时,它不会从 0 开始:df
>>> df.index.codes[0]
array([2, 2], dtype=int8)
所以我们遇到了不幸的情况:
>>> len(df.index.levels[0])
3
>>> len(df.index.get_level_values(level))
2
>>> len(df.index.unique(level))
1
并且唯一可以分配(在添加 后offset
)回到级别的是df.index.levels[0]
。
对于我的新代码,我唯一能想到的似乎可靠工作的是:
level_idx = df.index.names.index('date')
# level_idx is now 0
mi = df.index
mi = mi.set_levels(mi.levels[level_idx] + offset, level=level_idx)
现在:
>>> mi
MultiIndex([('2000-03-31', 'a'),
('2000-03-31', 'b')],
names=['date', 'name'])
>>> mi.codes[0]
array([2, 2], dtype=int8) # as before
那感觉不对。即使不是从 0 开始,最好有.set_unique()
一个与..unique()
.codes
.unique()
MultiIndex
我错过了什么吗?
解决方案
根据我们的讨论,我认为您可能想要删除unused levels
:
这是熊猫版本中的新功能:New in version 0.20.0.
mi = df1.index.remove_unused_levels()
df1.set_index(mi.set_levels(mi.unique(level) + offset, level=level))
推荐阅读
- java - 我在使用 Spring Boot 版本 2.2.0 的 Spring Batch 中遇到错误
- python - 在python中将长字符串写入没有换行符的文件
- swiftui - 如何在 ZStack 中正确使用 NavigationView?
- python - 反序列化浮点数时出错
- c++ - 使用 shrink_to_fit() 释放向量向量中的内存
- msbuild - 发布到文件系统 Visual Studio 2017 后运行自定义脚本
- java - 列出组合
- xamarin - 如何删除 iOS 上显示的标签栏上方的行
- swift - Swift:从字符串初始化对象
- python - 线性判别分析和二次判别分析 (LDA, QDA) 的 p 值