我有一个数据集,该数据集描绘了一组人员的合同开始日期和结束日期,如下所示:
> 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'])
答案 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.Series
与pandas.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