我的数据框结构如下:
First A B
Second bar baz foo bar baz foo
Third cat dog cat dog cat dog cat dog cat dog cat dog
0 3 8 7 7 4 7 5 3 2 2 6 2
1 8 6 5 7 8 7 1 8 6 0 3 9
2 9 2 2 9 7 3 1 8 4 1 0 8
3 3 6 0 6 3 2 2 6 2 4 6 9
4 7 6 4 3 1 5 0 4 8 4 8 1
所以有三个列级别。我想在第二级添加一个新列,对于每个第三级执行计算,例如'new'='foo'+'bar'。因此结果数据框看起来像:
First A B
Second bar baz foo new bar baz foo new
Third cat dog cat dog cat dog cat dog cat dog cat dog cat dog cat dog
0 3 8 7 7 4 7 7 15 5 3 2 2 6 2 11 5
1 8 6 5 7 8 7 16 13 1 8 6 0 3 9 4 17
2 9 2 2 9 7 3 16 5 1 8 4 1 0 8 1 16
3 3 6 0 6 3 2 6 8 2 6 2 4 6 9 8 15
4 7 6 4 3 1 5 8 11 0 4 8 4 8 1 8 5
我找到了一个在本文末尾列出的解决方法,但它根本不是“熊猫式”并且容易出错。组上的应用或转换功能似乎是正确的方法,但经过几个小时的尝试后,我仍然没有成功。我认为正确的方法应该是这样的:
def func(data):
fi = data.columns[0][0]
th = data.columns[0][2]
data[(fi,'new',th)] = data[(fi,'foo',th)] + data[(fi,'bar',th)]
print data
return data
print grouped.apply(func)
新列已在函数中正确添加,但未返回。如果df中已存在“新”列,则使用与转换相同的函数将起作用,但是如何“动态”或在分组之前在特定级别添加新列?
生成样本df的代码是:
import pandas, itertools
first = ['A','B']
second = ['foo','bar','baz']
third = ['dog', 'cat']
tuples = []
for tup in itertools.product(first, second, third):
tuples.append(tup)
columns = pandas.MultiIndex.from_tuples(tuples, names=['First','Second','Third'])
data = np.random.randint(0,10,(5, 12))
df = pandas.DataFrame(data, columns=columns)
我的解决方法:
dfnew = None
grouped = df.groupby(by=None, level=[0,2], axis=1)
for name, group in grouped:
newparam = group.xs('foo', axis=1, level=1) + group.xs('bar', axis=1, level=1)
dftmp = group.join(pandas.DataFrame(np.array(newparam), columns=pandas.MultiIndex.from_tuples([(group.columns[0][0], 'new', group.columns[0][2])], names=['First','Second', 'Third'])))
if dfnew is None:
dfnew = dftmp
else:
dfnew = pandas.concat([dfnew, dftmp], axis=1)
print dfnew.sort_index(axis=1)
可以使用,但为每个组创建一个新的数据框并“手动”分配级别是一种非常糟糕的做法。
那么这样做的正确方法是什么?我找到了几个处理类似问题的帖子,但所有这些帖子只有1个级别的列,这正是我所挣扎的。
答案 0 :(得分:7)
这里的API肯定有一个弱点,但我不确定是否能让你更容易做你正在做的事情。这是一个简单的方法,至少对你的例子来说是这样的:
In [20]: df
Out[20]:
First A B
Second foo bar baz foo bar baz
Third dog cat dog cat dog cat dog cat dog cat dog cat
0 7 2 9 3 3 0 5 9 8 2 0 6
1 1 4 1 7 2 3 2 3 1 0 4 0
2 6 5 0 6 6 1 5 1 7 4 3 6
3 4 8 1 9 0 3 9 2 3 1 5 9
4 6 1 1 5 1 2 2 6 3 7 2 1
In [21]: rdf = df.stack(['First', 'Third'])
In [22]: rdf['new'] = rdf.foo + rdf.bar
In [23]: rdf
Out[23]:
Second bar baz foo new
First Third
0 A cat 3 0 2 5
dog 9 3 7 16
B cat 2 6 9 11
dog 8 0 5 13
1 A cat 7 3 4 11
dog 1 2 1 2
B cat 0 0 3 3
dog 1 4 2 3
2 A cat 6 1 5 11
dog 0 6 6 6
B cat 4 6 1 5
dog 7 3 5 12
3 A cat 9 3 8 17
dog 1 0 4 5
B cat 1 9 2 3
dog 3 5 9 12
4 A cat 5 2 1 6
dog 1 1 6 7
B cat 7 1 6 13
dog 3 2 2 5
In [24]: rdf.unstack(['First', 'Third'])
Out[24]:
Second bar baz foo new
First A B A B A B A B
Third cat dog cat dog cat dog cat dog cat dog cat dog cat dog cat dog
0 3 9 2 8 0 3 6 0 2 7 9 5 5 16 11 13
1 7 1 0 1 3 2 0 4 4 1 3 2 11 2 3 3
2 6 0 4 7 1 6 6 3 5 6 1 5 11 6 5 12
3 9 1 1 3 3 0 9 5 8 4 2 9 17 5 3 12
4 5 1 7 3 2 1 1 2 1 6 6 2 6 7 13 5
你当然可以重新调整内心的内容:
In [28]: rdf.unstack(['First', 'Third']).reorder_levels(['First', 'Second', 'Third'], axis=1).sortlevel(0, axis=1)
Out[28]:
First A B
Second bar baz foo new bar baz foo new
Third cat dog cat dog cat dog cat dog cat dog cat dog cat dog cat dog
0 3 9 0 3 2 7 5 16 2 8 6 0 9 5 11 13
1 7 1 3 2 4 1 11 2 0 1 0 4 3 2 3 3
2 6 0 1 6 5 6 11 6 4 7 6 3 1 5 5 12
3 9 1 3 0 8 4 17 5 1 3 9 5 2 9 3 12
4 5 1 2 1 1 6 6 7 7 3 1 2 6 2 13 5