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

时间:2016-01-27 20:59:57

标签: python pandas multidimensional-array multi-index

问题

有没有一种方法可以将带有 n -level索引的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()行来演示的。

非常感谢任何建议。

1 个答案:

答案 0 :(得分:6)

修改即可。这种方法比我下面给出的方法更优雅(并且快两个数量级)。

# 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.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))