数据合并与重塑:掌握多表关联操作

举报
数字扫地僧 发表于 2025/08/22 14:30:43 2025/08/22
【摘要】 大家好!欢迎来到我的数据分析系列博客。今天我们要深入探讨数据分析中一个极其重要且实用的主题:数据合并与重塑。无论你是数据分析新手还是有一定经验的老手,掌握多表关联操作都是提升数据分析能力的关键一步。在我们日常的数据分析工作中,数据往往分散在多个表或数据源中。比如,一家电商公司可能有客户信息表、订单表、产品表等多个数据表,要想进行深入的业务分析,首先需要将这些表有机地整合在一起。这就是数据合并...

大家好!欢迎来到我的数据分析系列博客。今天我们要深入探讨数据分析中一个极其重要且实用的主题:数据合并与重塑。无论你是数据分析新手还是有一定经验的老手,掌握多表关联操作都是提升数据分析能力的关键一步。

在我们日常的数据分析工作中,数据往往分散在多个表或数据源中。比如,一家电商公司可能有客户信息表、订单表、产品表等多个数据表,要想进行深入的业务分析,首先需要将这些表有机地整合在一起。这就是数据合并与重塑的价值所在。

这篇博客将带你从零开始,全面学习数据合并与重塑的各种技术和方法。我会结合丰富的实例和详细的代码演示,让你不仅理解概念,更能实际动手操作。

让我们开始这段数据整合之旅吧!

I. 数据合并与重塑概述

数据合并与重塑是数据处理中的核心操作,它涉及将来自不同源的数据整合成一个统一的数据集,以及改变数据的结构布局以适应分析需求。在现代数据分析中,很少有所有需要的数据都整齐地存放在单个表格中的情况。相反,数据通常分散在多个文件、数据库表或API端点中。

为什么需要数据合并与重塑?

  • 数据通常存储在多个来源中,需要整合才能进行完整分析
  • 不同的数据可能以不同的结构存储,需要统一格式
  • 某些分析方法需要特定格式的数据结构
  • 可以提高数据处理的效率和灵活性

常见应用场景:

  • 合并来自不同部门的销售数据
  • 整合用户行为数据和用户属性数据
  • 将宽格式数据转换为长格式以适应某些可视化工具的需求
  • 准备机器学习特征工程所需的数据

为了更直观地理解数据合并与重塑的概念,让我们通过一个Mermaid图表来总结:

数据合并与重塑
数据合并
数据重塑
横向合并
纵向合并
基于键的合并
索引合并
concat操作
append操作
宽表转长表
长表转宽表
melt操作
pivot操作

这个图表概括了数据合并与重塑的主要分类和方法。接下来,我们将深入探讨每种方法的具体实现和应用场景。

II. 数据合并的基本概念

数据合并是指将两个或多个数据集整合在一起的过程。根据合并的方向和方式,可以分为几种不同的类型。了解这些基本概念是掌握多表关联操作的第一步。

合并类型分类

数据合并可以根据方向和方式进行分类,以下表格总结了主要的合并类型:

合并类型 方向 描述 适用场景
横向合并 水平方向 根据一个或多个键将多个数据集的列合并 合并不同属性但具有相同实体的数据
纵向合并 垂直方向 将多个数据集的行追加在一起 合并相同结构但不同时间段或来源的数据
内连接 横向合并 只保留两个数据集都存在的键 需要完全匹配的记录时
外连接 横向合并 保留所有记录,缺失值用NaN填充 需要保留所有记录时
左连接 横向合并 保留左边数据集的所有记录 以左边数据集为主时
右连接 横向合并 保留右边数据集的所有记录 以右边数据集为主时

合并键的概念

合并键是数据合并的基础,它是用于匹配两个数据集中的记录的列或一组列。选择合适的合并键至关重要:

键的选择考虑因素:

  • 唯一性:键应该能够唯一标识记录
  • 一致性:两个数据集中的键应该具有相同的含义和格式
  • 完整性:键不应该有太多缺失值

键的类型:

  • 单一键:使用单个列进行匹配
  • 复合键:使用多个列的组合进行匹配
  • 索引:使用数据集的索引作为键

理解了这些基本概念后,我们将进入实践环节,学习如何使用Python进行数据合并操作。

III. 使用pandas进行数据合并

Pandas是Python中最流行的数据分析库,提供了丰富的功能来处理数据合并操作。在这一节中,我们将深入学习pandas中的各种合并方法,并通过实例演示如何使用它们。

环境准备和示例数据

首先,让我们准备环境和创建一些示例数据来进行演示:

import pandas as pd
import numpy as np

# 创建示例数据:员工信息表
employees = pd.DataFrame({
    'employee_id': [1, 2, 3, 4, 5],
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eva'],
    'department_id': [101, 102, 101, 103, 102],
    'hire_date': pd.date_range('20220101', periods=5)
})

print("员工信息表:")
print(employees)
print("\n")

# 创建示例数据:部门信息表
departments = pd.DataFrame({
    'department_id': [101, 102, 103, 104],
    'department_name': ['HR', 'Engineering', 'Marketing', 'Finance'],
    'manager_id': [1, 2, 4, 6]
})

print("部门信息表:")
print(departments)
print("\n")

# 创建示例数据:薪资信息表
salaries = pd.DataFrame({
    'employee_id': [1, 2, 3, 4, 6],
    'salary': [50000, 60000, 55000, 45000, 70000],
    'effective_date': pd.date_range('20230101', periods=5)
})

print("薪资信息表:")
print(salaries)

代码解释:

  • 我们首先导入必要的库:pandas用于数据处理,numpy用于数值计算
  • 创建三个示例DataFrame:
    • employees表包含员工基本信息
    • departments表包含部门信息
    • salaries表包含员工薪资信息
  • 每个表都有一些共同的键(employee_id或department_id),但也有不匹配的记录

基本合并操作

现在让我们看看如何使用pandas进行基本的合并操作:

# 1. 内连接示例:合并员工和部门信息
inner_merge = pd.merge(employees, departments, on='department_id', how='inner')
print("内连接结果 - 员工和部门信息:")
print(inner_merge)
print("\n")

# 2. 左连接示例:保留所有员工信息,即使没有部门信息
left_merge = pd.merge(employees, departments, on='department_id', how='left')
print("左连接结果 - 所有员工信息:")
print(left_merge)
print("\n")

# 3. 右连接示例:保留所有部门信息,即使没有员工
right_merge = pd.merge(employees, departments, on='department_id', how='right')
print("右连接结果 - 所有部门信息:")
print(right_merge)
print("\n")

# 4. 外连接示例:保留所有记录
outer_merge = pd.merge(employees, departments, on='department_id', how='outer')
print("外连接结果 - 所有记录:")
print(outer_merge)
print("\n")

代码解释:

  • pd.merge() 是pandas中用于合并DataFrame的主要函数
  • on 参数指定用于合并的键
  • how 参数指定合并类型:‘inner’, ‘left’, ‘right’, 或 ‘outer’
  • 内连接只保留两个表中都存在的键
  • 左连接保留左表的所有记录,右表匹配不上的用NaN填充
  • 右连接保留右表的所有记录,左表匹配不上的用NaN填充
  • 外连接保留两个表的所有记录

多键合并和特殊情况处理

在实际应用中,我们经常需要基于多个键进行合并,或者处理一些特殊情况:

# 创建额外的示例数据:员工详细信息和职位历史
employee_details = pd.DataFrame({
    'employee_id': [1, 2, 3, 4, 5],
    'city': ['New York', 'Boston', 'Chicago', 'Seattle', 'Austin'],
    'age': [28, 32, 45, 36, 29]
})

position_history = pd.DataFrame({
    'employee_id': [1, 1, 2, 3, 4, 4, 5],
    'position': ['Analyst', 'Senior Analyst', 'Developer', 'Manager', 'Associate', 'Senior Associate', 'Director'],
    'start_date': pd.date_range('20200101', periods=7),
    'end_date': pd.date_range('20210101', periods=7)
})

print("员工详细信息表:")
print(employee_details)
print("\n")

print("职位历史表:")
print(position_history)
print("\n")

# 多对一合并:多个职位记录对应一个员工信息
multi_merge = pd.merge(position_history, employee_details, on='employee_id', how='left')
print("多对一合并结果:")
print(multi_merge)
print("\n")

# 处理重复键名的情况:当两个表有相同名称但不是用于合并的列时
# 添加一个同名的列但不是键的列到两个表中
employees_with_city = employees.copy()
employees_with_city['city'] = ['New York', 'Boston', 'Chicago', 'Seattle', 'Austin']

departments_with_city = departments.copy()
departments_with_city['city'] = ['New York', 'San Francisco', 'Chicago', 'Boston']

# 合并时会自动添加后缀区分同名列
suffix_merge = pd.merge(employees_with_city, departments_with_city, on='department_id', how='left', suffixes=('_emp', '_dept'))
print("处理重复列名的合并结果:")
print(suffix_merge)

代码解释:

  • 多对一合并是指一个表中的多条记录与另一个表的一条记录匹配
  • 当两个表有相同名称但不是用于合并的列时,pandas会自动添加后缀区分
  • 可以使用suffixes参数自定义后缀名称

使用concat进行数据合并

除了merge之外,pandas还提供了concat函数用于沿特定轴拼接数据:

# 创建两个结构相同的表用于concat示例
sales_q1 = pd.DataFrame({
    'product': ['A', 'B', 'C'],
    'q1_sales': [100, 150, 200]
})

sales_q2 = pd.DataFrame({
    'product': ['A', 'B', 'D'],
    'q2_sales': [120, 160, 90]
})

print("第一季度销售数据:")
print(sales_q1)
print("\n")

print("第二季度销售数据:")
print(sales_q2)
print("\n")

# 使用concat进行纵向合并(追加行)
vertical_concat = pd.concat([sales_q1, sales_q2], ignore_index=True)
print("纵向合并结果:")
print(vertical_concat)
print("\n")

# 使用concat进行横向合并(追加列)
horizontal_concat = pd.concat([sales_q1, sales_q2], axis=1)
print("横向合并结果:")
print(horizontal_concat)

代码解释:

  • pd.concat() 可以沿轴0(纵向)或轴1(横向)拼接数据
  • 纵向合并相当于SQL中的UNION ALL操作
  • 横向合并相当于将列追加到现有数据集中
  • ignore_index参数用于重置索引

通过以上示例,我们学习了pandas中各种数据合并的方法。接下来,让我们用Mermaid图表总结这一节的内容:

Pandas数据合并方法
merge函数
concat函数
合并类型
键处理
内连接
左连接
右连接
外连接
单键合并
多键合并
重复列处理
纵向合并
横向合并

掌握了数据合并的基本操作后,我们接下来学习数据重塑的技术。

IV. 数据重塑技术

数据重塑是指改变数据布局结构而不改变其内容的过程。在数据分析中,我们经常需要将数据从宽格式转换为长格式,或者反之,以适应不同的分析需求。Pandas提供了强大的功能来进行数据重塑。

宽格式与长格式

在深入具体技术之前,让我们先理解宽格式和长格式数据的区别:

格式类型 特点 优点 缺点
宽格式 每个变量有单独的列 易于阅读和理解 不适合某些统计分析
长格式 变量值存储在单列中,类型存储在另一列 适合大多数统计分析和可视化 可读性较差

使用melt进行宽表转长表

melt操作是将数据从宽格式转换为长格式的重要方法:

# 创建宽格式示例数据
wide_data = pd.DataFrame({
    'student': ['Alice', 'Bob', 'Charlie'],
    'math_score': [85, 92, 78],
    'science_score': [88, 90, 85],
    'history_score': [76, 88, 92]
})

print("宽格式数据:")
print(wide_data)
print("\n")

# 使用melt进行宽表转长表
long_data = pd.melt(wide_data, 
                   id_vars=['student'], 
                   value_vars=['math_score', 'science_score', 'history_score'],
                   var_name='subject', 
                   value_name='score')

print("转换后的长格式数据:")
print(long_data)
print("\n")

# 更复杂的melt示例:多个id变量
wide_data_with_year = pd.DataFrame({
    'student': ['Alice', 'Bob', 'Charlie'],
    'year': [2022, 2022, 2022],
    'math_score': [85, 92, 78],
    'science_score': [88, 90, 85],
    'history_score': [76, 88, 92]
})

print("包含年份的宽格式数据:")
print(wide_data_with_year)
print("\n")

long_data_complex = pd.melt(wide_data_with_year, 
                           id_vars=['student', 'year'], 
                           value_vars=['math_score', 'science_score', 'history_score'],
                           var_name='subject', 
                           value_name='score')

print("复杂转换后的长格式数据:")
print(long_data_complex)

代码解释:

  • pd.melt() 函数用于将宽格式数据转换为长格式
  • id_vars 参数指定要保持不变的列
  • value_vars 参数指定要转换的列
  • var_name 参数指定新列的名称,用于存储原来的列名
  • value_name 参数指定新列的名称,用于存储原来的值

使用pivot进行长表转宽表

pivot操作是melt的逆操作,用于将长格式数据转换为宽格式:

# 使用前面创建的long_data进行pivot操作
pivoted_data = long_data.pivot(index='student', columns='subject', values='score')

print("pivot转换后的宽格式数据:")
print(pivoted_data)
print("\n")

# 重置索引使数据更整洁
pivoted_data_reset = pivoted_data.reset_index()
pivoted_data_reset.columns.name = None  # 移除列名名称

print("整理后的宽格式数据:")
print(pivoted_data_reset)
print("\n")

# 处理重复值的pivot操作:使用pivot_table
# 创建有重复值的长格式数据
long_data_with_duplicates = pd.DataFrame({
    'student': ['Alice', 'Alice', 'Bob', 'Bob', 'Charlie', 'Charlie'],
    'subject': ['math', 'math', 'science', 'science', 'history', 'history'],
    'score': [85, 90, 92, 88, 78, 82],
    'test_date': pd.date_range('20230101', periods=6)
})

print("有重复值的长格式数据:")
print(long_data_with_duplicates)
print("\n")

# 使用pivot_table处理重复值,使用聚合函数
pivot_table_result = pd.pivot_table(long_data_with_duplicates, 
                                   values='score', 
                                   index='student', 
                                   columns='subject', 
                                   aggfunc='mean')  # 使用平均值处理重复值

print("pivot_table处理重复值的结果:")
print(pivot_table_result)

代码解释:

  • pivot() 方法用于将长格式数据转换为宽格式
  • index 参数指定作为行索引的列
  • columns 参数指定作为新列名的列
  • values 参数指定作为值的列
  • 当有重复值时,需要使用pivot_table()并指定聚合函数

使用stack和unstack进行重塑

除了melt和pivot,pandas还提供了stack和unstack方法进行数据重塑:

# 创建多层索引的示例数据
multi_index_data = pd.DataFrame({
    'school': ['School A'] * 6 + ['School B'] * 6,
    'class': ['Class 1', 'Class 1', 'Class 1', 'Class 2', 'Class 2', 'Class 2'] * 2,
    'subject': ['Math', 'Science', 'History'] * 4,
    'score': [85, 88, 76, 92, 90, 88, 78, 85, 92, 95, 89, 94]
})

print("原始多层数据:")
print(multi_index_data)
print("\n")

# 设置多层索引
indexed_data = multi_index_data.set_index(['school', 'class', 'subject'])
print("设置多层索引后的数据:")
print(indexed_data)
print("\n")

# 使用unstack将内层索引转换为列
unstacked_data = indexed_data.unstack()
print("unstack操作后的数据:")
print(unstacked_data)
print("\n")

# 使用stack将列转换回索引
restacked_data = unstacked_data.stack()
print("stack操作恢复的数据:")
print(restacked_data)

代码解释:

  • set_index() 方法用于设置多层索引
  • unstack() 方法将内层索引转换为列
  • stack() 方法是unstack的逆操作,将列转换回索引
  • 这些方法在处理分层数据时非常有用

通过以上示例,我们学习了各种数据重塑技术。让我们用Mermaid图表总结这一节的内容:

Lexical error on line 14. Unrecognized text. ... E --> K[指定index、columns和values] ----------------------^

掌握了数据合并和重塑的基本技术后,我们将通过一个综合实例来演示如何应用这些技术解决实际问题。

V. 综合实例:电商数据分析

在这一节中,我们将通过一个完整的电商数据分析实例,演示如何应用前面学到的数据合并与重塑技术。我们将使用模拟的电商数据,包含订单信息、客户信息和产品信息。

数据准备

首先,让我们创建一些模拟的电商数据:

# 创建客户信息表
customers = pd.DataFrame({
    'customer_id': [1, 2, 3, 4, 5],
    'name': ['John Doe', 'Jane Smith', 'Bob Johnson', 'Alice Brown', 'Charlie Wilson'],
    'email': ['john@example.com', 'jane@example.com', 'bob@example.com', 'alice@example.com', 'charlie@example.com'],
    'join_date': pd.date_range('20220101', periods=5),
    'segment': ['Premium', 'Standard', 'Standard', 'Premium', 'Standard']
})

print("客户信息表:")
print(customers)
print("\n")

# 创建产品信息表
products = pd.DataFrame({
    'product_id': [101, 102, 103, 104, 105],
    'product_name': ['Laptop', 'Mouse', 'Keyboard', 'Monitor', 'Headphones'],
    'category': ['Electronics', 'Accessories', 'Accessories', 'Electronics', 'Accessories'],
    'price': [1000, 25, 50, 300, 75]
})

print("产品信息表:")
print(products)
print("\n")

# 创建订单信息表
orders = pd.DataFrame({
    'order_id': [1001, 1002, 1003, 1004, 1005, 1006, 1007],
    'customer_id': [1, 2, 1, 3, 4, 2, 5],
    'order_date': pd.date_range('20230101', periods=7),
    'status': ['Delivered', 'Shipped', 'Delivered', 'Processing', 'Delivered', 'Shipped', 'Processing']
})

print("订单信息表:")
print(orders)
print("\n")

# 创建订单详情表
order_details = pd.DataFrame({
    'order_id': [1001, 1001, 1002, 1003, 1004, 1005, 1006, 1007],
    'product_id': [101, 102, 103, 101, 104, 105, 102, 101],
    'quantity': [1, 2, 1, 1, 1, 2, 3, 1],
    'unit_price': [1000, 25, 50, 1000, 300, 75, 25, 1000]
})

print("订单详情表:")
print(order_details)

数据合并实践

现在,让我们将这些表合并成一个完整的数据集进行分析:

# 第一步:合并订单和订单详情表
order_full = pd.merge(orders, order_details, on='order_id', how='inner')
print("订单与订单详情合并结果:")
print(order_full)
print("\n")

# 第二步:添加客户信息
order_customer = pd.merge(order_full, customers, on='customer_id', how='left')
print("添加客户信息后的结果:")
print(order_customer)
print("\n")

# 第三步:添加产品信息
complete_data = pd.merge(order_customer, products, on='product_id', how='left')
print("完整合并后的数据集:")
print(complete_data)
print("\n")

# 计算总销售额
complete_data['total_sale'] = complete_data['quantity'] * complete_data['unit_price']
print("添加总销售额后的数据集:")
print(complete_data[['order_id', 'customer_id', 'product_name', 'quantity', 'unit_price', 'total_sale']])

数据重塑实践

现在,让我们对合并后的数据进行一些重塑操作:

# 创建宽格式的销售数据:每个产品的销售额
sales_by_product = complete_data.groupby(['product_name', 'category'])['total_sale'].sum().reset_index()
print("按产品汇总的销售额:")
print(sales_by_product)
print("\n")

# 使用pivot创建宽格式:产品作为行,类别作为列
sales_pivot = complete_data.pivot_table(
    values='total_sale', 
    index='product_name', 
    columns='category', 
    aggfunc='sum',
    fill_value=0
)

print("产品-类别销售额透视表:")
print(sales_pivot)
print("\n")

# 创建客户-产品购买矩阵
customer_product_matrix = complete_data.pivot_table(
    values='quantity',
    index='name',
    columns='product_name',
    aggfunc='sum',
    fill_value=0
)

print("客户-产品购买矩阵:")
print(customer_product_matrix)

高级分析与可视化

最后,让我们进行一些高级分析并可视化结果:

import matplotlib.pyplot as plt

# 分析每个客户分段的销售情况
segment_sales = complete_data.groupby('segment')['total_sale'].sum().reset_index()
print("各客户分段的销售总额:")
print(segment_sales)
print("\n")

# 可视化客户分段销售情况
plt.figure(figsize=(10, 6))
plt.bar(segment_sales['segment'], segment_sales['total_sale'])
plt.title('Sales by Customer Segment')
plt.xlabel('Customer Segment')
plt.ylabel('Total Sales ($)')
plt.show()

# 分析销售趋势 over time
complete_data['order_month'] = complete_data['order_date'].dt.to_period('M')
monthly_sales = complete_data.groupby('order_month')['total_sale'].sum().reset_index()
monthly_sales['order_month'] = monthly_sales['order_month'].astype(str)

print("月度销售趋势:")
print(monthly_sales)
print("\n")

# 可视化销售趋势
plt.figure(figsize=(12, 6))
plt.plot(monthly_sales['order_month'], monthly_sales['total_sale'], marker='o')
plt.title('Monthly Sales Trend')
plt.xlabel('Month')
plt.ylabel('Total Sales ($)')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

通过这个综合实例,我们演示了如何应用数据合并与重塑技术解决真实的业务问题。从基本的数据合并到复杂的数据重塑和分析,这些技能是每个数据分析师必备的工具。

让我们用Mermaid图表总结这个实例的流程:

Lexical error on line 8. Unrecognized text. ...模拟数据] B --> G[客户、产品、订单数据] C ----------------------^

这个实例展示了数据合并与重塑在实际分析中的应用价值。通过灵活运用这些技术,我们可以从原始数据中提取有价值的业务洞察。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。