【问题标题】:Python/Pandas - TypeError when concatenating MultiIndex DataFramesPython/Pandas - 连接多索引数据帧时出现类型错误
【发布时间】:2020-08-20 16:30:30
【问题描述】:

我无法连接具有 2 个级别的 MultiIndex DataFrames 列表,并添加第三个以区分它们。

例如,我有以下输入数据。

import pandas as pd
import numpy as np

# Input data

start = '2020-01-01 00:00+00:00'
end = '2020-01-01 02:00+00:00'
pr1h = pd.period_range(start=start, end=end, freq='1h')

midx1 = pd.MultiIndex.from_tuples([('Sup',1),('Sup',2),('Inf',1),('Inf',2)], names=['Data','Position'])
df1 = pd.DataFrame(np.random.rand(3,4), index=pr1h, columns=midx1)
df3 = pd.DataFrame(np.random.rand(3,4), index=pr1h, columns=midx1)

midx2 = pd.MultiIndex.from_tuples([('Sup',3),('Inf',3)], names=['Data','Position'])
df2 = pd.DataFrame(np.random.rand(3,2), index=pr1h, columns=midx2)
df4 = pd.DataFrame(np.random.rand(3,2), index=pr1h, columns=midx2)

所以 df1 和 df2 具有相同标签 1h 的数据,虽然它们在 Data 级别具有相同的列名,但它们在 Position 级别没有相同的列名。

df1
Data                   Sup                 Inf          
Position                 1         2         1         2
2020-01-01 00:00  0.660795  0.538452  0.861801  0.502479
2020-01-01 01:00  0.205806  0.847124  0.474861  0.906546
2020-01-01 02:00  0.681480  0.479512  0.631771  0.961844

df2
Data                   Sup       Inf
Position                 3         3
2020-01-01 00:00  0.758533  0.672899
2020-01-01 01:00  0.096463  0.304843
2020-01-01 02:00  0.080504  0.990310

现在,df3 和 df4 遵循相同的逻辑和相同的列名。为了将它们与 df1 和 df2 区分开来,我想使用不同的标签,例如 2h

我想在调用pd.concat 期间添加名为Period 的第三层。为此,我尝试在pd.concat() 中使用keys 参数。我尝试了以下代码。

df_list = [df1, df2, df3, df4]
period_list = ['1h', '1h', '2h', '2h']
concatenated = pd.concat(df_list, keys=period_list, names=('Period', 'Data', 'Position'), axis=1)

但这会引发以下错误。

TypeError: int() argument must be a string, a bytes-like object or a number, not 'slice'

请问,你知道这个的正确调用是什么吗?

感谢您的帮助。最好的,

编辑 05/05

根据要求,这是所需的结果(直接从给定的答案复制。从给定答案获得的结果是我正在寻找的结果)。

Period                  1h                                                    \
Data                   Sup                 Inf                 Sup       Inf   
Position                 1         2         1         2         3         3   
2020-01-01 00:00  0.309778  0.597582  0.872392  0.983021  0.659965  0.214953   
2020-01-01 01:00  0.467403  0.875744  0.296069  0.131291  0.203047  0.382865   
2020-01-01 02:00  0.842818  0.659036  0.595440  0.436354  0.224873  0.114649   

Period                  2h                                                    
Data                   Sup                 Inf                 Sup       Inf  
Position                 1         2         1         2         3         3  
2020-01-01 00:00  0.356250  0.587131  0.149471  0.171239  0.583017  0.232641  
2020-01-01 01:00  0.397165  0.637952  0.372520  0.002407  0.556518  0.523811  
2020-01-01 02:00  0.548816  0.126972  0.079793  0.235039  0.350958  0.705332

【问题讨论】:

  • 问题不在于你一开始有多重索引,更多的是你在period_list 中有两倍相同的值。如果你一开始没有多索引,那么错误会更明确地说明问题:InvalidIndexError: Reindexing only valid with uniquely valued Index objects
  • 设置period_list = ['1h', '2h', '3h', '4h'] 有效。否则,请发布想要的结果。
  • @Parfait 嗨,我按要求添加了预期结果。 df1df2 必须共享相同的 Period,并且 df3df4 也必须共享相同的 Period
  • 其实和github上的这个open issue差不多
  • 谢谢,我已经订阅了这个问题。如果解决了,我会修改你提出的代码。再次感谢!

标签: python pandas concatenation multi-index


【解决方案1】:

一个快速的解决方法是在period_listrename 中紧跟concat 之后使用不同的名称。比如:

df_list = [df1, df2, df3, df4]
period_list = ['1h_a', '1h_b', '2h_a', '2h_b']
concatenated = pd.concat(df_list, 
                         keys=period_list, 
                         names=('Period', 'Data', 'Position'), 
                         axis=1)\
                 .rename(columns={col:col.split('_')[0] for col  in period_list}, 
                         level='Period')

print (concatenated)
Period                  1h                                                    \
Data                   Sup                 Inf                 Sup       Inf   
Position                 1         2         1         2         3         3   
2020-01-01 00:00  0.309778  0.597582  0.872392  0.983021  0.659965  0.214953   
2020-01-01 01:00  0.467403  0.875744  0.296069  0.131291  0.203047  0.382865   
2020-01-01 02:00  0.842818  0.659036  0.595440  0.436354  0.224873  0.114649   

Period                  2h                                                    
Data                   Sup                 Inf                 Sup       Inf  
Position                 1         2         1         2         3         3  
2020-01-01 00:00  0.356250  0.587131  0.149471  0.171239  0.583017  0.232641  
2020-01-01 01:00  0.397165  0.637952  0.372520  0.002407  0.556518  0.523811  
2020-01-01 02:00  0.548816  0.126972  0.079793  0.235039  0.350958  0.705332 

编辑:由于速度是一个问题,重命名似乎很慢,所以你可以这样做:

concatenated = pd.concat(df_list, 
                         keys=period_list,
                         axis=1)
concatenated.columns = pd.MultiIndex.from_tuples([(col[0].split('_')[0], col[1], col[2]) 
                                                  for col in concatenated.columns], 
                                                  names=('Period', 'Data', 'Position'), )

【讨论】:

  • 谢谢@Ben.T。我很惊讶,没有“直接”的方式来获得正确的标签吗?您的修复很好,但仍然增加了一些复杂性。非常感谢你带来的东西。
  • @pierre_j 我不知道为什么这是不可能的,冻糕的想法是另一种方式,但因为即使使用非多索引也不可能让我认为不会有直接的方式
  • 感谢您的回复。由于您似乎是熊猫方面的专家,所以我想提一下,我在速度方面比较了这两种方法,@Parfait 的方法似乎快了 30%。我可以在几个地方读到累积使用pd.concat 会导致性能问题。与“简单”rename() 相比,我当然没想到会有这个结果。请问,您知道导致这种结果的原因可能是什么(pd.concatrename() 快)?
  • @pierre_j 我现在知道rename 不是特别快,但比内部concat 慢,有点令人惊讶。我刚刚进行了测试,并且在 4 年 1 小时的时间间隔内具有类似的性能。如果速度是一个问题,请参阅我的编辑,您可以调用 MultiIndex.from_tuples,这对我来说更快
  • 非常感谢您一直以来的支持!你帮了大忙!
【解决方案2】:

考虑类似数据帧的内部concat,然后运行最终的concat 将所有数据绑定在一起:

concatenated = pd.concat([pd.concat([df1, df2], axis=1),
                          pd.concat([df3, df4], axis=1)],
                         keys = ['1h', '2h'],
                         names=('Period', 'Data', 'Position'),
                         axis=1)

print(concatenated)  

Period                  1h                                                    \
Data                   Sup                 Inf                 Sup       Inf   
Position                 1         2         1         2         3         3   
2020-01-01 00:00  0.189802  0.675083  0.624484  0.781774  0.453101  0.224525   
2020-01-01 01:00  0.249818  0.829180  0.190488  0.923107  0.495873  0.278201   
2020-01-01 02:00  0.602634  0.494915  0.612672  0.903609  0.426809  0.248981   

Period                  2h                                                    
Data                   Sup                 Inf                 Sup       Inf  
Position                 1         2         1         2         3         3  
2020-01-01 00:00  0.746499  0.385714  0.008561  0.961152  0.988231  0.897454  
2020-01-01 01:00  0.643730  0.365023  0.812249  0.291733  0.045417  0.414968  
2020-01-01 02:00  0.887567  0.680102  0.978388  0.018501  0.695866  0.679730

【讨论】:

  • @Parfait。谢谢,但我实际上是在追逐pd.concat,由于性能优化,我试图尽可能少地保留它们。我希望 rename() 比 2 concat() 快,对吧?我给出的数据只显示了 4 个 DataFrame。但我在循环中生成它们,可能有数百个,还有更多的数据。不过还是谢谢!
  • 哇,我错过了什么吗?您的解决方案实际上比 rename() 快 33%。这是值得期待的吗? (我使用这 4 个 DataFrame,但在 4 年内增加了它们的长度......)
  • 很高兴为您提供帮助。不知道为什么你会看到时间差异。此处的解决方案可以集成在循环中,甚至可以集成到为concat 构建数据框列表的列表理解中。
猜你喜欢
  • 1970-01-01
  • 2017-03-05
  • 1970-01-01
  • 1970-01-01
  • 2019-10-19
  • 2023-03-25
  • 2023-01-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多