如何将dataframe重塑为三维numpy数组?

vlf7wbxs  于 2023-08-05  发布在  其他
关注(0)|答案(4)|浏览(196)

我的数据框包含每个用户ID的多变量时间序列。第一列id是用户id(有N个用户),第二列dt是日期(每个用户有T天的数据,即每个用户有T行),其他列是指标(基本上,每列是每个id的时间序列)。下面是重新创建类似 Dataframe 的代码

import pandas as pd
from datetime import datetime
import numpy as np

N=5
T=100

dfs=[]
datelist = pd.date_range(datetime.today(), periods=T).tolist()

for id in range(N):
    test = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
    test['dt'] = datelist
    test['id']=id
    dfs.append(test)

dfs = pd.concat(dfs)

字符串
输出结果如下所示,其中'A','B'等是指标(如总购买量):
| | C类|D级|dt|标识符| dt | id |
| --|--|--|--|--|--| ------------ |
| 五十八|三个|五十二|五个|2023-07-05 14:34:12.852460| 0个| 0 |
| 六个|三十四|二十八个|八十八个|2023-07-06 14:34:12.852460| 0个| 0 |
| 二十七|九十八|七十四|八十一|2023-07-07 14:34:12.852460| 0个| 0 |
| 九十六|十三个|七个|五十二|2023-07-08 14:34:12.852460| 0个| 0 |
| 八十|六十九|二十二|十二个|2023-07-09 14:34:12.852460| 0个| 0 |
我想把这个数据转换成一个numpy矩阵X,形状为N x T x F,其中N是用户数量,T是时间序列中的天数(T对所有id都是常数),F是度量的数量(在上面的例子中,F=4)。
这意味着X[0]返回一个TxF数组,它应该与dfs.query('id==0')[['A','B','C','D']].values的输出完全相同
到目前为止,我已经尝试使用pivotreshape,但最终矩阵中的元素并没有按照我想要的方式排列。以下是我尝试过的:

# Pivot the dataframe
df_pivot = dfs.sort_values(['id','dt']).pivot(index='id', columns='dt')

# Get the values from the pivot table
X = df_pivot.values.reshape(dfs['id'].nunique(), -1, len([x for x in dfs.columns if x not in ['dt','id']]))


如果我做X[0],结果是:

[58,  6, 27, 96],
[80, 65, 41, 39],
[30, 26, 38, 13],
[50, 60, 60, 73],
...


从中你可以看出结果不是我想要的。这就是我需要的:

[58,  3, 52,  5],
[ 6, 34, 28, 88],
[27, 98, 74, 81],
[96, 13,  7, 52],
...


任何帮助感谢!

vnjpjtjt

vnjpjtjt1#

注意,numpy数组将只包含类似形状的数组。
假设你可以这样做:

r = sum(df.id == 0)
c = df.shape[1] - 2
arr = df.drop(columns = ['dt', 'id']).values.reshape(-1, r, c)

arr[0]
array([[58,  3, 52,  5],
       [ 6, 34, 28, 88],
       [27, 98, 74, 81],
       [96, 13,  7, 52],
       [80, 69, 22, 12]])

字符串

xwmevbvl

xwmevbvl2#

按id对dataframe进行分组,然后为每个唯一的id将指标导出为列表解析中的numpy数组

df = df.set_index(['id', 'dt']).sort_index()
np.array([g.values for _, g in df.groupby(level=0)])

个字符

a5g8bdjr

a5g8bdjr3#

如果您100%确定每个id都有完全相同的T个时间戳(而且,我猜它们是相同的时间戳,否则就没有意义了),则可以简单地

dfs.sort_values(['id', 'dt']).drop(['id', 'dt'], axis=1).values.reshape(N, -1, F)

字符串
请注意,我假设您知道NF。如果您不这样做,也可以使用T。3个.reshape(N, T, F)中的一个可以被-1替换。
无论如何,您都可以计算它们,即使您拥有的只是dfs

F=len(dfs.columns)-2 # minus dt and id
N=len(dfs.id.unique())
T=len(dfs.dt.unique())


后两种方法的计算速度比前一种方法慢。因此,使用F以及NT中的一个来表示reshape,如果有,最好使用您事先知道的那个。

avkwfej4

avkwfej44#

我添加了第二个答案,而不是编辑我的第一个答案,因为这个答案与我以前的答案完全不同,而且不仅仅是它的改进。
我之前的一个替代答案,灵感来自this one-关于大致相同的问题,但对于具有索引的 Dataframe ;但你必须这样做才能进入同样的境地

np.moveaxis(dfs.set_index(['id', 'dt']).to_xarray().to_array().to_numpy(), 0, 2)

字符串
set_index发送给我们链接问题的情况。
to_xarray().to_array()是这个问题的答案。
因为它仍然不会返回numpy数组,所以to_numpy()会这样做。
然后,由于它返回一个索引轴在末尾的3d数组,并且您希望首先使用moveaxis(无论如何,moveaxis基本上是免费的)。它不移动任何数据)。

一些时间对比

| 定时| Timing |
| --| ------------ |
| 5.2毫秒| 5.2 ms |
| 2.02毫秒| 2.02 ms |
| 索引然后排序)5.0 ms| 5.0 ms |
但是当缩放一点时(没有你在评论中说的那么多,N= 300 k。但是仅N= 5 k),定时改变
| 定时| Timing |
| --| ------------ |
| 142毫秒| 142 ms |
| 165毫秒| 165 ms |
| 625毫秒| 625 ms |
(缩放更多,不会改变这些比率,因为过了一段时间,所有3个方法都是O(N))。
所以在一定的大小之后,这种方法的成本不会超过基于sort的方法。
Shubham's不能很好地扩展N轴,因为它是纯python复合列表(我还没有测试过,但我很确定它可以很好地沿着其他轴)

对缺失行的鲁棒性

它有一个巨大的优势:如果偶然地,其中一个id缺少一个时间戳,它仍然有效(用nan填充),而我的另一个答案以及Shubham的答案将失败,因为它们都依赖于每个id的行数相同。(我的sort方法,因为reshape假设正好有N×T×F值。或者,至少,用-1,因子T×F,但如果该因子不是N×T×F,则情况更糟:它看起来会起作用,但价值观会改变
因为np.array(...)在2d数组的数组上假设所有这些数组具有相同的形状。
当然,你说过是这样的。但是任何程序员在使用这种假设时都会感到不舒服。我们需要有点防御性。既然,无论如何,它不会花费更多,我会使用这种方法(我坚持“它不会花费更多”,因为我链接的旧帖子将其作为一种交易:成本更高,但更可靠;但是从那以后,就像其中一条评论中说的,xarray得到了改进。到了今天,这不是一个权衡。
准确地说,如果我说“it doesn’t cost more”而不是跳到“it cost less”,那是因为在我做的随机计时中,有几次(5月1/4日),我的sort/reshape方法是最快的。但在另一个方向上,我们有同样的10%的利润率。
所以现在,权衡不是关于时机。这是关于“需要100%肯定没有行丢失”与“需要一个包安装”(因为.to_xarray()需要一个pip install xarray才能工作)。

相关问题