问题描述
我有一个带有多索引的数据透视表,其中一层是“字段”(数据实际是什么),一层在概念上是实际的索引。我想对数据进行一些转换,例如取自然对数,并将转换后的值添加为一组行。例如:
col 1 2 3 4 ... 100
field idx
foo A 7 9 2 5 ... 47
B 8 3 4 8 ... 82
C 3 6 1 9 ... 23
bar A 1 17 5 2 ... 32
B 7 5 4 8 ... 78
C 28 6 11 3 ... 11
应成为:
col 1 2 3 4 ... 100
field idx
foo A 7 9 2 5 ... 47
B 8 3 4 8 ... 82
C 3 6 1 9 ... 23
bar A 1 17 5 2 ... 32
B 7 5 4 8 ... 78
C 28 6 11 3 ... 11
lnfoo A <element-wise>
B <natural logs>
C <of foo>
是否有一种简单的方法可以在像这样的多索引数据透视表上执行这种数组范围的操作?特别是不需要遍历数据帧的行吗?
对Pandas来说还很陌生,很抱歉,这是一个愚蠢的问题!
解决方法
要将日志应用于完整的foo索引级别,可以使用loc
一次选择所有行,然后使用np.log
进行示例操作。
#example data
np.random.seed(10)
df = pd.DataFrame(np.random.randint(1,50,30).reshape(6,-1),index=pd.MultiIndex.from_product([['foo','bar'],list("ABC")],names=['field','idx']))
print(np.log(df.loc['foo']))
0 1 2 3 4
idx
A 2.302585 3.610918 2.772589 0.000000 3.367296
B 3.258097 3.401197 3.891820 3.401197 2.197225
C 2.302585 0.000000 3.761200 3.713572 3.610918
如您所见,级别字段不在结果中,您需要使用pd.concat
和字典以及要创建的级别的名称来重新创建此级别。然后再次使用pd.concat
将其添加到df的末尾。
df = pd.concat([df,pd.concat({'logfoo': np.log(df.loc['foo'])},names=['field'])
])
print(df)
0 1 2 3 4
field idx
foo A 10.000000 37.000000 16.000000 1.000000 29.000000
B 26.000000 30.000000 49.000000 30.000000 9.000000
C 10.000000 1.000000 43.000000 41.000000 37.000000
bar A 17.000000 37.000000 48.000000 12.000000 25.000000
B 44.000000 34.000000 9.000000 37.000000 15.000000
C 14.000000 6.000000 14.000000 26.000000 14.000000
logfoo A 2.302585 3.610918 2.772589 0.000000 3.367296
B 3.258097 3.401197 3.891820 3.401197 2.197225
C 2.302585 0.000000 3.761200 3.713572 3.610918
,
另一种解决方案:
tmp = df.query('field == "foo"').rename(index={'foo': 'lnfoo'})
pd.concat([df,np.log(tmp)])
您也可以轻松地将其扩展到bar
:
tmp = df.query('field in ("foo","bar")').rename(index={'foo': 'lnfoo','bar': 'lnbar'})
pd.concat([df,np.log(tmp)])