Python
Pandas Unstack: 数据重塑入门指南

Pandas Unstack: 数据重塑入门指南

MoeNagy Dev

理解 pandas unstack

pandas unstack 的解释

什么是 pandas unstack?

unstack() 是 pandas 中的一个函数,它可以将 DataFrame 从长格式转换为宽格式。它接受一个多级列索引,并将其"解堆叠",创建一个新的 DataFrame,其中一个或多个索引级别成为新的列。

它与 pivot 和 melt 有什么不同?

unstack() 函数类似于 pivot() 函数,但它们的用途不同。pivot() 用于将数据从长格式重塑为宽格式,而 unstack() 则用于将数据从宽格式重塑为长格式。

另一方面,melt() 函数用于将数据从宽格式转换为长格式,这与 unstack() 的作用相反。

什么时候使用 pandas unstack?

当您有一个具有多级列索引的 DataFrame,并且想将其转换为宽格式,使得列索引的各个级别成为新的列时,您应该使用 unstack()

准备数据

导入必要的库

import pandas as pd
import numpy as np

创建一个示例 DataFrame

# 创建一个示例 DataFrame
data = {
    ('Store A', 'Sales'): [100, 120, 80, 90, 110],
    ('Store A', 'Profit'): [20, 25, 15, 18, 22],
    ('Store B', 'Sales'): [150, 180, 120, 160, 200],
    ('Store B', 'Profit'): [30, 35, 25, 32, 40]
}
 
df = pd.DataFrame(data)

探索 DataFrame 的结构

print(df)
           (Store A, Sales)  (Store A, Profit)  (Store B, Sales)  (Store B, Profit)
0  .
100                 20               150                 30
1                      120                 25               180                 35
2                       80                 15               120                 25
3                       90                 18               160                 32
4                      110                 22               200                 40

如您所见,DataFrame 具有多级列索引,第一级表示商店,第二级表示指标(销售额或利润)。

pandas unstack 的基础知识

解压缩单级索引

要解压缩单级索引,可以使用 unstack() 函数,不需要任何参数:

df_unstacked = df.unstack()
print(df_unstacked)
                 Sales        Profit
                 Store A Store B Store A Store B
0                    100     150       20       30
1                    120     180       25       35
2                     80     120       15       25
3                     90     160       18       32
4                    110     200       22       40

生成的 DataFrame 将商店名称作为列索引,而原始列名(销售额和利润)已成为行索引。

解压缩多级索引

如果 DataFrame 具有多级列索引,您可以指定要解压缩的级别:

df_unstacked = df.unstack(level=0)
print(df_unstacked)
                  (Sales, Store A)  (Sales, Store B)  (Profit, Store A)  (Profit, Store B)
0                             100               150                 20                 30
1                             120               180                 25                 35
2                              80               120                 15                 25
3                              90               160                 18                 32
4                             110            .

在这种情况下,商店名称已成为列索引,而原来的列名(销售额和利润)现在是多级列索引的一部分。

#### 理解生成的 DataFrame 结构

解压缩后的 DataFrame 具有多级列索引,其中第一级表示原始列名,第二级表示先前在列索引中的值。

这种结构对于某些类型的数据分析和可视化很有用,因为它允许您轻松访问和操作数据。

### 处理 pandas unstack 中的缺失数据

#### 处理 NaN 值

如果原始 DataFrame 中存在任何缺失值,`unstack()` 函数将在生成的 DataFrame 中引入 NaN 值:

```python
# 向示例 DataFrame 添加一些缺失值
data = {
    ('Store A', 'Sales'): [100, 120, 80, np.nan, 110],
    ('Store A', 'Profit'): [20, 25, 15, 18, 22],
    ('Store B', 'Sales'): [150, 180, 120, 160, 200],
    ('Store B', 'Profit'): [30, 35, 25, 32, np.nan]
}

df = pd.DataFrame(data)
df_unstacked = df.unstack()
print(df_unstacked)
                 Sales        Profit
                 Store A Store B Store A Store B
0                    100     150       20     30.0
1                    120     180       25     35.0
2                     80     120       15     25.0
3                    NaN     160       18     32.0
4                    110     200       22      NaN

您可以看到,原始 DataFrame 中的缺失值已经传递到解压缩的 DataFrame 中。

填充缺失值

要处理缺失值,可以使用 fillna() 方法用特定值替换它们:

df_unstacked = df.unstack().fillna(0)
print(df_unstacked)
                 Sales        Profit
                 Store A Store B Store A Store B
0                    100     150       20     0.0
1                    120     180       25     35.0
2                     80     120       15     25.0
3                      0     160       18     32.0
4                    110     200       22     0.0

1 120 180 25 35 2 80 120 15 25 3 0 160 18 32 4 110 200 22 0

在这个例子中,我们用 0 填充缺失值。

指定填充值

你也可以指定不同的填充值,比如列的平均值或中位数:

# 用列的平均值填充缺失值
df_unstacked = df.unstack().fillna(df.mean())
print(df_unstacked)
                 Sales        Profit
                 Store A Store B Store A Store B
0                    100     150       20     32.5
1                    120     180       25     32.5
2                     80     120       15     32.5
3                    95.0     160       18     32.5
4                    110     200       22     22.0

在这个例子中,我们用各列的平均值来填充缺失值。

使用 pandas unstack 的高级技术

指定层级进行 unstack

你也可以只 unstack 列索引的特定层级,而不是全部层级:

# Unstack the second level of the column index
df_unstacked = df.unstack(level=1)
print(df_unstacked)
                   Sales   Profit
Store A  0            100       20
         1            120       25
         2             80       15
         3            NaN       18
         4            110       22
Store B  0            150       30
         1            180       35
         2            120       25
         3            160       32
         4            200       NaN

在这种情况下,商店名称成为了行索引,而原来的列名(Sales 和 Profit)成为了列索引。

将 unstack 与其他 pandas 操作结合使用

你可以将 unstack() 函数与其他 pandas 操作(如 reset_index()rename())结合使用,以进一步操作数据:

# Unstack and reset .
```这是中文翻译版本:
 

df_unstacked = df.unstack().reset_index() print(df_unstacked)

level_0 level_1 0 1 0 Store A Sales 100 20 1 Store A Sales 120 25 2 Store A Sales 80 15 3 Store A Sales NaN 18 4 Store A Sales 110 22 5 Store B Sales 150 30 6 Store B Sales 180 35 7 Store B Sales 120 25 8 Store B Sales 160 32 9 Store B Sales 200 NaN


在这个例子中, 我们对DataFrame进行了解堆叠操作, 然后重置了索引, 这样创建了一个新的DataFrame, 其中解堆叠的值都在一个单独的列中。

#### 在解堆叠后重置索引

如果你想在解堆叠后重置索引, 可以使用`reset_index()`方法:

```python
# 解堆叠并重置索引
df_unstacked = df.unstack().reset_index()
print(df_unstacked)
  level_0 level_1   0         1
0  Store A  Sales  100       20
1  Store A  Sales  120       25
2  Store A  Sales   80       15
3  Store A  Sales  NaN       18
4  Store A  Sales  110       22
5  Store B  Sales  150       30
6  Store B  Sales  180       35
7  Store B  Sales  120       25
8  Store B  Sales  160       32
9  Store B  Sales  200       NaN

这样创建了一个新的DataFrame, 其中解堆叠的值都在一个单独的列中, 而原来的索引级别现在成为了DataFrame的列。

可视化解堆叠的数据

创建热力图

可视化解堆叠数据的一种方式是使用seaborn库创建热力图:

import seaborn as sns
import matplotlib.pyplot as plt
 
# 解堆叠DataFrame
df_unstacked = df.unstack()
 
# 创建热力图
plt.figure(figsize=(8, 6))
sns.heatmap(df_unstacked, annot=True, cmap="YlOrRd")
plt.title("按店铺划分的销售和利润")
plt.show()

这将创建一个热力图, 可视化每个店铺的销售和利润数据。

生成透视表

你也可以使用pivot_table()函数从解堆叠的数据创建一个透视表:

# 创建透视表
pivot_table = ...
```以下是中文翻译:
 
e = df.pivot_table(index=['商店'], columns=['指标'], values=['值'])
print(pivot_table)
 

值 指标 利润 销售额 商店 A 20 100 25 120 15 80 18 NaN 22 110 商店 B 30 150 35 180 25 120 32 160 NaN 200


这个透视表的行索引是商店名称,列索引是指标名称,单元格中是相应的值。

#### 绘制未堆叠的数据

您也可以直接绘制未堆叠的数据,例如创建条形图或折线图:

```python
# 绘制未堆叠的数据
df_unstacked.plot(kind="bar", figsize=(10, 6))
plt.title("按商店划分的销售额和利润")
plt.xlabel("商店")
plt.ylabel("值")
plt.show()

这将创建一个条形图,显示每个商店的销售额和利润值。

pandas unstack的实际应用

分析销售数据

Unstack可用于分析销售数据,特别是当您有多级列索引时。您可以使用未堆叠的数据创建透视表、热图或其他可视化,以更好地了解不同商店、产品或时间段的销售趋势和绩效。

重塑时间序列数据

unstack()也可用于重塑时间序列数据,在这种情况下,您有一个包含时间和其他维度(如位置、产品)的多级索引。通过unstack数据,您可以创建一个宽格式的DataFrame,这对于某些类型的分析和可视化更加方便。

处理调查数据

对于调查数据,每个参与者都有对不同问题的响应,在这种情况下,unstack()可用于将数据从长格式转换为宽格式,从而更容易分析不同调查问题之间的关系。

故障排除和最佳实践

常见问题和错误消息

unstack()的一个常见问题是它可能会引入.

函数

函数是可重复使用的代码块,用于执行特定的任务。它们可以接受输入参数,执行操作,并返回一个值。函数有助于组织和模块化您的代码,使其更加可读和可维护。

以下是一个简单的计算圆面积的函数示例:

def calculate_circle_area(radius):
    """
    计算圆的面积。
 
    参数:
        radius (float): 圆的半径。
 
    返回:
        float: 圆的面积。
    """
    pi = 3.14159
    area = pi * (radius ** 2)
    return area
 
# 示例用法
circle_radius = 5.0
circle_area = calculate_circle_area(circle_radius)
print(f"半径为 {circle_radius} 的圆的面积为 {circle_area:.2f} 平方单位。")

在这个示例中,calculate_circle_area 函数接受一个 radius 参数,使用公式 pi * (radius ** 2) 计算面积,并返回结果。该函数还包含一个文档字符串,提供了函数的简要描述、输入参数和返回值。

模块和包

Python 的模块化设计允许您将代码组织成可重复使用的组件,称为模块。模块是包含变量、函数和类定义的 Python 文件。通过导入模块,您可以访问和使用它们提供的代码。

以下是如何创建和使用自定义模块的示例:

# my_module.py
def greet(name):
    print(f"你好, {name}!")
 
# main.py
import my_module
 
my_module.greet("Alice")

在这个示例中,我们创建了一个名为 my_module.py 的模块,其中包含一个 greet 函数。在 main.py 文件中,我们导入 my_module 并调用 greet 函数。

包是相关模块的集合。它们有助于组织您的代码,并提供一种分组和分发 Python 代码的方式。以下是一个简单的包结构示例:

my_package/
    __init__.py
    module1.py

module2.py subpackage/ init.py module3.py

在这个例子中,my_package 是一个包,包含两个模块 (module1.pymodule2.py) 和一个子包 (subpackage)。包和子包中的 __init__.py 文件用于定义包的结构和行为。

面向对象编程 (OOP)

面向对象编程 (OOP) 是一种编程范式,它专注于创建包含数据 (属性) 和函数 (方法) 的对象,用于表示和操作该数据。OOP 提供了诸如类、继承和多态等概念,有助于创建更有组织和可重用的代码。

以下是一个简单的 Python 类示例:

class Dog:
    # 初始化方法,设置名称和品种属性
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
 
    # 定义一个叫声方法
    def bark(self):
        print("Woof!")
 
# 使用示例
my_dog = Dog("Buddy", "Labrador")
print(my_dog.name)  # 输出: Buddy
print(my_dog.breed)  # 输出: Labrador
my_dog.bark()  # 输出: Woof!

在这个例子中,我们定义了一个 Dog 类,它有一个 __init__ 方法来初始化 namebreed 属性。该类还有一个 bark 方法,打印出 "Woof!"。我们然后创建了一个 Dog 类的实例,并访问了它的属性和方法。

OOP 还提供了继承的概念,子类可以继承父类的属性和方法。以下是一个示例:

class GuideDog(Dog):
    # 初始化方法,设置名称、品种和是否可以引导的属性
    def __init__(self, name, breed, can_guide):
        super().__init__(name, breed)
        self.can_guide = can_guide
 
    # 定义一个引导方法
    def guide(self):
        print("I can guide my owner.")
 
# 使用示例
my_guide_dog = GuideDog("Buddy", "Labrador", True)
print(my_guide_dog.name)  # 输出: Buddy
print(my_guide_dog.breed)  # 输出: Labrador
print(my_guide_dog.can_guide)  # 输出: True
my_guide_dog.bark()  # 输出: Woof!
my_guide_dog.guide()  # 输出: I can guide my owner.

在这个例子中,GuideDog 类继承自 Dog 类。

异常和错误处理

异常是程序执行过程中发生的事件,会中断程序的正常执行流程。Python 内置了一个异常处理机制,允许您预料并处理这些异常。

下面是一个处理 ZeroDivisionError 异常的示例:

def divide(a, b):
    try:
        # 尝试执行除法操作
        result = a / b
        return result
    except ZeroDivisionError:
        # 如果发生除以零的错误,打印错误信息并返回 None
        print("错误: 除数为零。")
        return None
 
# 示例用法
print(divide(10, 2))  # 输出: 5.0
print(divide(10, 0))  # 输出: 错误: 除数为零。

在这个示例中,divide 函数在 try 块中尝试执行除法操作。如果发生 ZeroDivisionError 异常,则执行 except 块中的代码,打印错误信息并返回 None 表示除法操作失败。

您还可以使用 finally 块来执行无论是否发生异常都需要执行的代码。这对于清理资源(如关闭文件或数据库连接)很有用。

try:
    # 尝试打开并读取文件
    file = open("file.txt", "r")
    content = file.read()
    print(content)
except FileNotFoundError:
    # 如果文件未找到,打印错误信息
    print("错误: 文件未找到。")
finally:
    # 无论是否发生异常,都会执行关闭文件的操作
    file.close()

在这个示例中,finally 块确保即使在 try 块中发生异常,文件也会被正确关闭。

结论

在这个 Python 教程中,我们涵盖了函数、模块和包、面向对象编程以及异常处理等广泛的主题。这些概念是编写有效且可维护的 Python 代码的基础。

通过理解和应用这些技术,您将能够编写更加健壮和可靠的 Python 程序。在通往成为熟练的 Python 程序员的道路上,请记住定期练习、尝试不同的代码示例,并探索 Python 丰富的库和框架生态系统,以拓展您的知识和技能。

祝您编码愉快!

MoeNagy Dev.

Python 入门教程

变量和数据类型

# 定义一个整数变量
age = 25
 
# 定义一个浮点数变量
pi = 3.14159
 
# 定义一个字符串变量
name = "John Doe"
 
# 定义一个布尔变量
is_student = True

条件语句

# 检查年龄是否大于等于18
if age >= 18:
    print("您已成年")
else:
    print("您未成年")

循环语句

# 使用 for 循环遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)
 
# 使用 while 循环打印数字 1 到 5
count = 1
while count <= 5:
    print(count)
    count += 1

函数

# 定义一个简单的函数
def greet(name):
    print("Hello, " + name + "!")
 
# 调用函数
greet("Alice")

总结

通过学习这些基础知识,您已经为成为一名熟练的 Python 程序员奠定了良好的基础。继续练习、探索更多的 Python 特性和库,相信您一定能够不断提升自己的编程技能。祝您学习愉快!