假设我有这个数据集:
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
的移动平均)执行相同的操作,因此最好提供对任何自定义函数均有效的解决方案。
答案 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_id
和date
中进行分组,以求和,然后除以计数之和
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)