Pandas `MultiIndex` 高级操作:复杂数据层次化处理与切片

Pandas MultiIndex 高级操作:复杂数据层次化处理与切片 (讲座模式)

大家好!欢迎来到今天的 Pandas MultiIndex 高级操作讲座。今天我们要聊聊 Pandas 中一个非常强大的功能,叫做 MultiIndex,中文可以理解为“多层索引”或者“分层索引”。如果你觉得你的数据长得像俄罗斯套娃,一层又一层,那么 MultiIndex 就是你的救星!

为什么要用 MultiIndex?

想象一下,你有一个关于全球各地不同城市的气象数据,数据维度可能包括:国家、城市、年份、月份、温度、湿度等等。 如果把所有这些信息都挤在一列索引里,那简直是一场灾难!

使用 MultiIndex,你可以把国家、城市、年份、月份都变成索引,这样你的数据就变得更有结构,更易于理解和操作。就像图书馆里的图书分类一样,方便我们快速找到想要的信息。

MultiIndex 的基础:从创建开始

Pandas 提供了多种方式来创建 MultiIndex。我们先从最简单的开始:

  • 使用 from_tuples 创建

    如果你已经有一组元组,每个元组代表一个索引的组合,那么 from_tuples 是一个不错的选择。

    import pandas as pd
    
    tuples = [
        ('中国', '北京'),
        ('中国', '上海'),
        ('美国', '纽约'),
        ('美国', '洛杉矶')
    ]
    
    index = pd.MultiIndex.from_tuples(tuples, names=['国家', '城市'])
    print(index)

    输出:

    MultiIndex([('中国', '北京'),
                ('中国', '上海'),
                ('美国', '纽约'),
                ('美国', '洛杉矶')],
               names=['国家', '城市'])

    这个例子中,我们创建了一个包含国家和城市两层索引的 MultiIndex。names 参数可以给每一层索引命名,方便我们后续操作。

  • 使用 from_arrays 创建

    如果你有多个列表或者数组,每个列表代表一层的索引值,那么 from_arrays 就能派上用场。

    countries = ['中国', '中国', '美国', '美国']
    cities = ['北京', '上海', '纽约', '洛杉矶']
    
    index = pd.MultiIndex.from_arrays([countries, cities], names=['国家', '城市'])
    print(index)

    输出和上面的例子一样。

  • 使用 from_product 创建

    如果你想生成所有可能的索引组合,from_product 是最方便的。它会计算多个列表的笛卡尔积。

    countries = ['中国', '美国']
    cities = ['北京', '上海', '纽约', '洛杉矶']
    
    index = pd.MultiIndex.from_product([countries, cities], names=['国家', '城市'])
    print(index)

    输出:

    MultiIndex([('中国', '北京'),
                ('中国', '上海'),
                ('中国', '纽约'),
                ('中国', '洛杉矶'),
                ('美国', '北京'),
                ('美国', '上海'),
                ('美国', '纽约'),
                ('美国', '洛杉矶')],
               names=['国家', '城市'])

    可以看到,from_product 帮我们生成了所有国家和城市的组合。

  • 直接在 DataFrame 中创建

    当然,你也可以在创建 DataFrame 的时候直接指定 MultiIndex。

    data = {
        '温度': [25, 28, 30, 32],
        '湿度': [60, 70, 65, 75]
    }
    
    index = pd.MultiIndex.from_tuples([('中国', '北京'), ('中国', '上海'), ('美国', '纽约'), ('美国', '洛杉矶')], names=['国家', '城市'])
    
    df = pd.DataFrame(data, index=index)
    print(df)

    输出:

              温度  湿度
    国家 中国   北京  25  60
             上海  28  70
    美国   纽约  30  65
             洛杉矶  32  75

    现在,我们的 DataFrame 就有了 MultiIndex,看起来更清晰了吧?

MultiIndex 的核心:切片和选择

有了 MultiIndex 之后,最重要的就是如何利用它来切片和选择数据。 这部分是 MultiIndex 的精华所在,也是最容易让人头疼的地方。

  • 基本切片

    最基本的切片方式就是直接使用 loc 或者 ilocloc 使用索引标签,iloc 使用整数位置。

    # 选择中国的数据
    print(df.loc['中国'])
    
    # 选择中国北京的数据
    print(df.loc[('中国', '北京')])
    
    # 选择第一行数据
    print(df.iloc[0])

    输出:

         温度  湿度
    城市      
    北京  25  60
    上海  28  70
    
    温度    25
    湿度    60
    Name: (中国, 北京), dtype: int64
    
    温度    25
    湿度    60
    Name: (中国, 北京), dtype: int64

    注意,当选择多层索引时,你需要用元组来指定索引值。

  • 使用 pd.IndexSlice 进行高级切片

    pd.IndexSlice 是 MultiIndex 切片的利器,它可以让你更灵活地选择数据。

    idx = pd.IndexSlice
    
    # 选择所有国家的北京的数据
    print(df.loc[idx[:, '北京'], :])
    
    # 选择中国的所有数据,以及美国的纽约的数据
    print(df.loc[idx[['中国', ('美国', '纽约')], :], :])

    输出:

         温度  湿度
    国家 中国   北京  25  60
    美国   北京  30  65
    
              温度  湿度
    国家 中国   北京  25  60
             上海  28  70
    美国   纽约  30  65

    idx[:, '北京'] 的意思是选择所有国家,但是只选择城市为北京的数据。 idx[['中国', ('美国', '纽约')], :] 的意思是选择中国的所有数据,以及美国的纽约的数据。

    pd.IndexSlice 的强大之处在于它可以处理各种复杂的切片需求。

  • 使用 xs 方法进行交叉选择

    xs 方法可以让你选择特定层级的索引值。

    # 选择所有城市的温度数据
    print(df.xs('温度', axis=1))
    
    # 选择中国的数据
    print(df.xs('中国'))
    
    # 选择城市为北京的数据
    print(df.xs('北京', level='城市'))

    输出:

    国家  城市
    中国  北京    25
        上海    28
    美国  纽约    30
        洛杉矶   32
    Name: 温度, dtype: int64
    
         温度  湿度
    城市      
    北京  25  60
    上海  28  70
    
         温度  湿度
    国家      
    中国  25  60
    美国  30  65

    xs('北京', level='城市') 的意思是选择城市这一层索引值为北京的数据。 xs 方法非常适合在特定层级上进行选择。

MultiIndex 的进阶:数据重塑和聚合

MultiIndex 不仅可以帮助你更好地组织数据,还可以让你更方便地进行数据重塑和聚合。

  • stackunstack:数据堆叠和展开

    stackunstack 是 MultiIndex 中非常重要的两个方法,它们可以让你在不同的索引层级之间进行转换。

    # 将城市这一层索引展开到列
    print(df.unstack(level='城市'))
    
    # 将温度和湿度这两列堆叠到索引
    print(df.stack())

    输出:

          温度        湿度      
    城市      北京  上海  纽约  洛杉矶    北京  上海  纽约  洛杉矶
    国家                                  
    中国    25  28 NaN  NaN    60  70 NaN  NaN
    美国   NaN NaN  30  32   NaN NaN  65  75
    
    国家  城市        
    中国  北京  温度    25
            湿度    60
        上海  温度    28
            湿度    70
    美国  纽约  温度    30
            湿度    65
        洛杉矶 温度    32
            湿度    75
    dtype: int64

    unstack 将指定的索引层级展开到列,stack 将列堆叠到索引。 这两个方法可以让你轻松地进行数据透视。

  • groupby:分组聚合

    MultiIndex 和 groupby 简直是天生一对! 你可以根据 MultiIndex 的不同层级进行分组聚合。

    # 按照国家进行分组,计算平均温度
    print(df.groupby(level='国家')['温度'].mean())
    
    # 按照城市进行分组,计算平均温度
    print(df.groupby(level='城市')['温度'].mean())

    输出:

    国家
    中国    26.5
    美国    31.0
    Name: 温度, dtype: float64
    
    城市
    北京    25
    上海    28
    纽约    30
    洛杉矶   32
    Name: 温度, dtype: int64

    groupby(level='国家') 的意思是按照国家这一层索引进行分组。

  • pivot_table:数据透视表

    pivot_table 也是一个强大的数据透视工具,它可以让你更灵活地进行数据聚合。

    # 创建一个更复杂的 DataFrame
    data = {
        '温度': [25, 28, 30, 32, 26, 29, 31, 33],
        '湿度': [60, 70, 65, 75, 62, 72, 67, 77],
        '年份': [2022, 2022, 2022, 2022, 2023, 2023, 2023, 2023]
    }
    
    index = pd.MultiIndex.from_product([['中国', '美国'], ['北京', '上海', '纽约', '洛杉矶']], names=['国家', '城市'])
    df = pd.DataFrame(data, index=index)
    
    # 使用 pivot_table 创建数据透视表,按照国家和年份计算平均温度
    print(pd.pivot_table(df, values='温度', index='国家', columns='年份', aggfunc='mean'))

    输出:

    年份    2022  2023
    国家          
    中国   26.5  27.5
    美国   31.0  32.0

    pivot_table 可以让你指定哪些列作为索引,哪些列作为值,以及使用什么聚合函数。

MultiIndex 的最佳实践:一些建议

  • 给索引命名!

    一定要给 MultiIndex 的每一层索引命名,这样可以提高代码的可读性,并且方便后续的操作。

  • 保持索引的顺序一致

    在创建 MultiIndex 时,要确保索引的顺序和数据的顺序一致,否则会导致数据错位。

  • 谨慎使用 set_indexreset_index

    set_index 可以将 DataFrame 的列设置为索引,reset_index 可以将索引重置为列。 这两个方法在处理 MultiIndex 时要小心,因为它们可能会改变数据的结构。

  • 多练习!

    MultiIndex 的学习曲线比较陡峭,需要多练习才能掌握。 可以尝试用 MultiIndex 来处理一些实际的数据集,例如气象数据、股票数据、销售数据等等。

MultiIndex 的实际案例:股票数据分析

我们来看一个使用 MultiIndex 进行股票数据分析的例子。

假设我们有以下股票数据:

股票代码  日期        开盘价  收盘价  成交量
AAPL    2023-01-01  130   132   10000
AAPL    2023-01-02  133   135   12000
MSFT    2023-01-01  250   252   8000
MSFT    2023-01-02  253   255   9000

我们可以使用 MultiIndex 来组织这些数据,并进行一些简单的分析。

import pandas as pd

data = {
    '股票代码': ['AAPL', 'AAPL', 'MSFT', 'MSFT'],
    '日期': ['2023-01-01', '2023-01-02', '2023-01-01', '2023-01-02'],
    '开盘价': [130, 133, 250, 253],
    '收盘价': [132, 135, 252, 255],
    '成交量': [10000, 12000, 8000, 9000]
}

df = pd.DataFrame(data)

# 将股票代码和日期设置为 MultiIndex
df = df.set_index(['股票代码', '日期'])

print(df)

# 计算每个股票的平均成交量
print(df.groupby(level='股票代码')['成交量'].mean())

# 选择 AAPL 的所有数据
print(df.loc['AAPL'])

输出:

            开盘价  收盘价   成交量
股票代码 日期                     
AAPL   2023-01-01  130  132  10000
       2023-01-02  133  135  12000
MSFT   2023-01-01  250  252   8000
       2023-01-02  253  255   9000

股票代码
AAPL    11000.0
MSFT     8500.0
Name: 成交量, dtype: float64

            开盘价  收盘价   成交量
日期                     
2023-01-01  130  132  10000
2023-01-02  133  135  12000

这个例子展示了如何使用 MultiIndex 来组织股票数据,并进行一些简单的分析,例如计算平均成交量和选择特定股票的数据。

总结

MultiIndex 是 Pandas 中一个非常强大的功能,它可以让你更好地组织和处理复杂的数据。 虽然学习曲线比较陡峭,但是一旦掌握了它,你就可以更高效地进行数据分析和处理。

希望今天的讲座能帮助你更好地理解和使用 Pandas MultiIndex。 记住,多练习,多实践,你就能成为 MultiIndex 大师!

谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注