【问题标题】:Adding level to middle of DF in Pandas在 Pandas 中向 DF 中间添加级别
【发布时间】:2017-03-03 19:48:35
【问题描述】:

我想为我的 DF 添加一个新关卡(这样我就可以使用pd.reindex 来做其他事情)。我的 DF 基本上是这样的:

df = pd.DataFrame({('A','a'): [-1,-1,0,10,12],
                   ('A','b'): [0,1,2,3,-1],
                   ('A','c'): [-1,1,0,10,12],
                   ('A','d'): [1,1,2,3,-1],
                   ('B','a'): [-20,-10,0,10,20],
                   ('B','b'): [-200,-100,0,-1,200],
                   ('B','c'): [-20,-10,0,10,20],
                   ('B','d'): [-200,-100,0,100,200]
})

##df
    A               B
    a   b   c   d   a   b     c     d
0   -1  0   1   1   -20 -200  -20   -200
1   -1  1   -1  1   -10 -100  -10   -100
2   0   2   0   2   0   0     0     0
3   10  3   10  3   10  -1    10    100
4   12  -1  12  -1  20  200   20    200

我想分配新的级别键L1 = a+bL2=c+d。我该怎么做?

期望的输出是

##df
    A               B
    L1      L2      L1        L2
    a   b   c   d   a   b     c     d
0   -1  0   1   1   -20 -200  -20   -200
1   -1  1   -1  1   -10 -100  -10   -100
2   0   2   0   2   0   0     0     0
3   10  3   10  3   10  -1    10    100
4   12  -1  12  -1  20  200   20    200

编辑:目标是实现类似于here 中所要求的目标。这意味着某些行将具有相同 KEY 的 NAs,具体取决于其他列的值。 例如。如果我想通过分别测试列 bd 是否为负来过滤列 ac

##df
    A               B
    L1      L2      L1        L2
    a   b   c   d   a   b     c     d
0   -1  0   1   1   NA  NA    NA    NA
1   -1  1   -1  1   NA  NA    NA    NA
2   0   2   0   2   0   0     0     0
3   10  3   10  3   NA  NA    10    100
4   NA  NA  NA  NA  20  200   20    200

【问题讨论】:

  • “这样我就可以使用 pd.reindex 做其他事情了”:如果您说明您的最终目的,也许我们可以更好地帮助您。
  • 嗯,最终目的与我在这里(stackoverflow.com/questions/42246373/…)提出的非常相似。现在的不同之处在于我必须解析列ab(就像我在上一篇文章中所做的那样),cd 也是如此。
  • 我将更改工作示例以反映此评论
  • @Wli 我添加了一些信息,希望对我有所帮助:)
  • 看来还是有人能回答的:)

标签: python pandas


【解决方案1】:

您需要使用map 创建新的array,然后分配:

d = {'a':'L1','b':'L1','c':'L2','d':'L2'}
a = df.columns.get_level_values(1).map(lambda x: d[x])
print (a)
['L1' 'L1' 'L2' 'L2' 'L1' 'L1' 'L2' 'L2']

df.columns = [df.columns.get_level_values(0),a,df.columns.get_level_values(1)]
#same as
df.columns = pd.MultiIndex.from_arrays([df.columns.get_level_values(0),
                                        df.columns.get_level_values(1).map(lambda x: d[x]),
                                        df.columns.get_level_values(1)])
print (df)
    A             B              
   L1     L2     L1       L2     
    a  b   c  d   a    b   c    d
0  -1  0  -1  1 -20 -200 -20 -200
1  -1  1   1  1 -10 -100 -10 -100
2   0  2   0  2   0    0   0    0
3  10  3  10  3  10   -1  10  100
4  12 -1  12 -1  20  200  20  200

第二个输出真的很复杂,对我来说有效:

#filter columns
idx = pd.IndexSlice
mask = df.loc[:, idx[:,:,['b','d']]] < 0
print (mask)
       A             B       
      L1     L2     L1     L2
       b      d      b      d
0  False  False   True   True
1  False  False   True   True
2  False  False  False  False
3  False  False   True  False
4   True   True  False  False

#create mask to columns a,c
mask1 = mask.reindex(columns=df.columns)
mask1 = mask1.groupby(level=[0,1], axis=1).apply(lambda x: x.bfill(axis=1))
print (mask1)
       A                           B                     
      L1            L2            L1            L2       
       a      b      c      d      a      b      c      d
0  False  False  False  False   True   True   True   True
1  False  False  False  False   True   True   True   True
2  False  False  False  False  False  False  False  False
3  False  False  False  False   True   True  False  False
4   True   True   True   True  False  False  False  False

print (df.mask(mask1))
      A                     B                    
     L1         L2         L1           L2       
      a    b     c    d     a      b     c      d
0  -1.0  0.0  -1.0  1.0   NaN    NaN   NaN    NaN
1  -1.0  1.0   1.0  1.0   NaN    NaN   NaN    NaN
2   0.0  2.0   0.0  2.0   0.0    0.0   0.0    0.0
3  10.0  3.0  10.0  3.0   NaN    NaN  10.0  100.0
4   NaN  NaN   NaN  NaN  20.0  200.0  20.0  200.0

另一个使用reindexmethod='bfill' 的解决方案,但必须进行双重转置(我认为这是错误-仅适用于index 中的MultiIndex,不适用于columns 中的MultiIndex):

idx = pd.IndexSlice
mask = df.loc[:, idx[:,['b','d']]] < 0
print (mask)
       A             B       
       b      d      b      d
0  False  False   True   True
1  False  False   True   True
2  False  False  False  False
3  False  False   True  False
4   True   True  False  False

mask1 = mask.T.reindex(df.columns, method='bfill').T
print (mask1)
       A                           B                     
       a      b      c      d      a      b      c      d
0  False  False  False  False   True   True   True   True
1  False  False  False  False   True   True   True   True
2  False  False  False  False  False  False  False  False
3  False  False  False  False   True   True  False  False
4   True   True   True   True  False  False  False  False

print (df.mask(mask1))
      A                     B                    
      a    b     c    d     a      b     c      d
0  -1.0  0.0  -1.0  1.0   NaN    NaN   NaN    NaN
1  -1.0  1.0   1.0  1.0   NaN    NaN   NaN    NaN
2   0.0  2.0   0.0  2.0   0.0    0.0   0.0    0.0
3  10.0  3.0  10.0  3.0   NaN    NaN  10.0  100.0
4   NaN  NaN   NaN  NaN  20.0  200.0  20.0  200.0

【讨论】:

  • 设置mask = df.loc[:, idx[:,:,['b','d']]] &lt; 0时出现以下错误:KeyError: 'MultiIndex Slicing requires the index to be fully lexsorted tuple len (3), lexsort depth (2)'。将您的mask 更改为mask = df.loc[:, idx[:,['b','d']]] &lt; 0 可以解决您的工作示例的问题并产生正确的print。但是,在我的真实示例中,我也收到了类似的错误the index to be fully lexsorted tuple len (3), lexsort depth (1)',我无法解决它并获得正确的打印
  • 我想你首先需要df = df.sort_index(axis=1),检查docs
  • 非常感谢@jezrael,我从这个和你之前对我的帖子的回答中学到了很多东西!我真的很感激!
  • MultiIndexes 真的很难,这个问题不太好,因为有一个 hack - groupbybfill,但我不知道如何做得更好。
猜你喜欢
  • 1970-01-01
  • 2023-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-16
  • 2015-09-02
  • 1970-01-01
  • 2022-11-04
相关资源
最近更新 更多