如何重新格式化数据以参考一个人使用大熊猫的合同期间的月份?

时间:2019-07-18 06:43:55

标签: python pandas

我有一个数据集,该数据集描绘了一组人员的合同开始日期和结束日期,如下所示:

> data = [['Andrew','2019-03','2019-06'],['Betty','2019-02','2019-07'],['Charlotte','2019-01','2019-08'],['Charlotte','2019-04','2019-06']]

> df = pd.DataFrame(data, columns = ['Name','Contract Start Date','Contract End Date'])

我想重新格式化它,以便它显示每个人每个月在合同上的停留时间,例如,第一个月将表示为1,第二个月表示为2,依此类推。应该注意的是,如果他们续签合同,计数器还会重新回到1,这是一个复杂情况。下面是预期的输出:

> data =[['Andrew',0,0,1,2,3,4,0,0],['Betty',0,1,2,3,4,5,6,0], 
        ['Charlotte',1,2,3,1,2,3,0,0]]

> df = pd.DataFrame(data, columns = ['Name','2019-01','2019-02','2019-03','2019-04','2019-05','2019-06','2019-07','2019-08'])

2 个答案:

答案 0 :(得分:1)

设置

import pandas as pd
from dateutil.relativedelta import relativedelta

data = [
    ['Andrew','2019-03','2019-06'],
    ['Betty','2019-02','2019-07'],
    ['Charlotte','2019-01','2019-08'],
    ['Charlotte','2019-04','2019-06']
]

df = pd.DataFrame(data, columns = ['Name','Contract Start Date','Contract End Date'])

df['Contract Start Date'] = pd.to_datetime(df['Contract Start Date'])
df['Contract End Date'] = pd.to_datetime(df['Contract End Date'])
date_range = pd.date_range('2019-01', '2019-08', freq='MS')

创建助手功能以查找有效合约

给出一个数据框,根据参考日期返回最近的合同(按开始日期)。在iloc语句中使用return是为了确保它始终返回pandas.Seriespandas.DataFrame

def get_active_contract(df, _date):
    temp_df = df.sort_values('Contract Start Date')
    ret_df = temp_df[temp_df['Contract Start Date'] <= _date]

    return ret_df.iloc[-1] if len(ret_df) > 1 else temp_df.iloc[0]

创建助手功能以查找保有权

给出一个数据框,找到有效合约(使用active_contract函数自上而下)。从此处开始,如果给定的日期在有效合同之间,则使用relativedelta.relativedelta基本包下的dateutil函数找到相对增量(以月为单位)。 +1用于调整计数权属不是零索引事务的事实(即,如果开始日期与当前日期相同,则不应将其计为0,而应计为1)。如果给定的日期在有效合同之外,则使用0。

def get_tenure(arg_df, current):
    srs = get_active_contract(arg_df, current)

    start = srs['Contract Start Date']
    end = srs['Contract End Date']
    name = srs['Name']
    if start <= current and current <= end:
        srs['tenure'] = relativedelta(current, start).months + 1

    else:
        srs['tenure'] = 0.0        

    return srs

创建表

步骤基本上是:

  • 浏览创建的date_range
  • 应用get_tenure函数并创建一个数据框列表,以显示参考日期和每个人在该日期的任期。
  • 通过串联所有上述数据帧来创建主数据帧(final_df)。
  • 枢轴final_df创建表。

使用df.copy只是出于安全考虑(不更改原始df)。

for date in date_range:
    temp_df = df.copy()
    temp_df = temp_df.groupby(['Name']).apply(
        get_tenure, current=date
    )

    temp_df['date'] = date.strftime('%Y-%m')

    df_list.append(temp_df)

final_df = pd.concat(df_list)
final_df.reset_index(drop=True, inplace=True)

pd.pivot_table(final_df, index=['Name'], columns=['date'], values=['tenure'])

答案 1 :(得分:0)

想法将新列的值转换为月份。

对于以下情况,首先需要对End列进行必要的预处理,以按条件按组每组的下一个先前值进行更改:

s = pd.to_datetime(df.groupby('Name')['Contract Start Date'].shift(-1)).dt.to_period('m')
m = s > pd.to_datetime(df['Contract Start Date']).dt.to_period('m')
df['End'] = df['Contract End Date'].mask(m, s-1)
print (df)
        Name Contract Start Date Contract End Date      End
0     Andrew             2019-03           2019-06  2019-06
1      Betty             2019-02           2019-07  2019-07
2  Charlotte             2019-01           2019-08  2019-03
3  Charlotte             2019-04           2019-06  2019-06

然后按组使用自定义功能:

def f(x):
    zipped = zip(x['Contract Start Date'], x['End'])
    L = []
    for y1, y2 in zipped:
        per = pd.period_range(y1, y2, freq='m')
        rng = range(1, len(per)+1)
        out = pd.Series(rng, index=per)
        L.append(out)
    return pd.concat(L).to_frame().T

df1 = (df.groupby('Name')['Contract Start Date','End'].apply(f)
        .reset_index(level=1, drop=True)
        .fillna(0)
        .astype(int)
        .reset_index()
       )
print (df1)
        Name  2019-01  2019-02  2019-03  2019-04  2019-05  2019-06  2019-07
0     Andrew        0        0        1        2        3        4        0
1      Betty        0        1        2        3        4        5        6
2  Charlotte        1        2        3        1        2        3        0