将Pandas Dataframe或Panel转换为3D numpy数组

7 投票
4 回答
16802 浏览
提问于 2025-04-18 05:27

设置:

pdf = pd.DataFrame(np.random.rand(4,5), columns = list('abcde'))
pdf['a'][2:]=pdf['a'][0]
pdf['a'][:2]=pdf['a'][1]
pdf.set_index(['a','b'])

输出:

                         c           d           e
a           b           
0.439502    0.115087     0.832546    0.760513    0.776555
            0.609107     0.247642    0.031650    0.727773
0.995370    0.299640     0.053523    0.565753    0.857235
            0.392132     0.832560    0.774653    0.213692

每组数据都是根据索引 ID a 来分组的,而 b 则表示与 a 相关的时间索引。有没有办法让 pandas 生成一个 numpy 的三维数组,以反映 a 的分组情况?目前它读取的数据是二维的,所以 pdf.shape 输出的是 (4, 5)。我希望得到的数组是这样的形式:

array([[[-1.38655912, -0.90145951, -0.95106951,  0.76570984],
        [-0.21004144, -2.66498267, -0.29255182,  1.43411576],
        [-0.21004144, -2.66498267, -0.29255182,  1.43411576]],

       [[ 0.0768149 , -0.7566995 , -2.57770951,  0.70834656],
        [-0.99097395, -0.81592084, -1.21075386,  0.12361382]]])

有没有 pandas 自带的方法可以做到这一点?请注意,实际数据中每个 a 分组的行数是变化的,所以我不能简单地转置或重塑 pdf.values。如果没有自带的方法,针对成千上万行和几百列的数据,构建这些数组的最佳方法是什么?

4 个回答

0

以前我们用过的 .as_matrix.values() 这些方法已经不再推荐使用了。现在,pandas 的文档建议我们使用 .to_numpy() 这个方法。

'警告: 我们建议使用 DataFrame.to_numpy() 代替。'

3

as_matrix 这个方法已经不再推荐使用了。这里我们假设第一个关键字是 a,那么 a 中的组可能有不同的长度,这个方法可以解决所有相关的问题。

import pandas as pd
import numpy as np
from typing import List

def make_cube(df: pd.DataFrame, idx_cols: List[str]) -> np.ndarray:
    """Make an array cube from a Dataframe

    Args:
        df: Dataframe
        idx_cols: columns defining the dimensions of the cube

    Returns:
        multi-dimensional array
    """
    assert len(set(idx_cols) & set(df.columns)) == len(idx_cols), 'idx_cols must be subset of columns'

    df = df.set_index(keys=idx_cols)  # don't overwrite a parameter, thus copy!
    idx_dims = [len(level) + 1 for level in df.index.levels]
    idx_dims.append(len(df.columns))

    cube = np.empty(idx_dims)
    cube.fill(np.nan)
    cube[tuple(np.array(df.index.to_list()).T)] = df.values

    return cube

测试:


pdf = pd.DataFrame(np.random.rand(4,5), columns = list('abcde'))
pdf['a'][2:]=pdf['a'][0]
pdf['a'][:2]=pdf['a'][1]

# a, b must be integer 
pdf1 = (pdf.assign(a=lambda df: df.groupby(['a']).ngroup())
.assign(b=lambda df: df.groupby(['a'])['b'].cumcount())
)

make_cube(pdf1, ['a', 'b']).shape

给出: (2, 2, 3)


pdf = pd.DataFrame(np.random.rand(5,5), columns = list('abcde'))
pdf['a'][2:]=pdf['a'][0]
pdf['a'][:2]=pdf['a'][1]

pdf1 = (pdf.assign(a=lambda df: df.groupby(['a']).ngroup())
.assign(b=lambda df: df.groupby(['a'])['b'].cumcount())
)

make_cube(pdf1, ['a', 'b']).shape

给出 s (2, 3, 3) 。

21

我刚遇到一个非常相似的问题,解决方法是这样的:

a3d = np.array(list(pdf.groupby('a').apply(pd.DataFrame.as_matrix)))

输出结果:

array([[[ 0.47780308,  0.93422319,  0.00526572,  0.41645868,  0.82089215],
    [ 0.47780308,  0.15372096,  0.20948369,  0.76354447,  0.27743855]],

   [[ 0.75146799,  0.39133973,  0.25182206,  0.78088926,  0.30276705],
    [ 0.75146799,  0.42182369,  0.01166461,  0.00936464,  0.53208731]]])

确认它是三维的,a3d.shape 显示为 (2, 2, 5)。

最后,如果想把新创建的维度放在最后一位(而不是第一位),可以使用:

a3d = np.dstack(list(pdf.groupby('a').apply(pd.DataFrame.as_matrix)))

这样就变成了 (2, 5, 2) 的形状


对于数据不规则的情况(正如评论中提到的),如果你想继续使用numpy的解决方案,可以尝试以下方法。但要注意,处理缺失数据的最佳策略因情况而异。在这个例子中,我们简单地为缺失的行添加了零。

不规则形状的示例设置:

pdf = pd.DataFrame(np.random.rand(5,5), columns = list('abcde'))
pdf['a'][2:]=pdf['a'][0]
pdf['a'][:2]=pdf['a'][1]
pdf.set_index(['a','b'])

数据框:

                        c           d           e
a           b           
0.460013    0.577535    0.299304    0.617103    0.378887
            0.167907    0.244972    0.615077    0.311497
0.318823    0.640575    0.768187    0.652760    0.822311
            0.424744    0.958405    0.659617    0.998765
            0.077048    0.407182    0.758903    0.273737

一种可能的解决方案:

n_max = pdf.groupby('a').size().max()
a3d = np.array(list(pdf.groupby('a').apply(pd.DataFrame.as_matrix)
                    .apply(lambda x: np.pad(x, ((0, n_max-len(x)), (0, 0)), 'constant'))))

a3d.shape 显示为 (2, 3, 5)

2
panel.values

这段代码会直接返回一个numpy数组。这个数组的类型会是能接受的最高级别,因为所有的数据都被压缩成一个三维的numpy数组。这个数组是一个全新的数组,而不是pandas数据的一个视图(无论它的数据类型是什么)。

撰写回答