Pandas:应用一个以 2 个数组作为输入和 return 单个值的函数

Pandas: apply a function with 2 array as input and return a single value

我有一个 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' 以及 returns 取为单个值。

函数的一个例子(在实际情况下更复杂)可以是

def function(first_column, second_column):
    return(max(first_column) - max(second_column))

当我将它应用到我的 DataFrame 时,我想返回一个 DataFrame 告诉我每个组的 'function' 的输出,因此,在这种情况下,'g1' 和'g2'.

请注意,我希望它在 gorupby() 的情况下也能工作,这样,在这种情况下,我得到每个组的函数结果 ('g1' 和 'g2') 以及每个 groupby 子集。

对于上面的情况,如果我想按月汇总,结果应该是:

         g1      g2                    
1  0.909464     1.638375
2  0.698515     0.33819

我认为 MultiIndex 的 select 第二级需要 groupby by first level of MultiIndex with custom function with xs:

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

编辑:

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:

解决方案应该更加简化:

a = df.resample('M').max()
b = a.xs('c2', axis=1, level=1)
c = a.xs('c3', axis=1, level=1)
d = b - c
print (d)
                  g1        g2
2017-01-31  0.909464  1.638375
2017-02-28  0.698516  0.338194

感谢 jezrael 提供的有用信息。在此基础上,我编写了一个解决该问题的方法:应用一个复杂的函数,该函数将两个或多个数组作为输入并 return 一个值,并将其应用于具有分层列名的数据框以及基于重采样的数据框关于日期时间索引。

首先,这是我将用于示例的 table

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))

请注意,这是一个简单的案例:在实际案例中,功能极其复杂,work-around无法接受!

当我将函数应用于 'Col1' 'Col 3' 时,所需的输出如下:

            Group1  Group2
2018-01-31     296     124
2018-02-28     214      81

为此,我应用了一点面向对象的编程来将重采样与 groupby 相结合。

所以我创建了这个 class

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.

中没有找到suitable解决方案

我希望这个解决方案对某人有用;当然还有改进的空间,所以请随时 post 替代和更有效的解决方案!

谢谢。马可