我有一个带有分层列名的pandas DataFrame,比如这个
import pandas as pd
import numpy as np
np.random.seed(1542)
dates = pd.date_range('29/01/17', periods = 6)
pd.DataFrame(np.random.randn(6,6), index = dates,\
columns = [['g1', 'g1', 'g1', 'g2', 'g2', 'g2'],\
['c1', 'c2', 'c3', 'c1', 'c2', 'c3']])
我想要应用一个函数,对于第一级列中的每个组,它都会使用列' c2'和' c3'并返回单个值。
该功能的一个例子(在实际情况下更复杂)可以是
def function(first_column, second_column):
return(max(first_column) - max(second_column))
当我将它应用到我的DataFrame时,我想要一个DataFrame来告诉我' function'的输出。对于每个组,因此,在这种情况下,只有2个数字用于' g1'和' g2'。
请注意,我希望它也适用于gorupby(),以便在这种情况下,我得到每个组的函数结果(' g1'和' g2')以及每个groupby子集。
对于上述情况,如果我想按月汇总,结果应为:
g1 g2
1 0.909464 1.638375
2 0.698515 0.33819
答案 0 :(得分:1)
我认为->
的第一级需要groupby
,xs
的自定义功能需要MultiIndex
的第二级:
MultiIndex
类似的解决方案:
np.random.seed(1542)
df = pd.DataFrame(np.random.randn(6,6), index = range(6),\
columns = [['g1', 'g1', 'g1', 'g2', 'g2', 'g2'],\
['c1', 'c2', 'c3', 'c1', 'c2', 'c3']])
print (df)
g1 g2
c1 c2 c3 c1 c2 c3
0 -0.556376 -0.295627 0.618673 -0.409434 0.107020 -1.143460
1 -0.145909 0.017417 0.117667 -0.301128 0.880918 -1.027282
2 2.287448 1.528137 -1.528636 0.052728 -1.842634 -0.757457
3 -0.651587 -1.075176 1.128277 0.632036 -0.240965 0.421812
4 -1.620718 0.146108 0.030500 -0.446294 -0.206774 0.819859
5 -0.757296 1.826793 -0.352837 -2.048026 1.362865 1.024671
def f(x):
a = x.xs('c2', axis=1, level=1)[x.name].max()
b = x.xs('c3', axis=1, level=1)[x.name].max()
#print (a)
return a - b
s = df.groupby(level=0, axis=1).apply(f)
print (s)
g1 0.698516
g2 0.338194
dtype: float64
编辑:
def f(x):
a = x.xs('c2', axis=1, level=1).squeeze()
b = x.xs('c3', axis=1, level=1).squeeze()
return a.max() - b.max()
a = df.groupby(level=0, axis=1).apply(f)
print (a)
g1 0.698516
g2 0.338194
dtype: float64
EDIT1:
解决方案应该更简化:
def f(x):
a = x.xs('c2', axis=1, level=1)[x.name]
b = x.xs('c3', axis=1, level=1)[x.name]
#print (a)
return a - b
s = df.resample('M').max().groupby(level=0, axis=1).apply(f)
print (s)
g1 g2
2017-01-31 0.909464 1.638375
2017-02-28 0.698516 0.338194
print (df.resample('M').max())
g1 g2
c1 c2 c3 c1 c2 c3
2017-01-31 2.287448 1.528137 0.618673 0.052728 0.880918 -0.757457
2017-02-28 -0.651587 1.826793 1.128277 0.632036 1.362865 1.024671
答案 1 :(得分:0)
感谢jezrael提供的有用信息。在此基础上,我已经编写了一个问题的解决方案:应用一个复杂的函数,它将两个或多个数组作为输入并返回一个值,并将其应用于具有分层列名称的数据帧以及基于日期时间的重新采样索引。
首先,这是我将用于示例的表
mat = np.random.randint(0, 101, size = (10, 6))
index = pd.date_range(start = '25 Jan 2018', periods = 10)
first_column_name = ['Group1']*3 + ['Group2']*3
second_column_name = ['Col1', 'Col2', 'Col3']*2
df = pd.DataFrame(mat, index = index, columns = [first_column_name,\
second_column_name])
Group1 Group2
Col1 Col2 Col3 Col1 Col2 Col3
2018-01-25 11 36 80 88 31 33
2018-01-26 30 32 61 53 55 43
2018-01-27 64 26 21 63 33 93
2018-01-28 52 59 23 54 91 60
2018-01-29 93 88 27 16 88 7
2018-01-30 28 76 48 5 38 1
2018-01-31 7 29 45 86 53 96
2018-02-01 18 89 69 3 34 34
2018-02-02 0 7 94 99 5 68
2018-02-03 29 13 98 25 51 44
现在我想申请这个功能:
def my_fun(arr1, arr2):
arr1 = np.array(arr1)
arr2 = np.array(arr2)
tmp = np.abs(arr1 - arr2)
return(np.sum(tmp))
请注意,这是一个简单的案例:在实际情况下,该功能非常复杂,无法解决问题!
当我将功能应用于' Col1'时,所需的输出如下: ' Col 3':
Group1 Group2
2018-01-31 296 124
2018-02-28 214 81
为此,我已经应用了一些面向对象的编程来将resample与groupby结合起来。
所以我创建了这个类
class ApplyFunction():
def __init__(self, column_names, fun, resample = None):
self.cn = column_names
self.fun = fun
self.resample = resample
# Initialize the stored values
self.stored_values = dict()
for name in self.cn:
self.stored_values[name] = []
def __store(self, x):
self.stored_values[self.to_store].append(x.values.copy())
def wrapper_with_resample(self, x):
if self.resample is None:
print('Can not use this function with resample = None')
return np.nan
# Get the names of the group
group_name = x.columns.levels[0][x.columns.labels[0][0]]
# Get the time-steps output of resample (doing a dumm operation)
self.timesteps = x.resample(self.resample).apply(lambda x : len(x)).index
# Store the resampled variables
for name in self.cn:
self.to_store = name
x[(group_name, name)].resample(self.resample).apply(self.__store)
# Create a new DataFrame for the output
out = []
for i in range(len(self.timesteps)):
out.append(self.fun(*[self.stored_values[name][i] for name in self.cn]))
out = pd.Series(out, index = self.timesteps)
# Reset self.stored_values
for name in self.cn:
self.stored_values[name] = []
return out
然后我按如下方式使用它:
f = ApplyFunction(column_names = ['Col1', 'Col3'], fun = my_fun, resample = 'M')
output = df.groupby(level = 0, axis = 1).apply(f.wrapper_with_resample)
此解决方案已经完成,因为我们希望将groupby和resample一起应用,但我还没有在pandas中找到合适的解决方案。
我希望这个解决方案对某些人有用;当然还有改进的空间,所以随意发布替代和更有效的解决方案!
感谢。马可