多列分组并应用移动函数

2024-03-28 20:27:50 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有一个数据集:

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,最小周期=1,NAs不计算在内),我希望有以下内容:

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实现这一点?你知道吗

举一个简单的文字例子,例如,我想要在2018年1月3日对国家1进行的计算是取该国2018年1月2日和2018年1月3日所有公司价值的平均值(在窗口大小为2的情况下)。你知道吗

因此,我希望在2018年1月3日为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,因此提供一个对任何自定义函数都有效的解决方案是很好的。你知道吗


Tags: 数据函数idpandasdatevalue方式情况
2条回答

您可以通过以下方式获得想要的结果:

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

更新附加信息

数据:

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)

现在groupby在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

让我们更详细地看一下。因为我们是按国家/地区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的滚动平均数

如果我们想按日期进行分组,我们首先按国家/地区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

这里的问题是,您希望滚动平均值首先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-03-01滚动平均值,我们有1和2,也就是3。除以2等于1.5。你知道吗

我们必须首先对滚动值求和,然后除以记录数。你知道吗

相关问题 更多 >