我从输入DataFrame
开始,看起来像是:
df = pd.DataFrame({"created_on":[datetime(2015, 1, 3),
datetime(2015 , 1, 5),
datetime(2015, 2, 24),
datetime(2015, 3, 6),
datetime(2015, 3, 17),
datetime(2015, 5, 31),
datetime(2015, 6, 3)],
"value":[3, 2, 1, 1, 3, 2, 2]
}
)
即
created_on value
0 2015-01-03 3
1 2015-01-05 2
2 2015-02-24 1
3 2015-03-06 1
4 2015-03-17 3
5 2015-05-31 2
6 2015-06-03 2
我希望每月获得created_on
在当月或过去2个月内从当月开始的观察的中位数。
上述输入数据的预期输出为:
month median_value
2015-01-01 2.5
2015-02-01 2
2015-03-01 2
2015-04-01 1
2015-05-01 2
2015-06-01 2
即" 2015-01-01",仅观察" 2015-01-03"和" 2015-01-05"使用了一个月" 2015-02-01",我采取" 2015-01-03"," 2015-01-05"和" 2015-02-24"等等。
我想从日期列中提取月份,然后像这样使用groupby
:
df['created_on_month'] = df['created_on'].apply(
lambda dt: datetime(dt.year, dt.month, 1)
)
df.groupby('created_on_month').median()
但我不知道如何在groupby
中的3个月内聚合,以便一行可以属于多个群组。此解决方案的其他问题是空白月(" 2015-04-01"在上面的示例中)不会出现在结果中。
我也尝试过使用pandas提供的rolling_median
可以完成这项工作,但它只有一个resample
只能计算观察次数,但中位数的中位数不一样。
最后,我还可以使用一个简单的循环:
months = pd.date_range('2015-01-01', '2015-06-01', freq='MS')
output = pd.DataFrame(index=months, columns=("month", "median_value"))
for m in months:
tmp = df [ (df.created_on >= (m - pd.DateOffset(months=2)))
& (df.created_on <= m+pd.DateOffset(months=1))]
res = {"month":m, "median_value":tmp["value"].median()}
output.loc[m] = res
print output
产生:
month median_value
2015-01-01 2015-01-01 00:00:00 2.5
2015-02-01 2015-02-01 00:00:00 2
2015-03-01 2015-03-01 00:00:00 2
2015-04-01 2015-04-01 00:00:00 1
2015-05-01 2015-05-01 00:00:00 2
2015-06-01 2015-06-01 00:00:00 2
但如果存在更优雅的解决方案,我会很乐意学习它。
答案 0 :(得分:1)
好的,这应该非常接近。我正在使用90天的窗口b / c我不确定我是否可以做3个月的窗口。否则,它似乎工作得很好。
df2 = pd.rolling_apply( df.set_index('created_on')['value'], window=90,
func=np.nanmedian, freq='d', min_periods=1 )
df2[ (df2.index.day == 1)[1:] ] # [1:] is a kludge to get end of month
# rather than beginning, probably a
# better way to do that...
created_on
2015-01-31 2.5
2015-02-28 2.0
2015-03-31 2.0
2015-04-30 1.0
2015-05-31 2.0
请注意,由于我的方法与您的方法不同,因此它会标注为月末而不是月初,但这不会以任何方式影响结果,我认为月末实际上更准确。< / p>
我不确定将2015年6月的结果打印出来的最佳方法,但它在6月3日的df2中正确存储:
df2.tail(1)
created_on
2015-06-03 2
所以这只是提取和显示信息的最佳方式。我想只需填写6月30日缺少值的原始数据帧就可以了。