Python 熊猫:修改多索引的特定级别
我有一个带有多索引的数据框架,并希望修改多索引的一个特定级别。例如,第一个级别可能是字符串,我可能希望删除该索引级别中的空格:Python 熊猫:修改多索引的特定级别,python,pandas,immutability,multi-index,Python,Pandas,Immutability,Multi Index,我有一个带有多索引的数据框架,并希望修改多索引的一个特定级别。例如,第一个级别可能是字符串,我可能希望删除该索引级别中的空格: df.index.levels[1] = [x.replace(' ', '') for x in df.index.levels[1]] 但是,上面的代码会导致错误: TypeError: 'FrozenList' does not support mutable operations. 我知道我可以重置索引并修改列,然后重新创建多索引,但我想知道是否有更优雅的方
df.index.levels[1] = [x.replace(' ', '') for x in df.index.levels[1]]
但是,上面的代码会导致错误:
TypeError: 'FrozenList' does not support mutable operations.
我知道我可以重置索引并修改列,然后重新创建多索引,但我想知道是否有更优雅的方法可以直接修改多索引的一个特定级别。如注释中所述,索引是不可变的,必须在修改时重新生成,但您不必为此使用
重置索引,您可以直接创建新的多索引:
df.index = pd.MultiIndex.from_tuples([(x[0], x[1].replace(' ', ''), x[2]) for x in df.index])
此示例适用于3级索引,您希望在其中修改中间级别。您需要更改不同级别大小的元组的大小
更新
John的改进在性能方面是非常好的,但正如评论中提到的,它会导致错误。下面是经过小改进的正确实现:
df.index.set_levels(
df.index.levels[0].str.replace(' ',''),
level=0,
inplace=True, # If False, you will need to use `df.index = ...`
)
如果希望使用级别名称而不是数字,则需要另一个小的变体:
df.index.set_levels(
df.index.levels[df.index.names.index('level_name')].str.replace(' ',''),
level='level_name',
inplace=True,
)
感谢@cxrogers的评论,我认为最快的方法是:
df.index = df.index.set_levels(df.index.levels[0].str.replace(' ', ''), level=0)
老生常谈的回答:
我发现@Shovalt建议的列表理解可以工作,但在我的机器上感觉很慢(使用>10000行的数据帧)
相反,我能够使用.set_levels
方法,这对我来说要快得多
%timeit pd.MultiIndex.from_tuples([(x[0].replace(' ',''), x[1]) for x in df.index])
1 loop, best of 3: 394 ms per loop
%timeit df.index.set_levels(df.index.get_level_values(0).str.replace(' ',''), level=0)
10 loops, best of 3: 134 ms per loop
实际上,我只需要准备一些文本。使用时速度更快。设置\u级别
:
%timeit pd.MultiIndex.from_tuples([('00'+x[0], x[1]) for x in df.index])
100 loops, best of 3: 5.18 ms per loop
%timeit df.index.set_levels('00'+df.index.get_level_values(0), level=0)
1000 loops, best of 3: 1.38 ms per loop
%timeit df.index.set_levels('00'+df.index.levels[0], level=0)
1000 loops, best of 3: 331 µs per loop
此解决方案基于@denfromufa评论中链接中的答案
其他答案都很有效。根据多索引的结构,直接在级别上应用贴图而不是构建新的多索引可能会快得多
我使用以下函数修改特定的索引级别。它也适用于单级指数
def映射索引级别(索引,映射器,级别=0):
"""
返回新索引或多索引,并映射级别值。
"""
断言(isinstance(索引,局部索引))
如果存在(索引,pd.多索引):
new_level=index.levels[level].map(映射器)
新建索引=索引。设置索引级别(新建索引级别,级别=级别)
其他:
#单级索引。
断言(级别==0)
新建索引=index.map(映射器)
返回新的索引
用法:
df=pd.DataFrame([[1,2],[3,4])
df.index=pd.MultiIndex.from_乘积([[“a”],[“i”,“ii”])
df.columns=[“x”,“y”]
df.index=map\u index\u level(index=df.index,mapper=str.upper,level=1)
df.columns=map\u index\u level(index=df.columns,mapper={“x”:“foo”,“y”:“bar”})
#结果:
#富吧
#a I 1 2
#II 3 4
注意:仅当mapper
保留级别值的唯一性时,上述操作才有效!在上面的示例中,mapper={“i”:“new”,“ii”:“new”}
将在set_index()
中失败,并带有ValueError:级别值必须唯一
。可以禁用完整性检查,修改上述代码以:
new_index = index.set_levels(new_level, level=level,
verify_integrity=False)
但最好不要!请参阅的文档。否,索引是不可变的。如果你想改变它,你必须重新制作它。这似乎比构建一个新的索引更快更优雅。我还想补充一点,在大多数情况下,您只需执行inplace=True
。实际上,我认为您的代码包含错误,应该是df.index.levels[0]
无论您在哪里df.index.get\u level\u值(0)
。这也是他们在回答“你的链接是”时所做的。获取\u级别\u值
对你不可用?你用的是哪种版本的熊猫?我使用的是v0.22.0,两个版本似乎都给出了相同的结果,但您的建议使用的是.levels[0]
比.get\u level\u values(0)
快得多。我将把它添加到我的答案中。get\u level\u values
与levels
的作用不同。。。。我不完全理解它,但第一个给出了每一行的该级别值,而levels
只给出了不同的级别值,或者类似的东西。@John+1但使用df.index.unqiue(level=0)
而不是df.index.levels[0]
或df.index.get\u level\u值(0)
。它更安全,专为这种情况而设计。尤其是对于get_level_值
,重复的级别条目可能会有冲突。