【问题标题】:How to convert start and end dates of rows into monthly chunks using pandas?如何使用熊猫将行的开始日期和结束日期转换为每月块?
【发布时间】:2019-12-26 08:02:17
【问题描述】:

这是我的数据集

  id      start        end
0  A   1-Jan-19  31-Dec-19
1  A   1-Jan-20  31-Dec-20
2  B  13-Jun-19  19-Nov-19
df = pd.DataFrame({'id': ["A", "A", "B"],
           'start': ["1-Jan-19", "1-Jan-20", "13-Jun-19"],
           'end': ["31-Dec-19", "31-Dec-20", "19-Nov-19"]})

这是我需要的数据集

   id      start        end
0   A   1-Jan-19  31-Jan-19
1   A   1-Feb-19  28-Feb-19
2   A   1-Mar-19  31-Mar-19
3   A   1-Apr-19  30-Apr-19
4   A   1-May-19  31-May-19
5   A   1-Jun-19  30-Jun-19
6   A   1-Jul-19  31-Jul-19
7   A   1-Aug-19  31-Aug-19
8   A   1-Sep-19  30-Sep-19
9   A   1-Oct-19  31-Oct-19
10  A   1-Nov-19  30-Nov-19
11  A   1-Dec-19  31-Dec-19
12  B  13-Jun-19  30-Jun-19
13  B   1-Jul-19  31-Jul-19
14  B   1-Aug-19  31-Aug-19
15  B   1-Sep-19  30-Sep-19
16  B   1-Oct-19  31-Oct-19
17  B   1-Nov-19  19-Nov-19

数据按 id 分组,时间块分布在几个月内。我尝试使用 resample 和 bfill Pandas 方法没有成功。是否可以使用 Pandas 执行此操作,或者我是否需要通过对日期系列进行合并并过滤日期重叠的位置来解决方法?

谢谢

【问题讨论】:

  • 预期数据集中索引值为 0 到 11 的行似乎是日期为 1-Jan-1931-Dec 的源行的“结果” -19。然后(索引值为 12 到 17 的行)从日期为 13-Jun-1919-Nov-19 的行产生。但是,两个日期都从 2020 年开始的行怎么办?这行的“结果”应该是什么?

标签: python pandas time-series


【解决方案1】:

这是解决方案。

from dateutil.relativedelta import relativedelta
import pandas as pd
import numpy as np

df1 = pd.DataFrame({'id': ["A", "A", "B"],
           'start': ["1-Jan-19", "1-Jan-20", "13-Jun-19"],
           'end': ["31-Dec-19", "31-Dec-20", "19-Nov-19"]})

df1["start"] = df1["start"].apply(lambda x: datetime.strptime(x, "%d-%b-%y") )
df1["end"] = df1["end"].apply(lambda x: datetime.strptime(x, "%d-%b-%y") )
df1["rows"] = abs((df1["start"]-df1["end"])/np.timedelta64(1,'M')).astype(int)
df_new = pd.DataFrame([df1.ix[idx] 
                       for idx in df1.index 
                       for i in range(df1.ix[idx]['rows'] +1)])
df_new = df_new.reset_index(drop=True)
groups = df_new.groupby(["start", "end"],as_index = False)
grouped_df = groups.apply(lambda x: x.reset_index(drop = True)).reset_index().drop('level_0',axis = 1)
grouped_df["start"] =grouped_df.apply(lambda row : row["start"]+  relativedelta(months=row["level_1"]), axis =1)
grouped_df.drop(['level_1', 'rows'], axis =1 ,inplace = True)
grouped_df

我假设您跳过了第二个输入行的处理。
如果不是这种情况,请告诉我。

输入:

输出:

【讨论】:

    【解决方案2】:

    我不认为我的解决方案是优雅的。但是,它会返回您想要的输出。第一步,我只是像你一样导入包并制作df 数据框。

    import pandas as pd
    import datetime
    from dateutil.rrule import rrule, MONTHLY
    
    df = pd.DataFrame(
        {
            'id': ["A", "A", "B"],
            'start': ["1-Jan-19", "1-Jan-20", "13-Jun-19"],
            'end': ["31-Dec-19", "31-Dec-20", "19-Nov-19"]
        }
    )
    

    在第二部分中,我遍历df 数据帧中的每一行,并尝试为第二个数据帧创建一个项目——我称之为df_2。项目存储在data_items 列表中。

    data_items = []
    for id_, start, end in zip(df['id'], df['start'], df['end']):
        start_dt = datetime.datetime.strptime(start, '%d-%b-%y')
        end_dt = datetime.datetime.strptime(end, '%d-%b-%y')
        start_dates = [dt for dt in rrule(MONTHLY, dtstart=start_dt, until=end_dt.replace(day=start_dt.day))]
        for i, start_date in enumerate(start_dates):
            if i == len(start_dates) - 1:
                end_date = end_dt
            elif start_date.month == 12:
                end_date = start_date.replace(day=31)
            else:
                end_date = start_date.replace(month=start_date.month + 1, day=1) - datetime.timedelta(days=1)
            data_items.append(
                {
                    'id': id_,
                    'start': start_date if i == 0 else start_date.replace(day=1),
                    'end': end_date
                }
            )
    

    您可以检查df_2 中的输出是否是您想要实现的。

    df_2 = pd.DataFrame(data_items)
    df_2[['id', 'start', 'end']]
    

    【讨论】:

      【解决方案3】:

      从定义要应用于每一行的函数开始:

      def expDates(row):
          d1 = pd.to_datetime(row.start)
          d2 = pd.to_datetime(row.end)
          stList = [d.strftime('%#d-%b-%y') for d in pd.date_range(d1, d2, freq='MS')]
          if len(stList) == 0:
              stList.append(row.start)
          elif stList[0] != row.start:
              stList.insert(0, row.start)
          endList = [d.strftime('%#d-%b-%y') for d in pd.date_range(d1, d2, freq='M')]
          if len(endList) == 0 or endList[-1] != row.end:
              endList.append(row.end)
          return pd.DataFrame({'id': row.id, 'start': stList, 'end': endList})
      

      然后应用此函数,将结果保存为 DataFrames 列表:

      tbl = df.apply(expDates, axis=1).tolist()
      

      最后一步是连接这些 DataFrame:

      pd.concat(tbl, ignore_index=True)
      

      在我看来,这个解决方案不是“pythonic”,而是更多“pandasonic” 然后是所有其他人。

      【讨论】:

        猜你喜欢
        • 2021-12-06
        • 2019-10-21
        • 1970-01-01
        • 2023-03-17
        • 2023-03-10
        • 1970-01-01
        • 2021-06-14
        • 2022-01-23
        • 1970-01-01
        相关资源
        最近更新 更多