# 7.8 分层索引

> 原文：[Hierarchical Indexing](http://nbviewer.jupyter.org/github/donnemartin/data-science-ipython-notebooks/blob/master/pandas/03.05-Hierarchical-Indexing.ipynb)
> 
> 译者：[飞龙](https://github.com/wizardforcel)
> 
> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
> 
> 本节是[《Python 数据科学手册》](https://github.com/jakevdp/PythonDataScienceHandbook)（Python Data Science Handbook）的摘录。

到目前为止，我们主要关注一维和二维数据，分别存储在 Pandas ``Series``和``DataFrame``对象中。通常，超出此范围并存储更高维度的数据（即由多于一个或两个键索引的数据）是有用的。

虽然 Pandas 确实提供了``Panel``和``Panel4D``对象，这些对象原生地处理三维和四维数据（参见“旁注：面板数据”），实践中的更常见模式是利用分层索引（也称为多重索引），在单个索引中合并多个索引层次。通过这种方式，可以在熟悉的一维`Series`和二维`DataFrame`对象中，紧凑地表示高维数据。

在本节中，我们将探索``MultiIndex``对象的直接创建，在对多重索引数据执行索引，切片和计算统计数据时的注意事项，以及在数据的简单和分层索引表示之间进行转换的有用例程。

我们以标准导入开始：

```py
import pandas as pd
import numpy as np
```

## 多重索引的序列

让我们首先考虑如何在一维`Series`中表示二维数据。具体而言，我们将考虑数据序列，其中每个点都有一个字符和数字键。

### 不好的方式

假设你想跟踪两个不同年份的州的数据。使用我们已经介绍过的 Pandas 工具，你可能只想使用 Python 元组作为键：

```py
index = [('California', 2000), ('California', 2010),
         ('New York', 2000), ('New York', 2010),
         ('Texas', 2000), ('Texas', 2010)]
populations = [33871648, 37253956,
               18976457, 19378102,
               20851820, 25145561]
pop = pd.Series(populations, index=index)
pop

'''
(California, 2000)    33871648
(California, 2010)    37253956
(New York, 2000)      18976457
(New York, 2010)      19378102
(Texas, 2000)         20851820
(Texas, 2010)         25145561
dtype: int64
'''
```

使用此索引方案，你可以根据这个多重索引，直接对序列索引或切片：

```py
pop[('California', 2010):('Texas', 2000)]

'''
(California, 2010)    37253956
(New York, 2000)      18976457
(New York, 2010)      19378102
(Texas, 2000)         20851820
dtype: int64
'''
```

但也就这样了。 例如，如果你需要选择 2010 年的所有值，则需要进行一些混乱（并且可能很慢）的调整来实现它：

```py
pop[[i for i in pop.index if i[1] == 2010]]

'''
(California, 2010)    37253956
(New York, 2010)      19378102
(Texas, 2010)         25145561
dtype: int64
'''
```

这会产生所需的结果，但不像我们所喜欢的 Pandas 中的切片语法那样干净（或对大型数据集有效）。

### 更好的方式：Pandas `MultiIndex`

幸运的是，Pandas 提供了一种更好的方式。
我们的基于元组的索引，本质上是一个基本的多重索引，而 Pandas 的``MultiIndex``类型为我们提供了我们希望拥有的操作类型。我们可以从元组创建多重索引，如下所示：

```py
index = pd.MultiIndex.from_tuples(index)
index

'''
MultiIndex(levels=[['California', 'New York', 'Texas'], [2000, 2010]],
           labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]])
'''
```

请注意，``MultiIndex``包含索引的多个层次 - 在这种情况下，州名称和年份，以及编码这些层次的，每个数据点的多个标签。

如果我们用这个``MultiIndex``重新索引我们的序列，我们会看到数据的分层表示：

```py
pop = pop.reindex(index)
pop

'''
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64
'''
```

这里``Series``表示的前两列展示了多个索引值，而第三列展示数据。请注意，第一列中缺少某些条目：在多重索引表示中，任何空白条目都表示与其上方的行相同的值。

现在来访问第二个索引是 2010 的所有数据，我们可以简单地使用 Pandas 切片表示法：

```py
pop[:, 2010]

'''
California    37253956
New York      19378102
Texas         25145561
dtype: int64
'''
```

结果是一个单值索引的数组，只带有我们感兴趣的键。与我们开始使用的自制的基于元组的多重索引解决方案相比，这种语法更方便（并且操作更加高效！）。我们现在将进一步讨论分层索引数据上的这种索引操作。

### 作为额外维度的`MultiIndex`

你可能会注意到其他内容：我们可以使用带有索引和列标签的简单`DataFrame`，来轻松存储相同的数据。事实上，Pandas 的构建具有这种等价关系。 ``unstack()``方法会快速将多重索引的``Series``转换为常规索引的``DataFrame``：

```py
pop_df = pop.unstack()
pop_df
```

|  | 2000 | 2010 |
| --- | --- | --- |
| California | 33871648 | 37253956 |
| New York | 18976457 | 19378102 |
| Texas | 20851820 | 25145561 |

当然，``stack()``方法提供了相反的操作：

```py
pop_df.stack()

'''
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64
'''
```
看到这个，你可能想知道为什么我们会纠结于分层索引。原因很简单：就像我们能够使用多重索引，在一维序列中表示二维数据一样，我们也可以用它在``Series``或``DataFrame``中表示更多维的数据。

多重索引中的每个额外层次表示数据的额外维度；利用这个属性，我们可以更灵活地处理我们可以表示的数据类型。具体而言，我们可能希望，每年为每个州添加另一列人口统计数据（例如，18 岁以下的人口）; 使用``MultiIndex``就像在``DataFrame``中添加另一列一样简单：

```py
pop_df = pd.DataFrame({'total': pop,
                       'under18': [9267089, 9284094,
                                   4687374, 4318033,
                                   5906301, 6879014]})
pop_df
```

|  |  | total | under18 |
| --- | --- | --- | --- |
| California | 2000 | 33871648 | 9267089 |
| 2010 | 37253956 | 9284094 |
| New York | 2000 | 18976457 | 4687374 |
| 2010 | 19378102 | 4318033 |
| Texas | 2000 | 20851820 | 5906301 |
| 2010 | 25145561 | 6879014 |

此外，“Pandas 中的数据操作”中讨论的所有`ufunc`和其他功能也适用于分层索引。根据以上数据，我们在这里按年计算 18 岁以下人口的比例：

```py
f_u18 = pop_df['under18'] / pop_df['total']
f_u18.unstack()
```

|  | 2000 | 2010 |
| --- | --- | --- |
| California | 0.273594 | 0.249211 |
| New York | 0.247010 | 0.222831 |
| Texas | 0.283251 | 0.273568 |

这使我们能够轻松快速地操作和探索高维数据。

## `MultiIndex`的创建方法

为``Series``或``DataFrame``构造多重索引的最简单方法，是简单地将两个或多个索引数组的列表传递给构造器。 例如：

```py
df = pd.DataFrame(np.random.rand(4, 2),
                  index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
                  columns=['data1', 'data2'])
df
```

|  |  | data1 | data2 |
| --- | --- | --- | --- |
| a | 1 | 0.554233 | 0.356072 |
| 2 | 0.925244 | 0.219474 |
| b | 1 | 0.441759 | 0.610054 |
| 2 | 0.171495 | 0.886688 |

创建``MultiIndex``的工作是在后台完成的。

类似地，如果你传递一个带有适当元组作为键的字典，Pandas 会自动识别它并默认使用`MultiIndex`：

```py
data = {('California', 2000): 33871648,
        ('California', 2010): 37253956,
        ('Texas', 2000): 20851820,
        ('Texas', 2010): 25145561,
        ('New York', 2000): 18976457,
        ('New York', 2010): 19378102}
pd.Series(data)

'''
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64
'''
```

然而，显式创建一个``MultiIndex``有时很有用；我们在这里会看到其中的几种方法。

### `MultiIndex`显式构造器

为了更灵活地构造索引，你可以使用``pd.MultiIndex``中提供的类方法构造器。例如，正如我们之前所做的那样，你可以从一个简单的数组列表中构造``MultiIndex``，提供每个层次中的索引值：

```py
pd.MultiIndex.from_arrays([['a', 'a', 'b', 'b'], [1, 2, 1, 2]])

'''
MultiIndex(levels=[['a', 'b'], [1, 2]],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
'''
```

你可以从元组列表构造它，提供每个点的多重索引值：

```py
pd.MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1), ('b', 2)])

'''
MultiIndex(levels=[['a', 'b'], [1, 2]],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
'''
```

你甚至可以从单个索引的笛卡尔积中构造它：

```py
pd.MultiIndex.from_product([['a', 'b'], [1, 2]])

'''
MultiIndex(levels=[['a', 'b'], [1, 2]],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
'''
```

类似地，你可以通过传递``levels``（列表的列表，包含每个级别的可用索引值）和``labels``（引用这些标签的列表的列表），直接使用其内部编码构造``MultiIndex``：

```py
pd.MultiIndex(levels=[['a', 'b'], [1, 2]],
              labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
              
'''
MultiIndex(levels=[['a', 'b'], [1, 2]],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
'''
```

当创建``Series``或``Dataframe``时，这些对象中的任何一个都可以作为``index``参数传递，或者传递给现有``Series``或``DataFrame``的``reindex``方法。

### `MultiIndex`层次名称

有时命名`MultiIndex`的层次很方便。
这可以通过将``names``参数传递给上述任何一个``MultiIndex``构造器，或者通过在事后设置索引的``names``属性来实现：

```py
pop.index.names = ['state', 'year']
pop

'''
state       year
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64
'''
```

在更复杂数据集的情况下，这可能是跟踪各种索引值含义的有用方法。

### 列的`MultiIndex`

在``DataFrame``中，行和列是完全对称的，就像行可以有多个索引层次一样，列也可以有多个层次。考虑以下内容，这是一些（有些现实的）医学数据的模型：

```py
# 分层索引和列
index = pd.MultiIndex.from_product([[2013, 2014], [1, 2]],
                                   names=['year', 'visit'])
columns = pd.MultiIndex.from_product([['Bob', 'Guido', 'Sue'], ['HR', 'Temp']],
                                     names=['subject', 'type'])

# 生成一些数据
data = np.round(np.random.randn(4, 6), 1)
data[:, ::2] *= 10
data += 37

# 创建数据帧
health_data = pd.DataFrame(data, index=index, columns=columns)
health_data
```

|  | subject | Bob | Guido | Sue |
| --- | --- | --- | --- | --- | --- | --- | --- |
|  | type | HR | Temp | HR | Temp | HR | Temp |
| year | visit|  |  |  |  |  | |
| 2013 | 1 | 31.0 | 38.7 | 32.0 | 36.7 | 35.0 | 37.2 |
| | 2 | 44.0 | 37.7 | 50.0 | 35.0 | 29.0 | 36.7 |
| 2014 | 1 | 30.0 | 37.4 | 39.0 | 37.8 | 61.0 | 36.9 |
| | 2 | 47.0 | 37.8 | 48.0 | 37.3 | 51.0 | 36.5 |

在这里，我们看到行和列的多重索引可以非常方便。这基本上是四维数据，其中维度是受试者，测量类型，年份和就诊次数。有了这个，我们就可以通过人名来索引顶级列，并得到一个完整的``DataFrame``，其中只包含该人的信息：

```py
health_data['Guido']
```

|  | type | HR | Temp |
| --- | --- | --- | --- |
| year | visit|  | |
| 2013 | 1 | 32.0 | 36.7 |
| | 2 | 50.0 | 35.0 |
| 2014 | 1 | 39.0 | 37.8 |
| | 2 | 48.0 | 37.3 |

对于一些复杂记录，它包含多个标记的测量值，并多次跨越许多受试者（人，国家，城市等），使用分层的行和列非常方便！

## `MultiIndex`的索引和切片

`MultiIndex`上的索引和切片设计得很直观，如果你将索引视为添加的维度，它会有所帮助。我们首先看一下多重索引的``Series``的索引，然后是多重索引的``DataFrame``。

### 多重索引的序列

考虑一下我们之前看到的，州人口的多重索引的序列：

```py
pop

'''
state       year
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64
'''
```

我们可以通过索引多项来访问单个元素：

```py
pop['California', 2000]

# 33871648
```

``MultiIndex``也支持部分索引，或仅索引索引中的一个层次。结果是另一个``Series``，持有较低层次的索引：

```py
pop['California']

'''
year
2000    33871648
2010    37253956
dtype: int64
'''
```

`MultiIndex`是有序的，就可以执行部分切片（参见“排序和未排序索引”中的讨论）：

```py
pop.loc['California':'New York']

'''
state       year
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
dtype: int64
'''
```

对于有序索引，可以通过在第一个索引中传递空切片，来在较低层次上执行部分索引：

```py
pop[:, 2000]

'''
state
California    33871648
New York      18976457
Texas         20851820
dtype: int64
'''
```

其他类型的索引和选择（在“数据索引和选择”中讨论）也可以使用；例如，基于布尔掩码的选择：

```py
pop[pop > 22000000]

'''
state       year
California  2000    33871648
            2010    37253956
Texas       2010    25145561
dtype: int64
'''
```

基于花式索引的选择也有效：

```py
pop[['California', 'Texas']]

'''
state       year
California  2000    33871648
            2010    37253956
Texas       2000    20851820
            2010    25145561
dtype: int64
'''
```

### 多重索引数据帧

多重索引的`DataFrame`的表现类似。考虑我们之前的玩具医疗``DataFrame``：

```py
health_data
```

|  | subject | Bob | Guido | Sue |
| --- | --- | --- | --- | --- | --- | --- | --- |
|  | type | HR | Temp | HR | Temp | HR | Temp |
| year | visit|  |  |  |  |  | |
| 2013 | 1 | 31.0 | 38.7 | 32.0 | 36.7 | 35.0 | 37.2 |
| | 2 | 44.0 | 37.7 | 50.0 | 35.0 | 29.0 | 36.7 |
| 2014 | 1 | 30.0 | 37.4 | 39.0 | 37.8 | 61.0 | 36.9 |
| | 2 | 47.0 | 37.8 | 48.0 | 37.3 | 51.0 | 36.5 |

请记住，列在``DataFrame``中是主要的，并且用于多重索引的``Series``的语法适用于列。例如，我们可以通过简单的操作恢复 Guido 的心率数据：

```py
health_data['Guido', 'HR']

'''
year  visit
2013  1        32.0
      2        50.0
2014  1        39.0
      2        48.0
Name: (Guido, HR), dtype: float64
'''
```

此外，与单值索引情况一样，我们可以使用“数据索引和选择”中介绍的``loc``，``iloc``和``ix``索引器。例如：

```py
health_data.iloc[:2, :2]
```

|  | subject | Bob |
| --- | --- | --- | --- |
|  | type | HR | Temp |
| year | visit|  | |
| 2013 | 1 | 31.0 | 38.7 |
| | 2 | 44.0 | 37.7 |

这些索引器提供了底层二维数据的数组式的视图，但是可以向``loc``或``iloc``中的每个索引器，传递多个索引的元组。例如：

```py
health_data.loc[:, ('Bob', 'HR')]

'''
year  visit
2013  1        31.0
      2        44.0
2014  1        30.0
      2        47.0
Name: (Bob, HR), dtype: float64
'''
```

在这些索引元组中使用切片并不是特别方便；尝试在元组中创建切片将导致语法错误：

```py
health_data.loc[(:, 1), (:, 'HR')]

'''
  File "<ipython-input-32-8e3cc151e316>", line 1
    health_data.loc[(:, 1), (:, 'HR')]
                     ^
SyntaxError: invalid syntax
'''
```

你可以通过使用 Python 的内置``slice()``函数，显式构建所需的切片，来解决这个问题，但在这种情况下，更好的方法是使用``IndexSlice``对象，正是由 Pandas 为这种情况提供的。

例如：

```py
idx = pd.IndexSlice
health_data.loc[idx[:, 1], idx[:, 'HR']]
```

|  | subject | Bob | Guido | Sue |
| --- | --- | --- | --- | --- |
|  | type | HR | HR | HR |
| year | visit|  |  | |
| 2013 | 1 | 31.0 | 32.0 | 35.0 |
| 2014 | 1 | 30.0 | 39.0 | 61.0 |

有很多方法可以在多重索引的``Series``和``DataFrame`中与数据进行交互，就像本书中的许多工具一样，熟悉它们的最好方法就是尝试它们！

## 重排多重索引

处理多重索引数据的关键之一，是知道如何有效地转换数据。有许多操作将保留数据集中的所有信息，但为了各种计算的目的重新排列它。

我们在``stack()``和``unstack()``方法中看到了一个简短的例子，但是还有很多方法，可以精确控制分层索引和列之间的数据重排，在这里我们将探索他们。

### 排序和未排序索引

早些时候，我们简要地提到了一个警告，但我们应该在这里强调一下。如果索引未排序，多数``MultiIndex``切片操作将失败。在这里我们来看看。

我们首先创建一些简单的多重索引数据，其中索引不是按字母顺序排序：

```py
index = pd.MultiIndex.from_product([['a', 'c', 'b'], [1, 2]])
data = pd.Series(np.random.rand(6), index=index)
data.index.names = ['char', 'int']
data

'''
char  int
a     1      0.003001
      2      0.164974
c     1      0.741650
      2      0.569264
b     1      0.001693
      2      0.526226
dtype: float64
'''
```

如果我们尝试对此索引进行部分切片，则会导致错误：

```py
try:
    data['a':'b']
except KeyError as e:
    print(type(e))
    print(e)

'''
<class 'KeyError'>
'Key length (1) was greater than MultiIndex lexsort depth (0)'
'''
```

虽然从错误消息中并不完全清楚，但这是`MultiIndex`未排序的结果。由于各种原因，部分切片和其他类似操作要求`MultiIndex`中的层次是（按字母顺序）排序的。

Pandas 提供了许多便利的例程来执行这种排序；例如``DataFrame``的``sort_index()``和``sortlevel()``方法。我们这里将使用最简单的``sort_index()``：

```py
data = data.sort_index()
data

'''
char  int
a     1      0.003001
      2      0.164974
b     1      0.001693
      2      0.526226
c     1      0.741650
      2      0.569264
dtype: float64
'''
```

通过以这种方式排序索引，部分切片将按预期工作：

```py
data['a':'b']

'''
char  int
a     1      0.003001
      2      0.164974
b     1      0.001693
      2      0.526226
dtype: float64
'''
```

### 索引堆叠和解除堆叠

正如我们之前简要介绍的那样，可以将数据集从堆叠的多索引转换为简单的二维表示，可选择指定要使用的层次：

```py
pop.unstack(level=0)
```

| state | California | New York | Texas |
| --- | --- | --- | --- |
| year|  |  | |
| 2000 | 33871648 | 18976457 | 20851820 |
| 2010 | 37253956 | 19378102 | 25145561 |

```py
pop.unstack(level=1)
```

| year | 2000 | 2010 |
| --- | --- | --- |
| state|  | |
| California | 33871648 | 37253956 |
| New York | 18976457 | 19378102 |
| Texas | 20851820 | 25145561 |

``unstack()``的反面是``stack()``，这里可以用来恢复原始序列：

```py
pop.unstack().stack()

'''
state       year
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64
'''
```

### 索引设置和重设

重排分层数据的另一种方法是将索引标签转换为列；这可以通过``reset_index``方法完成。
在人口字典上调用它将产生一个带有`state`和`year`列的``DataFrame``，包含以前在索引中的信息。
为清楚起见，我们可以为列表示指定数据名称：

```py
pop_flat = pop.reset_index(name='population')
pop_flat
```

|  | state | year | population |
| --- | --- | --- | --- |
| 0 | California | 2000 | 33871648 |
| 1 | California | 2010 | 37253956 |
| 2 | New York | 2000 | 18976457 |
| 3 | New York | 2010 | 19378102 |
| 4 | Texas | 2000 | 20851820 |
| 5 | Texas | 2010 | 25145561 |

通常处理现实世界中的数据时，原始输入数据看起来像这样，从列值构建`MultiIndex`会有用。这可以通过``DataFrame``的``set_index``方法完成，它返回一个多重索引的``DataFrame``：

```py
pop_flat.set_index(['state', 'year'])
```

|  |  | population |
| --- | --- | --- |
| state | year| |
| | California | 2000 | 33871648 |
| 2010 | 37253956 |
| | New York | 2000 | 18976457 |
| 2010 | 19378102 |
| | Texas | 2000 | 20851820 |
| 2010 | 25145561 |

在实践中，遇到真实数据集时，我发现这种类型的重索引，是更有用的模式之一。

## 多重索引上的数据聚合

我们以前看到，Pandas 有内置的数据聚合方法，比如`mean()``，`sum()``和``max()``。对于分层索引数据，可以传递``level``参数，该参数控制聚合在上面计算的数据子集。

例如，让我们回到我们的健康数据：

```py
health_data
```

|  | subject | Bob | Guido | Sue |
| --- | --- | --- | --- | --- | --- | --- | --- |
|  | type | HR | Temp | HR | Temp | HR | Temp |
| year | visit|  |  |  |  |  | |
| 2013 | 1 | 31.0 | 38.7 | 32.0 | 36.7 | 35.0 | 37.2 |
| | 2 | 44.0 | 37.7 | 50.0 | 35.0 | 29.0 | 36.7 |
| 2014 | 1 | 30.0 | 37.4 | 39.0 | 37.8 | 61.0 | 36.9 |
| | 2 | 47.0 | 37.8 | 48.0 | 37.3 | 51.0 | 36.5 |

也许我们想对每年的两次就诊的测量值求平均。 我们可以通过指定我们想要探索的索引层次来实现，在这种情况下是年份：

```py
data_mean = health_data.mean(level='year')
data_mean
```

| subject | Bob | Guido | Sue |
| --- | --- | --- | --- | --- | --- | --- |
| type | HR | Temp | HR | Temp | HR | Temp |
| year|  |  |  |  |  | |
| 2013 | 37.5 | 38.2 | 41.0 | 35.85 | 32.0 | 36.95 |
| 2014 | 38.5 | 37.6 | 43.5 | 37.55 | 56.0 | 36.70 |

通过进一步利用``axis``关键字，我们可以在列上的层次中取平均值：

```py
data_mean.mean(axis=1, level='type')
```

| type | HR | Temp |
| --- | --- | --- |
| year|  | |
| 2013 | 36.833333 | 37.000000 |
| 2014 | 46.000000 | 37.283333 |

因此，在两行中，我们已经能够查询每年所有就诊和所有受试者的平均心率和体温。这个语法实际上是``GroupBy``函数的简写，我们将在“聚合和分组”中讨论。虽然这是一个玩具示例，但许多真实世界的数据集具有相似的层次结构。

## 旁注：面板数据

Pandas 还有一些我们尚未讨论的基本数据结构，即``pd.Panel``和``pd.Panel4D``对象。这些可以分别认为是（一维）``Series``和（二维）``DataFrame``结构的三维和四维扩展。

一旦熟悉了``Series``和``DataFrame``中的数据索引和操作，``Panel``和``Panel4D``就相对简单易用了。特别是，“数据索引和选择”中讨论的``ix``，``loc``和``iloc``索引器，很容易扩展到这些更高维的结构。

我们将不会在本文中进一步介绍这些面板结构，因为我在大多数情况下发现，对于更高维数据来说，多重索引是更有用且概念上更简单的表示。另外，面板数据基本上是密集数据表示，而多索引基本上是稀疏数据表示。

随着维度数量的增加，对于大多数真实世界的数据集，密集表示可能变得非常低效。然而，对于特定场合下的应用，这些结构可能是有用的。如果你想了解``Panel``和``Panel4D``结构的更多信息，请参考“更多资源”中列出的参考资料。