将长格式面板数据重塑为宽堆叠时间序列

Posted

技术标签:

【中文标题】将长格式面板数据重塑为宽堆叠时间序列【英文标题】:Reshape long form panel data to wide stacked time series 【发布时间】:2020-10-01 11:43:04 【问题描述】:

我有以下形式的面板数据:

+--------+----------+------------+----------+
|        | user_id  | order_date |  values  |
+--------+----------+------------+----------+
| 0      | 11039591 | 2017-01-01 | 3277.466 |
| 1      | 25717549 | 2017-01-01 | 587.553  |
| 2      | 13629086 | 2017-01-01 | 501.882  |
| 3      | 3022981  | 2017-01-01 | 1352.546 |
| 4      | 6084613  | 2017-01-01 | 441.151  |
| ...    | ...      | ...        | ...      |
| 186415 | 17955698 | 2020-05-01 | 146.868  |
| 186416 | 17384133 | 2020-05-01 | 191.461  |
| 186417 | 28593228 | 2020-05-01 | 207.201  |
| 186418 | 29065953 | 2020-05-01 | 430.401  |
| 186419 | 4470378  | 2020-05-01 | 87.086   |
+--------+----------+------------+----------+

作为 Python 中的 Pandas DataFrame。

数据基本上是堆叠的时间序列数据;该表包含多个时间序列,对应于特定时间段内(2017/01 - 2020/05)内唯一用户的观察结果。该时期的覆盖水平在单个用户中可能非常低,这意味着如果您隔离单个时间序列,它们的长度都是不同的。

我想把这个长格式的面板数据转换成宽格式,这样每一列都是一天,每一行对应一个唯一的用户:

+----------+------------+------------+------------+------------+------------+
|          | 2017-01-01 | 2017-01-02 | 2017-01-03 | 2017-01-04 | 2017-01-05 |
+----------+------------+------------+------------+------------+------------+
| 11039591 | 3277.466   | 6482.722   | NaN        | NaN        | NaN        |
| 25717549 | 587.553    | NaN        | NaN        | NaN        | NaN        |
| 13629086 | 501.882    | NaN        | NaN        | NaN        | NaN        |
|  3022981 | 1352.546   | NaN        | NaN        | 557.728    | NaN        |
|  6084613 | 441.151    | NaN        | NaN        | NaN        | NaN        |
+----------+------------+------------+------------+------------+------------+

我一直在努力使用 unstack/pivot 或其他 Pandas 内置插件来解决这个问题:

ValueError: Index contains duplicate entries, cannot reshape

由于重复的用户 ID。

我目前的解决方案使用循环来索引各个时间序列并将它们连接在一起,因此它不可扩展 - 只有 180k 行它已经非常慢:

def time_series_stacker(df):

  ts = list()

  for user in df['user_id'].unique():

    values = df.loc[df['user_id']==user].drop('user_id', axis=1).T.values

    instance = pd.DataFrame(
        values[1,:].reshape(1,-1), 
        index=[user],
        columns=values[0,:].astype('datetime64[ns]')
    )

    ts.append(instance)

  return pd.concat(ts, axis=0)

有人可以帮忙更有效地重塑这个吗?

【问题讨论】:

【参考方案1】:

现在是试用pivot_table的最佳时机

    user_id  order_date    values
0  11039591  2017-01-01  3277.466
1  11039591  2017-01-02   587.553
2  13629086  2017-01-03   501.882
3  13629086  2017-01-02  1352.546
4   6084613  2017-01-01   441.151

df.pivot_table(index='user_id',columns='order_date',values='values')

输出

order_date  2017-01-01  2017-01-02  2017-01-03
user_id
6084613        441.151         NaN         NaN
11039591      3277.466     587.553         NaN
13629086           NaN    1352.546     501.882

【讨论】:

哈哈我意识到我刚刚在我的问题中准确地描述了数据透视表的功能 - 我想我没有考虑它,因为我自动将它们与聚合相关联,但如果我只有每天观察一次,那么 aggfunc 无关紧要。完美的答案,谢谢!

以上是关于将长格式面板数据重塑为宽堆叠时间序列的主要内容,如果未能解决你的问题,请参考以下文章

以特定方式转换为宽格式

在 Grafana 中,我们如何处理在图表/面板中返回 0 行的查询?

Mysql,重塑数据从长/高到宽

组合不同长度的长格式数据帧并转换为宽格式

熊猫从长到宽重塑,由两个变量

使用多个变量和一些时间不变将数据框从宽重塑为面板