给定一个数据框
df
Value
Category Pool Class
A 1.0 1.0 1
9.0 2
B 1.0 1.0 3
C 1.0 1.0 4
5.0 5
我想将级别
Pool
和 Class
转换为没有 reset_index
的整数(见下文)。
我尝试使用
get_level_values
和set_levels
的组合,就像这样
for c in ['Pool', 'Class']:
df.index.set_levels(df.index.get_level_values(c).astype(int), level=c, inplace=True)
然而,这提高了
ValueError: Level values must be unique: [1, 1, 1, 1, 1] on level 1
为了了解会发生什么,我还尝试使用
verify_integrity=False
。然后
df.index.set_levels(df.index.get_level_values('Class').astype(int),
level='Class', verify_integrity=False, inplace=True)
产生
Value
Category Pool Class
A 1.0 1 1
1 2
B 1.0 1 3
C 1.0 1 4
9 5
而我的目标是获得
Value
Category Pool Class
A 1.0 1 1
9 2
B 1.0 1 3
C 1.0 1 4
5 5
如何正确实现这一目标?链接
get_level_values
和 set_levels
是正确的方法吗?为什么pandas
用astype
变换后无法正确设置级别?
我想你可以使用
reset_index
和 set_index
但是使用这些方法 set_levels
有什么好处?
d = {'Category': str, 'Pool': int, 'Class': int}
df.reset_index(drop=False, inplace=True)
for k, v in d.items():
df[k] = df[k].astype(v)
df.set_index(list(d.keys()), inplace=True)
您可以直接通过
pd.MultiIndex.levels
访问索引级别并提供给 pd.MultiIndex.set_levels
:
df.index = df.index.set_levels(df.index.levels[2].astype(int), level=2)
print(df)
Value
Category Pool Class
A 1.0 1 1
9 2
B 1.0 1 3
C 1.0 1 4
5 5
以下功能可以作为
get_level_values
的补充:
def set_level_values(midx, level, values):
full_levels = list(zip(*midx.values))
names = midx.names
if isinstance(level, str):
if level not in names:
raise ValueError(f'No level {level} in MultiIndex')
level = names.index(level)
if len(full_levels[level]) != len(values):
raise ValueError('Values must be of the same size as original level')
full_levels[level] = values
return pd.MultiIndex.from_arrays(full_levels, names=names)
使用此函数,原问题的解决方案将是:
for c in ['Pool', 'Class']:
df.index = set_level_values(df.index, level=c, values=df.index.get_level_values(c).astype(int))
要获取与变量
k
中存储的级别名称相对应的整数位置,您可以使用:
df.index.names.index(k)
因此,如果像 OP 一样,您有
dict
的关卡名称和类型,只需执行以下操作:
levels = [df.index.levels[df.index.names.index(k)].astype(v)
for k, v in d.items()]
df.index = df.index.set_levels(levels=levels, level=d.keys())
或者,方法链中的相同内容:
df.set_index(
df.index.set_levels(
[df.index.levels[df.index.names.index(k)].astype(v)
for k, v in d.items()],
level=d.keys())
)...
OP 的
DataFrame
和 dict
设置:
import pandas as pd
df = pd.DataFrame(
range(1, 6),
index=pd.MultiIndex.from_tuples(
[
('A', 1., 1.),
('A', 1., 9.),
('B', 1., 1.),
('C', 1., 1.),
('C', 1., 5.)
],
names=['Category', 'Pool', 'Class']
),
columns=['Value']
)
d = {'Category': str, 'Pool': int, 'Class': int}
我发现所有的答案要么不起作用,要么对我来说太混乱而无法实施。我确实找到了一个解决方案是绕过 set_index。而是完全形成 df.columns 外部的元组,将其称为 x。然后 df.columns=x 将替换与元组中任何不唯一元素的唯一性无关的列。我还没有测试如果两个元组相同会发生什么。代码片段如下所示: 代码片段未显示,因为我不知道如何格式化它。我认为这个想法是,虽然级别可能具有非唯一元素,但元组是唯一的 - 只是在这里猜测;但它确实有效。