【问题标题】:Calculate Days Under Contract from Table of Contract Start and End Dates从合同开始和结束日期表计算合同天数
【发布时间】:2019-10-11 04:27:37
【问题描述】:

我有一个pandas 特定资产(A、B、...)的合同数据框。每个合同都有一个开始日期、结束日期(包括两个日期)和一个日期(合同不能重叠)。我想生成一个表格,显示在指定期间(即日期范围,在本例中为季度)内的总天数,每项资产都已签订合同。然后,我想计算每项资产的总收入(日费率 * 合同天数)。

我从生成季度结束日期列表开始,但不知道如何继续:

pd.date_range(start='9/30/2019',end='12/31/2020',freq='Q').tolist()

这是我的示例数据:

pd.DataFrame([['A', pd.to_datetime('07/30/2019'), pd.to_datetime('08/25/2019'), 5], ['B', pd.to_datetime('08/30/2022'), pd.to_datetime('09/30/2019'), 10], ['A',pd.to_datetime('09/30/2019'),pd.to_datetime('10/31/2019'), 2]], columns=['Asset', 'start', 'end', 'dayrate']).set_index('Asset')

    start   end dayrate
Asset           
A   2019-07-30  2019-08-25  5
B   2022-08-30  2019-09-30  10
A   2019-09-30  2019-10-31  2

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    如果我正确理解了问题陈述,这应该可以工作。

    # create the dates for each quarter
    date_range_quarter_lst = pd.date_range(start='9/30/2019',end='12/31/2020', freq='Q').tolist()
    
    # create tuples of those dates
    def pairwise(iterable):
        it = iter(iterable)
        a = next(it, None)
    
        for b in it:
            yield (a, b)
            a = b
    date_range_quarter_zip = [*pairwise(date_range_quarter_lst)]
    
    # extract day by day views between the start and end dates
    date_range_days = [pd.date_range(start=_[0], end=_[1], freq='d').tolist() for _ in date_range_quarter_zip]
    
    # function to get the total revenue for the intersection of days
    def get_day_count(row, date_range):
        # get all days worked by the contracter between their start and end date
        day_dates = pd.date_range(start=row['start'],end=row['end'], freq='d').tolist()
        # set this with the specified date range and multiply by the day rate
        return len(set(day_dates).intersection(set(date_range))) * row['dayrate']
    
    rev_cols = []
    # iterate over each period (quarter) and create a new column
    for date_range in date_range_days:
        col_nm = f"total_revs_{date_range[0].strftime('%Y%m%d')}_{date_range[-1].strftime('%Y%m%d')}"
        df[col_nm] = df.apply(lambda row: get_day_count(row, date_range), axis=1)
        rev_cols.append(col_nm)
    
    # groupby
    df.groupby(df.index)[rev_cols].sum()
    

    输出(分组前)

            start   end         dayrate total_revs_20190930_20191231    total_revs_20200331_20200630    total_revs_20200930_20201231
    Asset                       
    A   2019-07-30  2019-08-25  5       0                               0                               0
    B   2022-08-30  2019-09-30  10      0                               0                               0
    A   2019-09-30  2019-10-31  2       64                              0                               0
    

    输出(发布分组)

    Asset    total_revs_20190930_20191231   total_revs_20200331_20200630    total_revs_20200930_20201231
    
    A       64                              0                                0
    B       0                               0                                0
    

    【讨论】:

    • 不完全。资产 A 在 2019 年 10 月 31 日之后开始的期间应该有零收入,因为那是它的最后一份合同结束的时间。我正在寻找一个汇总表,其中包含行 A、B 和 cols 2019 年第三季度、2019 年第四季度等,显示所有合同下每项资产的收入。
    • 如果有助于更好地理解这个问题,我同样询问了如何在 Excel 中执行此操作:superuser.com/q/1490685/1090414
    • 哎呀,对于设置操作,我使用了union 而不是intersection。已更新。
    • 另请注意,对于 Asset B,您有一个 start 日期 > 您的 end 日期。
    • 问题中的错字。很好的收获。
    【解决方案2】:

    假设数据框中的日期确实是 datetime 对象,这样的事情可能会起作用。 See the timedelta docs for more info

    contracts = {item : 0 for item in df.index.unique()}
    
    for index, row in df.iterrows():
        contracts[index] += row['end'] - row['start']
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-18
      • 2019-09-20
      • 1970-01-01
      • 1970-01-01
      • 2012-04-06
      • 1970-01-01
      • 2020-10-08
      • 1970-01-01
      相关资源
      最近更新 更多