Python 在分层数据帧上添加带有groupby的列
我有一个如下结构的数据帧:Python 在分层数据帧上添加带有groupby的列,python,group-by,pandas,Python,Group By,Pandas,我有一个如下结构的数据帧: 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
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中已经存在“new”列,那么使用相同的函数进行转换也可以,但是如何在特定级别“动态”或分组之前添加新列
生成示例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)
这是可行的,但是为每个组创建一个新的数据帧并“手动”分配级别是一个非常糟糕的做法
那么正确的方法是什么呢?我找到了几个关于类似问题的帖子,但所有这些帖子都只有一个级别的专栏,这正是我正在努力解决的问题。这里的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
基于分组值创建一个新列是transform的任务,但我不知道transform是否可以输出多个列。我会像你一样处理这个问题。顺便说一句,transform还为每个组创建了一个新的框架,并在最后将它们全部合并。应用/转换机制能够输出结构化值和广播到列中的值(也就是说,如果一个元组是由应用函数生成的,那么组件将在单独的列中,而不是元组成为单个列中的一个原子元素)这将是一个奇妙的特性,即使它只是语法上的糖分。为了明确目的,可能使用另一个方法名称(applyWork或类似的东西,或者apply中的关键字splitseq=True)。谢谢Wes,这样更好。