将具有 n 级分层索引的 Pandas DataFrame 转换为 n-D Numpy 数组

Posted

技术标签:

【中文标题】将具有 n 级分层索引的 Pandas DataFrame 转换为 n-D Numpy 数组【英文标题】:Transform Pandas DataFrame with n-level hierarchical index into n-D Numpy array 【发布时间】:2016-05-05 00:36:46 【问题描述】:

问题

有没有一种好方法可以将具有 n 级索引的 DataFrame 转换为 n-D Numpy 数组(又名 n-张量)?


示例

假设我设置了一个类似的 DataFrame

from pandas import DataFrame, MultiIndex

index = range(2), range(3)
value = range(2 * 3)
frame = DataFrame(value, columns=['value'],
                  index=MultiIndex.from_product(index)).drop((1, 0))
print frame

哪个输出

     value
0 0      0
  1      1
  2      3
1 1      5
  2      6

该索引是一个 2 级分层索引。我可以使用

从数据中提取一个二维 Numpy 数组
print frame.unstack().values

哪个输出

[[  0.   1.   2.]
 [ nan   4.   5.]]

这如何推广到 n 级索引?

unstack(),好像只能用来按摩DataFrame的二维形状,不能加轴。

我不能使用例如frame.values.reshape(x, y, z),因为这将要求框架包含准确的 x * y * z 行,这是无法保证的。这就是我在上面的例子中通过drop()ing 一行来试图证明的。

非常感谢任何建议。

【问题讨论】:

“它是如何概括的”的答案是它没有。 pandas DataFrame 本质上是一个二维对象。正如您的示例所示,它不会在索引“维度”中强制执行相同的大小,因此如果您尝试将其扩展到更多维度,则可能存在差距。我认为,如果您想获得一个 n-D 数组,您可能必须自己通过迭代索引级别并为每个索引级别创建一个单独的结果数组“切片”来制作它。 Pandas 并不针对这种结构。 谢谢@Bren。我设法解决了丢失行的问题并使用了reshape()(见下文)。这似乎适用于我的数据集,尽管如果出现阻塞的情况我不会感到惊讶。 【参考方案1】:

编辑。这种方法比我在下面给出的方法更优雅(并且快两个数量级)。

# create an empty array of NaN of the right dimensions
shape = map(len, frame.index.levels)
arr = np.full(shape, np.nan)

# fill it using Numpy's advanced indexing
arr[frame.index.codes] = frame.values.flat
# ...or in Pandas < 0.24.0, use
# arr[frame.index.labels] = frame.values.flat

原始解决方案。给定与上述类似的设置,但在 3-D 中,

from pandas import DataFrame, MultiIndex
from itertools import product

index = range(2), range(2), range(2)
value = range(2 * 2 * 2)
frame = DataFrame(value, columns=['value'],
                  index=MultiIndex.from_product(index)).drop((1, 0, 1))
print(frame)

我们有

       value
0 0 0      0
    1      1
  1 0      2
    1      3
1 0 0      4
  1 0      6
    1      7

现在,我们继续使用reshape() 路线,但进行了一些预处理以确保沿每个维度的长度保持一致。

首先,使用所有维度的完整笛卡尔积重新索引数据框。 NaN 值将根据需要插入。此操作可能既慢又消耗大量内存,具体取决于维度数和数据框的大小。

levels = map(tuple, frame.index.levels)
index = list(product(*levels))
frame = frame.reindex(index)
print(frame)

哪个输出

       value
0 0 0      0
    1      1
  1 0      2
    1      3
1 0 0      4
    1    NaN
  1 0      6
    1      7

现在,reshape() 将按预期工作。

shape = map(len, frame.index.levels)
print(frame.values.reshape(shape))

哪个输出

[[[  0.   1.]
  [  2.   3.]]

 [[  4.  nan]
  [  6.   7.]]]

(相当丑陋的)单线是

frame.reindex(list(product(*map(tuple, frame.index.levels)))).values\
     .reshape(map(len, frame.index.levels))

【讨论】:

效果很好!有一个小错字:frame.reindex(levels) 应该是 frame.reindex(index) 对于我们新手来说;不要忘记,在 python3 中,您需要将“map”的结果转换为列表,然后才能使用。 IE。 shape = list(map(len, frame.index.levels)) 获得形状更直接:frame.index.levshape。这和给定的解决方案似乎都不适用于非唯一索引。 df.index.labels 我得到AttributeError: 'MultiIndex' object has no attribute 'labels'。那是怎么回事? @CrabMan 很晚的响应,但 MultiIndex.labels 已被弃用,取而代之的是 MultiIndex.codes - 使用后者应该可以修复错误。 (pandas-docs.github.io/pandas-docs-travis/whatsnew/…)【参考方案2】:

这可以使用 Python xarray 包很好地完成,可以在这里找到:http://xarray.pydata.org/en/stable/。它与 Pandas 有很好的集成,一旦你掌握它就会非常直观。

如果您有一个多索引系列,您可以调用内置方法 multiindex_series.to_xarray() (https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_xarray.html)。这将生成一个 DataArray 对象,它本质上是一个名称索引的 numpy 数组,使用索引值和名称作为坐标。在此之后,您可以调用 DataArray 对象上的 .values 来获取底层的 numpy 数组。

如果您需要您的张量以特定顺序符合一组键,您还可以在 DataArray 上调用 .reindex(index_name = index_values_in_order) (http://xarray.pydata.org/en/stable/generated/xarray.DataArray.reindex.html)。这非常有用,并且可以更轻松地处理新生成的张量!

【讨论】:

以上是关于将具有 n 级分层索引的 Pandas DataFrame 转换为 n-D Numpy 数组的主要内容,如果未能解决你的问题,请参考以下文章

从具有复合(分层)索引的 Pandas 数据框中选择行

Pandas:使用循环和分层索引将多个 csv 文件导入数据帧

如何使用分层索引保存和检索 Pandas 数据帧?

pandas索引操作

Pandas + Scikit 学习:分层 k 折问题

Pandas-分组函数和分层索引的展开