我有以下输入表:
id | 类型 | 价值 | 日期_来自 | 日期_至 |
---|---|---|---|---|
1 | 部门 | 金融 | 2020-01-01 | 9999-12-31 |
1 | 人数 | 10 | 2020-01-01 | 2020-02-03 |
1 | 人数 | 15 | 2020-02-04 | 9999-12-31 |
1 | 地点 | 直流电 | 2020-01-01 | 2020-01-21 |
1 | 地点 | 纽约 | 2020-01-22 | 9999-12-31 |
我想将其转换为包含所有“类型”字段作为列的宽表:
id | 部门 | 人数 | 地点 | 日期_来自 | 日期_至 |
---|---|---|---|---|---|
1 | 金融 | 10 | 直流电 | 2020-01-01 | 2020-01-21 |
1 | 金融 | 10 | 纽约 | 2020-01-22 | 2020-02-03 |
1 | 金融 | 15 | 纽约 | 2020-02-04 | 9999-12-31 |
注意到数据中有不同的“id”和未知的“type”。如何使用 sql、r 或 python 有效地实现这一点?
-编辑-
最终输出代表一个历史表格,它使我能够返回到任何给定的一天来查看当天的状态。
在我的例子中,部门始终是财务 - 所以它是财务。 人数和位置已更改,因此出现了两行新内容。希望这是有道理的
IIUC,这是一种方法:
pd.to_datetime
将列“date_from”更改为日期时间值。df.pivot
,链接 df.ffill
和 df.reset_index
。Series.ne
和 Series.shift
区分列“id”中的组,并使用“date_from”的移位版本减去 1 天(使用 pd.DataOffset
)重建列“date_to”,我们将 Series.mask
应用于其中命令为每组添加最终的未来日期。df.assign
添加重建的“date_to”,并使用 df.loc
按顺序获取列。请注意,“9999-12-31”远高于可接受的最大时间戳(参见
pd.Timestamp.max
,移动目标)。例如,我们输入“2200-12-31”。
代码
# based on adjusted `df` below
import pandas as pd
df['date_from'] = pd.to_datetime(df['date_from'], format='%Y-%m-%d')
out = (
df.pivot(index=['id', 'date_from'], columns=['type'], values='value')
.ffill()
.reset_index()
)
cond = out['id'].ne(out['id'].shift(-1))
cols = ['id'] + df['type'].unique().tolist() + ['date_from', 'date_to']
out = (
out.assign(
date_to=(out['date_from'] - pd.DateOffset(days=1))
.shift(-1)
.mask(cond=cond, other=pd.Timestamp('2200-12-31'))
)
.loc[:, cols]
)
输出
id department headcount location date_from date_to
0 1 finance 10 DC 2020-01-01 2020-01-21
1 1 finance 10 NY 2020-01-22 2020-02-03
2 1 finance 15 NY 2020-02-04 2200-12-31
3 2 marketing 10 DC 2020-01-01 2020-01-21
4 2 marketing 10 NY 2020-01-22 2020-02-03
5 2 marketing 15 NY 2020-02-04 2200-12-31
使用的数据
添加另一个具有不同部门的“id”:
import pandas as pd
data = {
'id': {0: 1, 1: 1, 2: 1, 3: 1, 4: 1},
'type': {0: 'department', 1: 'headcount', 2: 'headcount',
3: 'location', 4: 'location'},
'value': {0: 'finance', 1: '10', 2: '15', 3: 'DC', 4: 'NY'},
'date_from': {0: '2020-01-01', 1: '2020-01-01', 2: '2020-02-04',
3: '2020-01-01', 4: '2020-01-22'},
'date_to': {0: '9999-12-31', 1: '2020-02-03', 2: '9999-12-31',
3: '2020-01-21', 4: '9999-12-31'}
}
df = pd.DataFrame(data)
df = pd.concat([df, df.assign(id=2, value=df.value.replace('finance','marketing'))],
ignore_index=True)
df
id type value date_from date_to
0 1 department finance 2020-01-01 9999-12-31
1 1 headcount 10 2020-01-01 2020-02-03
2 1 headcount 15 2020-02-04 9999-12-31
3 1 location DC 2020-01-01 2020-01-21
4 1 location NY 2020-01-22 9999-12-31
5 2 department marketing 2020-01-01 9999-12-31
6 2 headcount 10 2020-01-01 2020-02-03
7 2 headcount 15 2020-02-04 9999-12-31
8 2 location DC 2020-01-01 2020-01-21
9 2 location NY 2020-01-22 9999-12-31