pandas中的惯用多索引列分配

时间:2014-06-16 15:32:23

标签: python pandas

我有一个带有2级Multiindex的数据框:

ix = pd.MultiIndex.from_tuples(list(enumerate(np.random.choice(['A', 'B'], 5))))
df = pd.DataFrame({'Val': np.random.randint(0, 30, 5)}, index=ix).unstack().fillna(0)
df
   Val    
     A   B
0   27   0
1    0   3
2    0   7
3    9   0
4    0  19

我想为每个现有的子级('A''B')添加一列,该列等于Val列的一半。我的直觉是做

df['Half_val'] = df.Val / 2

,提供ValueError: Wrong number of items passed 2, placement implies 1例外。

我可以手动执行

res = df.Val / 2
df.loc[:, ('Half_val', 'A')] = res.A
df.loc[:, ('Half_val', 'B')] = res.B

给出了我之后的内容:

>>> df
   Val      Half_val     
     A   B         A    B
0   27   0      13.5  0.0
1    0   3       0.0  1.5
2    0   7       0.0  3.5
3    9   0       4.5  0.0
4    0  19       0.0  9.5

是否有一种不那么详细,更惯用的方式来制作这样的多索引列分配(尤其是我不必在左侧明确指定每个子级的那个)?

编辑:

我忘了提到尝试

res = df.Val / 2
df.loc[:, res.columns] = res

提供KeyError: "['A' 'B'] not in index"例外。

编辑2 如果解决方案允许数据帧中的伪混合级别列,那将是很好的。在我的例子中,我可以做

In [5]: df['C'] = 'a'
In [6]: df
Out[6]:
   Val      C
     A   B
0    4   0  a
1    0  10  a
2    0   4  a
3   21   0  a
4    0  14  a

添加一个单一级别的列。但由于该列已经有2个级别,因此它似乎给出了一个隐含的第二级空字符串

In [9]: list(df)
Out[9]: [('Val', 'A'), ('Val', 'B'), ('C', '')]

当我尝试下面提供的解决方案时,单级C列似乎打破了它:

In [7]: pd.concat([df,df['Val']/2],axis=1,keys=['Val', 'C', 'Half'])
==> AssertionError: Cannot concat indices that do not have the same number of levels

keys参数是否有一些技巧可以传递,或者我是否需要为C提供不同的虚拟值以用于第二级(因为看起来""没有&#39 ; t count)然后在连接后将其删除?

2 个答案:

答案 0 :(得分:3)

您可以迭代级别值并进行直接分配(一次一个值)

In [55]: df.columns.get_level_values(1)
Out[55]: Index([u'A', u'B'], dtype='object')

In [51]: df[('Half','A')] = df[('Val','A')]/2

In [52]: df[('Half','B')] = df[('Val','B')]/2

In [53]: df
Out[53]: 
   Val      Half      
     A   B     A     B
0    0  12   0.0   6.0
1    0   5   0.0   2.5
2    0  26   0.0  13.0
3    3   0   1.5   0.0
4   25   0  12.5   0.0

你也可以这样做

In [59]: concat([df['Val'],df['Val']/2],axis=1,keys=['Val','Half'])
Out[59]: 
   Val      Half        
     A   B     A    B
0    0  10   0.0  5.0
1    0  10   0.0  5.0
2    0  13   0.0  6.5
3   27   0  13.5  0.0
4    2   0   1.0  0.0

此问题是跟踪此错误/增强功能的问题:https://github.com/pydata/pandas/issues/7475

答案 1 :(得分:1)

我认为此选项比concat选项更可取,因为您不必冒错误地重新标记'Val'列。如果你不同意,请纠正我!

根据您的输入数据框:

In [3]: df
Out[3]:
  Val
    A   B
0  26   0
1  10   0
2  18   0
3   0  18
4   2   0

值得考虑的第三个选择是:

In [4]: df[pd.MultiIndex.from_product([['Half']] + df.columns.levels[1:])] = df['Val'] / 2

In [5]: df
Out[5]:
  Val     Half
    A   B    A  B
0  26   0   13  0
1  10   0    5  0
2  18   0    9  0
3   0  18    0  9
4   2   0    1  0

这种方法也适用于任意嵌套的MultiIndex。 (我不知道是否可以使用MultiIndex的子列进行此分配。)​​

In [1]: df = pd.DataFrame({'Val': np.random.randint(5, 30, 12)}, index=pd.MultiIndex.from_product([['A', 'B','C'], ['a', 'b'], [0, 1]])).unstack().unstack()

In [2]: df
Out[2]:
  Val
    0       1
    a   b   a   b
A   6  10  11   7
B  16   8  23  15
C  29  17  11  18

In [3]: df[pd.MultiIndex.from_product([['Half']] + df.columns.levels[1:])] = df['Val'] / 2

In [4]: df
Out[4]:
  Val              Half
    0       1         0          1
    a   b   a   b     a    b     a    b
A   6  10  11   7   3.0  5.0   5.5  3.5
B  16   8  23  15   8.0  4.0  11.5  7.5
C  29  17  11  18  14.5  8.5   5.5  9.0