【问题标题】:monthly resampling pandas with specific start day每月对具有特定开始日期的熊猫进行重新采样
【发布时间】:2020-12-05 06:41:57
【问题描述】:

我正在创建一个带有随机日期和随机整数值的 pandas DataFrame,我想按月重新采样并计算整数的平均值。这可以通过以下代码完成:

def random_dates(start='2018-01-01', end='2019-01-01', n=300):

    start_u = start.value//10**9
    end_u = end.value//10**9
    return pd.to_datetime(np.random.randint(start_u, end_u, n), unit='s')

start = pd.to_datetime('2018-01-01')
end = pd.to_datetime('2019-01-01')

dates = random_dates(start, end)
ints = np.random.randint(100, size=300)
df = pd.DataFrame({'Month': dates, 'Integers': ints})

print(df.resample('M', on='Month').mean())

问题是重新采样的月份总是从第一天开始,我希望所有月份都从第 15 天开始。我使用的是 pandas 1.1.4,我尝试过使用 origin='15/01/2018'offset='15',但都没有它们适用于'M' resample 规则(当我使用30D 时它们确实有效,但它没有用)。我也尝试过使用'2SM',但它也不起作用。

所以我的问题是,是否有办法更改重采样规则,或者我必须在数据中添加偏移量?

【问题讨论】:

  • 您希望所有月份都从第 15 天开始还是仅从第 1 个月开始?
  • @kate-melnykova,我希望从第 15 天开始的所有月份。我在我的问题上对其进行了编辑以使其更加清晰。谢谢。

标签: python pandas


【解决方案1】:

假设源DataFrame是:

       Month  Amount
0 2020-05-05       1
1 2020-05-14       1
2 2020-05-15      10
3 2020-05-20      10
4 2020-05-30      10
5 2020-06-15      20
6 2020-06-20      20

要计算您的“移位”重采样,请先移位 Month 列,以便 每月的第 15 天变成第 1 天:

df.Month = df.Month - pd.Timedelta('14D')

然后重新采样:

res = df.resample('M', on='Month').mean()

结果是:

            Amount
Month             
2020-04-30       1
2020-05-31      10
2020-06-30      20

如果需要,将索引中的日期更改为月份:

res.index = res.index.to_period('M')

那么结果将是:

         Amount
Month          
2020-04       1
2020-05      10
2020-06      20

【讨论】:

  • 感谢您的回答。它确实解决了这个问题,因此我赞成它,但我希望有一种方法可以做到这一点而不向数据数据添加偏移量
  • 您不需要“破坏”您原来的 Month 列。您可以将“移位日期”保存在另一列中(例如 Month_wrk),然后仅在此列上重新采样。然后您可以删除此列。
  • 我同意,直到知道这肯定是解决这个问题的最佳方法。唯一的问题是,在我的问题中,我特别要求提供一种不需要为日期添加偏移量的解决方案,因为我已经知道我可以做到。不过还是谢谢你。
【解决方案2】:

编辑:不是 OP 要求的有效解决方案。请参阅 cmets 中的简短讨论。

有趣的问题。我建议使用“SMS”重新采样 - 半月开始频率(1 日和 15 日)。不只保留平均值,而是保留计数值和总和值,并通过其两个子周期重新计算每个月周期的加权平均值(例如:15/1 到 15/2 由 15/1-31/1 组成和 1/2-15/2)。

这里的优点是,与(不当使用)偏移量不同,我们确信我们总是从每月 15 日开始直到下个月 14 日。

df_sm = df.resample('SMS', on='Month').aggregate(['sum', 'count'])
df_sm

           Integers      
                sum count
Month                    
2018-01-01      876    16
2018-01-15      864    16
2018-02-01      412    10
2018-02-15      626    12
...
2018-12-01      492    10
2018-12-15      638    16

滚动和滚动计数;找出它们的平均值:

df_sm['sum_rolling'] = df_sm['Integers']['sum'].rolling(2).sum()
df_sm['count_rolling'] = df_sm['Integers']['count'].rolling(2).sum()
df_sm['mean'] = df_sm['sum_rolling'] / df_sm['count_rolling']

df_sm
           Integers       count_sum count_rolling       mean
                sum count                                   
Month                                                       
2018-01-01      876    16       NaN           NaN        NaN
2018-01-15      864    16    1740.0          32.0  54.375000
2018-02-01      412    10    1276.0          26.0  49.076923
2018-02-15      626    12    1038.0          22.0  47.181818
...
2018-12-01      492    10    1556.0          27.0  57.629630
2018-12-15      638    16    1130.0          26.0  43.461538

现在,只需过滤 df_sm 的奇数索引:

df_sm.iloc[1::2]['mean']

Month
2018-01-15    54.375000
2018-02-15    47.181818
2018-03-15    51.000000
2018-04-15    44.897436
2018-05-15    52.450000
2018-06-15    33.722222
2018-07-15    41.277778
2018-08-15    46.391304
2018-09-15    45.631579
2018-10-15    54.107143
2018-11-15    58.058824
2018-12-15    43.461538
Freq: 2SMS-15, Name: mean, dtype: float64

代码:

df_sm = df.resample('SMS', on='Month').aggregate(['sum', 'count'])
df_sm['sum_rolling'] = df_sm['Integers']['sum'].rolling(2).sum()
df_sm['count_rolling'] = df_sm['Integers']['count'].rolling(2).sum()
df_sm['mean'] = df_sm['sum_rolling'] / df_sm['count_rolling']
df_out = df_sm[1::2]['mean']

编辑:更改了其中一列的名称以使其更清晰

【讨论】:

  • 这个想法很棒。谢谢。
  • 抱歉,我知道您的解决方案有误。正在打印的第一行,日期为2018-01-15,实际上是从2018-01-012018-01-31 的日期总和。您可以通过过滤偶数索引来修复它,但第一个值将是 NaN。
  • 对不起!我以为我检查过了。只是为了确保在我尝试弄清楚之前,您将“SMS”而不是“SM”传递给resample,对吗?我从“SM”开始,这就是我尝试使用“SMS”的确切原因,认为它解决了它..
  • 是的,你是对的。可以用df_sm.iloc[::2]过滤,然后“2018-02-01”月对应“2018-01-15”到“2018-02-14”,“2018-12-01”月对应“2018-11-” 15' 直到 '2018-12-14'。但是今年剩下的半个月:'2018-01-01'到'2018-01-15'和'2018-12-15'到'2018-12-31'有什么要求呢?
  • 我发现使用 SM 或 SMS 没有区别。 “2018-01-15”之前的数据应该是从“2017-12-15”开始的一个时期。我的意思是,目的是使用真实数据而不是随机生成的日期,但总会有一个初始日期,我应该保证它处于正确的时期。我相信使用和偏移14天的解决方案是最简单的解决方案。我只是期待origin 应该以某种方式进行月重采样。它似乎很愚蠢,以至于它没有。
猜你喜欢
  • 2019-02-28
  • 2017-06-10
  • 1970-01-01
  • 1970-01-01
  • 2018-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-16
相关资源
最近更新 更多