Python DataFrame 从每日数据中选择每月增量的行

Posted

技术标签:

【中文标题】Python DataFrame 从每日数据中选择每月增量的行【英文标题】:Python DataFrame selecting the rows with monthly increment from daily data 【发布时间】:2019-03-09 09:43:44 【问题描述】:

让我们直接进入问题。以下为每日数据:

             AAA    BBB    CCC
date                           
2012-04-16  44.48  28.48  17.65
2012-04-17  44.59  28.74  17.65
2012-04-18  44.92  28.74  17.72
2012-04-19  44.92  28.62  17.72
2012-04-20  45.09  28.68  17.71
2012-04-23  45.09  28.40  17.76
2012-04-24  45.09  28.51  17.73
2012-04-25  45.01  28.76  17.73
2012-04-26  45.40  28.94  17.76
2012-04-27  45.57  29.02  17.79
2012-04-30  45.45  28.90  17.80
2012-05-01  45.79  29.07  17.80
2012-05-02  45.71  28.98  17.77
2012-05-03  45.44  28.81  17.79
2012-05-04  45.05  28.48  17.79
2012-05-07  45.05  28.48  17.79
2012-05-08  45.00  28.40  17.93
2012-05-09  44.87  28.30  17.94
2012-05-10  44.93  28.34  17.85
2012-05-11  44.86  28.30  17.96
           ...    ...    ...

我想从第一行开始选择月增量的行,即索引为2012-04-16, 2012-05-16, 2012的行-06-16,... 。我可以使用 relativedelta 并手动添加它们,但我想知道是否有更有效的方法。我尝试重新采样,但我只能选择每月的第一天或最后一天,如df.resample('M').first()

使问题更复杂的是缺少某些日期;它们是工作日,但不是美国的工作日。有几种方法可以解决此问题:

    选择确切日期或最接近日期的较早日期。如果 这样的日期不存在,然后开始查找以后的日期。

    选择确切日期或最接近日期的较晚日期。如果这样 日期不存在,然后开始查找更早的日期。

    选择最接近确切日期的日期,无论是否早 或迟到;我可以使用min(df.index, key=lambda x: abs(x - (df.index[0] + relativedelta(months=1)))

在每种情况下,我都想知道哪种方法最有效且易于阅读。在最后一个代码示例中,月份是一个变量,所以我不确定是否可以将其作为 lambda 过程并使用“应用”。

提前致谢。

【问题讨论】:

可能是this可以帮助你。 @pygo 抱歉,这对我没有帮助:( 【参考方案1】:

在查看您的数据之前,让我们先看看如何为每个月的特定日期创建DatetimeIndex。由于具有每月频率的常规 pd.date_range 占用每个月的最后天,我们可以简单地添加固定天数:

idx = pd.date_range('2018-04-01', '2018-07-01', freq='1M') + pd.DateOffset(days=16)

DatetimeIndex(['2018-05-16', '2018-06-16', '2018-07-16'],
              dtype='datetime64[ns]', freq=None)

现在让我们以一个缺少 16th 天的示例数据框为例:

              AAA    BBB    CCC
date                           
2012-04-16  44.48  28.48  17.65
2012-04-17  44.59  28.74  17.65
2012-05-15  45.79  29.07  17.80
2012-05-16  45.71  28.98  17.77
2012-05-17  45.44  28.81  17.79
2012-06-15  44.87  28.30  17.94
2012-06-17  44.95  28.50  17.98
2012-07-14  44.65  28.25  17.87
2012-07-17  44.55  28.75  17.75

正如您所提到的,您可以通过多种方式来决定如何选择不匹配的日期,或者向后、向前或无偏好地寻找最近的日期。 您需要考虑什么最适合您的项目。下面是一个坚持 Pandas 功能并避免自定义 lambda 函数的解决方案。

DatetimeIndex定义数据框

首先创建一个只指定所需索引的数据框:

offset = pd.DateOffset(days=16)
start_date = df.index[0]-pd.DateOffset(months=1)
idx = pd.date_range(start_date, df.index[-1], freq='1M') + offset

df_idx = pd.DataFrame(index=idx)

注意,我们需要从 start 参数中减去一个月,这样在加上 16 天后第一个月就不会被省略。现在您可以使用pd.merge_asof 和多种选项:-

通过merge_asof向后/向前/最近匹配

根据需要将direction 参数指定为'backward'(默认)、'forward''nearest'。例如,使用'forward':

print(pd.merge_asof(df_idx, df, left_index=True, right_index=True, direction='forward'))

              AAA    BBB    CCC
2012-04-16  44.48  28.48  17.65
2012-05-16  45.71  28.98  17.77
2012-06-16  44.95  28.50  17.98
2012-07-16  44.55  28.75  17.75

现在这可能足以满足您的需求。


编辑:如果要保留数据帧中的索引,可以反转合并的方向并使用'backward'而不是'forward'

res = pd.merge_asof(df.reset_index(),
                    df_idx.reset_index().rename(columns='index': 'date_idx'),
                    left_on='date', right_on='date_idx', direction='backward')

res['diff'] = (res['date'] - res['date_idx']).dt.days.abs()
grouper = res['date'].dt.strftime('%Y-%m')
res = res[res['diff'] == res.groupby(grouper)['diff'].transform('min')]

print(res)

        date    AAA    BBB    CCC   date_idx  diff
0 2012-04-16  44.48  28.48  17.65 2012-04-16     0
3 2012-05-16  45.71  28.98  17.77 2012-05-16     0
6 2012-06-17  44.95  28.50  17.98 2012-06-16     1
8 2012-07-17  44.55  28.75  17.75 2012-07-16     1

【讨论】:

非常感谢@jpp 您的回答对我帮助很大。不过,我有一些问题。 (1) 我想知道是否可以选择索引而不是 df_idx 的索引,而是转发的索引(例如,'2012-06-17' 而不是 '2012-06-16')。 (2) 如果偏移天数设置得太晚,索引会溢出到下个月,因此 idx 将有两个相同月份的索引。我想知道是否可以使用带有 relativedelta 的 dateuitl 模块来防止这种溢出。 - 我查看了您使用过的功能的文档,但我无法弄清楚。多一点帮助将不胜感激! (1),当然,有点复杂,但请参阅更新。 (2) 只需为此使用手动检查,例如if 声明,这是一次性任务,因此计算量不会很大。 (3) 避免 dateutilrelativedelta 使用 Pandas。这些库是为常规 datetime 对象设计的,而不是为 Pandas pd.Timestamp 对象设计的。最好不要将两者混用。 您的建议很有帮助。我想我可以通过使用函数手动选择我想要的索引来构造 idx,然后应用你的方法。如果你能告诉我在哪里可以有组织地学习熊猫,我会很高兴;仅仅阅读文件对我来说效率很低...... @Taxxi,IMO,您可以查看一些教程 here,否则您可能会发现通过尝试事物和在遇到困难时使用 SO 来学习是一种有用的过程。

以上是关于Python DataFrame 从每日数据中选择每月增量的行的主要内容,如果未能解决你的问题,请参考以下文章

从每 1 小时块中选择最小值

从每组 sqlite 中选择前 n 条记录

Python Pandas DataFrame:查询数据or选择数据(selection)之loc,iloc,at,iat,ix的用法和区别

如何在 Python 中创建DataFrame

Python Pandas Dataframe 合并并只选择几列

Python数据分析之Pandas