如何使用sql、python或r整合缓慢变化的维度表?

问题描述 投票:0回答:1

我有以下输入表:

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 有效地实现这一点?

-编辑-

最终输出代表一个历史表格,它使我能够返回到任何给定的一天来查看当天的状态。

在我的例子中,部门始终是财务 - 所以它是财务。 人数和位置已更改,因此出现了两行新内容。希望这是有道理的

sql pandas dplyr data.table tidyverse
1个回答
0
投票

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
© www.soinside.com 2019 - 2024. All rights reserved.