Python 更改多索引级别:“;ValueError:在级别0上,代码最大值>;=“标高长度”;
我很难更改一些代码,这些代码将Python 更改多索引级别:“;ValueError:在级别0上,代码最大值>;=“标高长度”;,python,pandas,multi-index,Python,Pandas,Multi Index,我很难更改一些代码,这些代码将多索引的'date'部分调整为MonthEnd,如下所示(取决于'date'部分位于位置0): offset=pd.offset.MonthEnd() df.index.set_levels(df.index.levels[0]+偏移量,level=0,inplace=True) inplace参数在pandas=1.2.1中标记为已弃用(出于充分的理由,我完全赞成) 在重构代码时,我想我也希望使用命名级别('date'),而不是int级别(0),以便于可读性和可
多索引的'date'
部分调整为MonthEnd
,如下所示(取决于'date'
部分位于位置0):
offset=pd.offset.MonthEnd()
df.index.set_levels(df.index.levels[0]+偏移量,level=0,inplace=True)
inplace
参数在pandas=1.2.1
中标记为已弃用(出于充分的理由,我完全赞成)
在重构代码时,我想我也希望使用命名级别('date'
),而不是int
级别(0
),以便于可读性和可维护性。因此,我写道:
level='date'
mi=df.index
df=df.set\u索引(mi.set\u级别(mi.unique(级别)+偏移量,级别=级别)
这很好地工作了,直到它遇到了一个df
,它是另一个df的副本,带有多索引的子集
考虑以下设置作为一个简单的示例:
def get_example_df(notbefore=None):
np.random.seed(0)
n=3
日期=pd.日期范围('2000',频率=MS',周期=n)
名称=列表('ab')
df=pd.DataFrame(
np.random.randint(10,size=n*len(名称)),
列=['x'],
index=pd.MultiIndex.from_产品([日期,名称],
名称=('date','name'))
)
如果之前没有:
dates=df.index.get_level_值('date')
df=df.loc[日期>=notbefore]
返回df
级别='日期'
offset=pd.offset.MonthEnd()
没有截断,一切都很好:
df=get\u example\u df()
>>>df
x
日期名称
2000-01-01 a 5
b 0
2000-02-01甲3
b 3
2000-03-01 a 7
b 9
#注:
>>>测向索引代码[0]
数组([0,0,1,1,2,2],dtype=int8)
>>>mi=df.index
>>>df.set_索引(mi.set_级别(mi.unique(级别)+偏移,级别=级别))
x
日期名称
2000-01-31 a 5
b 0
2000-02-29 a 3
b 3
2000-03-31 a 7
b 9
但是,当多索引
是一个视图时(因为notbefore
不是None
),那么它就相当糟糕了:
df=get_example_df(notbefore='2000-02-15')
>>>df
x
日期名称
2000-03-01 a 7
b 9
>>>mi=df.index
>>>df.set_索引(mi.set_级别(mi.unique(级别)+偏移,级别=级别))
...
ValueError:在级别0上,代码max(2)>=级别(1)的长度。注意:此索引处于不一致状态
问题是当df
为截断值时,mi.codes[0]
不是从0开始的:
>>df.index.code[0]
数组([2,2],dtype=int8)
因此,我们面临的不幸情况是:
>len(df.index.levels[0])
3.
>>>len(df.index.get_level_值(level))
2.
>>>len(df.index.unique(级别))
1.
并且唯一可以分配回该级别的(在添加偏移量之后)是df.index.levels[0]
对于我的新代码,我能想到的唯一一件似乎可靠工作的事情是:
level_idx=df.index.names.index('date'))
#级别_idx现在为0
mi=df.index
mi=mi.set_levels(mi.levels[level_idx]+偏移量,level=level_idx)
现在:
>>mi
多索引([('2000-03-31','a'),
('2000-03-31','b'),
名称=[“日期”,“名称”])
>>>mi.代码[0]
数组([2,2],dtype=int8)#与前面一样
这感觉是错误的。最好有一个.set_unique()
,它将是.unique()
的强大对应物,即使.code
不是从0开始的。这也是低效的(例如,当.unique()
值比多索引的整个长度少得多时)
我遗漏了什么吗?根据我们的讨论,我认为您可能需要删除:
这在pandas版本中是新的:在0.20.0版本中是新的。
mi = df1.index.remove_unused_levels()
df1.set_index(mi.set_levels(mi.unique(level) + offset, level=level))
我相信您可能需要删除未使用的级别,但只需确认df1=get\u example\u df(notbefore='2000-02-15')
,mi=df1.index.remove\u unused\u levels()
然后df1.set\u index(mi.unique(level)+offset,level=level))
?太好了!如果你能把它作为一个答案发布,我就可以接受并升级了。这有多方便,你知道吗?(我的意思是,我们支持pandas早在pandas>=0.24
)它是从0.20.0受支持的。
:)
from pandas.tseries.offsets import MonthEnd
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
df=get_example_df()
endOfMonth=[pd.to_datetime(x[0].strftime("%Y-%m"))+MonthEnd(1) for x in df.index]
df.reset_index(inplace=True)
df['date']=endOfMonth
df=df.set_index(['date','name'])
print(df)
date name x
2000-01-31 a 5
b 0
2000-02-29 a 3
b 3
2000-03-31 a 7
b 9