对多个列进行分组并应用移动功能

时间:2019-02-20 17:55:39

标签: python pandas

假设我有这个数据集:

Country_id  Company_id  Date    Company_value
1   1   01/01/2018  1
1   1   02/01/2018  0
1   1   03/01/2018  2
1   1   04/01/2018  NA
1   2   01/01/2018  1
1   2   02/01/2018  2
1   2   03/01/2018  NA
1   2   04/01/2018  NA
2   1   01/01/2018  3
2   1   02/01/2018  0
2   1   03/01/2018  2
2   1   04/01/2018  NA
2   2   01/01/2018  1
2   2   02/01/2018  2
2   2   03/01/2018  NA
2   2   04/01/2018  NA

我想应用移动函数(例如移动平均值)来检索每个日期和国家/地区的汇总值。

例如,在移动平均值的情况下(窗口= 2且min_periods = 1,不包括NA),我希望获得以下信息:

Country_id  Date    Companies_value
1   01/01/2018  1
1   02/01/2018  1
1   03/01/2018  1.33
1   04/01/2018  2
2   01/01/2018  2
2   02/01/2018  1.5
2   03/01/2018  1.33
2   04/01/2018  2

为方便起见,它是通过以下方式计算的:

Country_id  Date    Companies_value
1   01/01/2018  (1+1)/2
1   02/01/2018  (0+1+2+1)/4
1   03/01/2018  (2+0+2)/3
1   04/01/2018  (2)/1
2   01/01/2018  (3+1)/2
2   02/01/2018  (0+3+2+1)/4
2   03/01/2018  (2+0+2)/3
2   04/01/2018  (2)/1

如何使用pandas来做到这一点?

举一个简短的例子,例如我想要在日期03/01/2018的国家1中取该国家在02/01/2018和03/01/2018(对于2号窗口)。

因此,这就是我想要在2018年3月1日对1国进行的操作:

( Company_value(Company_1, 03/01/2018) + Company_value(Company_1, 02/01/2018) 
+ Company_value(Company_2, 03/01/2018) + Company_value(Company_2, 02/01/2018) ) / 4 =

= ( 2 + 0 + NA + 2) / 4 

= ( 2 + 0 + 2) / 3 # NAs not counted in

= 1.33

类似地,我想对每个国家/地区的所有日期执行相同的操作。

正如我说过的,我想对自己的移动函数(超出pandas的移动平均)执行相同的操作,因此最好提供对任何自定义函数均有效的解决方案。

2 个答案:

答案 0 :(得分:2)

更新了其他信息

数据

import pandas as pd
import numpy as np

df = pd.DataFrame({'date':['2018-01-01', '2018-02-01', '2018-03-01', '2018-04-01']*4,
              'country_id':[1]*8+[2]*8,
              'company_id':[1]*4+[2]*4+[1]*4+[2]*4,
              'value':[1, 0, 2, np.nan, 1, 2, np.nan, np.nan, 3, 0, 2, np.nan, 1, 2, np.nan, np.nan]})

country_id内创建滚动总和

df['rolling_sum'] = df.groupby('country_id').apply(lambda x: x.value.rolling(window=2, min_periods=1).sum()).reset_index(drop=True)

country_id内创建滚动计数

df['sum_records'] = df.groupby('country_id').apply(lambda x: x.value.rolling(window=2, min_periods=1).count()).reset_index(drop=True)

现在在country_iddate中进行分组,以求和,然后除以计数之和

summarized_df = df.groupby(['country_id', 'date']).apply(lambda x: x.rolling_sum.sum()/x.sum_records.sum()).reset_index()

country_id  date      
1           2018-01-01    1.000000
            2018-02-01    1.000000
            2018-03-01    1.333333
            2018-04-01    2.000000
2           2018-01-01    2.000000
            2018-02-01    1.500000
            2018-03-01    1.333333
            2018-04-01    2.000000

让我们更详细地了解这一点。由于我们是按country_id进行分组的,因此我们将子集化为一个国家/地区ID,以在以下方面实施此方法:

如果我们只取其中的一部分,请说country_id == 1

df2 = df[df['country_id'] == 1]

         date  country_id  company_id  value
0  2018-01-01           1           1    1.0
1  2018-02-01           1           1    0.0
2  2018-03-01           1           1    2.0
3  2018-04-01           1           1    NaN
4  2018-01-01           1           2    1.0
5  2018-02-01           1           2    2.0
6  2018-03-01           1           2    NaN
7  2018-04-01           1           2    NaN

如果我们想要这个的滚动平均值,我们可以这样做:

df2.value.rolling(window=2, min_periods=1).mean()
0    1.0
1    0.5
2    1.0
3    2.0
4    1.0
5    1.5
6    2.0
7    NaN

在这里我们可以看到子集country_id == 1数据帧中的值以及它们与滚动平均值的关系:

0    1.0  = (1)/1 = 1
1    0.0  = (0 + 1)/2 = 0.5
2    2.0  = (2 + 0)/2 = 1
3    NaN  = (Nan + 2)/1 = 2
4    1.0  = (1 + Nan)/1 = 1
5    2.0  = (2 + 1)/2 = 1.5
6    NaN  = (Nan + 2)/1 = 2
7    NaN  = (Nan + Nan)/0 = Nan

这是我们如何获得country_id单个分组的滚动平均值的方法

如果我们想按日期进行分组,然后我们先按country_id分组,然后按日期分组,那么单个分组将如下所示:

df3 = df[(df['country_id'] == 1) & (df['date'] == '2018-03-01')]

df3.value
2    2.0
6    NaN

df3.value.rolling(window=2, min_periods=1).mean()
2    2.0
6    2.0

df3.value
2    2.0 = (2)/1 = 2
6    NaN = (Nan + 2)/1 = 2

这里的问题是,您希望滚动平均值 first country_id,而不是与date分组。 然后找到按国家/地区划分的滚动平均值后,您想获取那些值并将其取平均值。如果我们要滚动平均值,然后平均值,那将是不正确的。

让我们回到为country_id == 1创建的原始滚动平均值,然后查看日期:

2018-01-01    1.0  = (1)/1 =         1
2018-02-01    0.0  = (0 + 1)/2 =     0.5
2018-03-01    2.0  = (2 + 0)/2 =     1
2018-04-01    NaN  = (Nan + 2)/1 =   2
2018-01-01    1.0  = (1 + Nan)/1 =   1
2018-02-01    2.0  = (2 + 1)/2 =     1.5
2018-03-01    NaN  = (Nan + 2)/1 =   2
2018-04-01    NaN  = (Nan + Nan)/0 = Nan

现在这里最棘手的部分是,此时我们不能仅对它们进行平均,因为例如,如果您查看2018年3月1日的滚动平均值,我们有1和2,即3。 2将给我们1.5。

我们必须先求和滚动值,然后除以记录数。

答案 1 :(得分:0)

您可以通过以下方式获得所需的结果:

# get company value by date
avg = df.groupby(["Country_id", "Date", "Company_id"]).sum().unstack(level=2).loc[:, "Company_value"]
avg = pd.concat([avg, avg.shift(1)], axis=1)
avg["sum"] = avg.apply("sum", axis=1)

# get company count by date
counts = df.groupby(["Country_id", "Date"]).count().loc[:, "Company_value"]
counts2 = counts + counts.shift(1)

# get the "mean"
result = avg["sum"] / counts2.fillna(counts)
相关问题