AI赋能未来:Pandas数据索引与聚合实战
一、loc、iloc函数
Pandas 中两种主要的索引方法:.loc 和 .iloc。理解它们的关键在于记住它们名字的由来:
- .loc: 全称
location,意为 “位置”,但它是基于 标签(Label) 进行索引的。 - .iloc: 全称
integer location,意为 “整数位置”,它是基于 数字索引(Position) 进行索引的。
1.核心区别对比
| 特性 | .loc |
.iloc |
|---|---|---|
| 索引依据 | 标签 (Label)(行索引的名称、列名) | 整数位置 (Position)(从 0 开始的索引) |
| 索引方式 | 支持标签、布尔数组、条件表达式 | 支持整数、整数数组、切片 |
| 切片操作 | 包含结束边界 | 不包含结束边界 |
| 适用场景 | 按行名、列名查找数据 | 按数据的物理位置查找数据 |
import pandas as pd
data = {'A': [1, 2, 3, 4], 'B': [10, 20, 30, 40], 'C': [100, 200, 300, 400]}
df = pd.DataFrame(data, index=['a', 'b', 'c', 'd'])
print(df)
输出:
A B C
a 1 10 100
b 2 20 200
c 3 30 300
d 4 40 400
1) .loc 的用法
通过行标签和列名来索引数据。
- 单个元素
print(df.loc['b', 'B']) # 输出: 20
- 一行数据
print(df.loc['c'])
# 输出:
# A 3
# B 30
# C 300
# Name: c, dtype: int64
- 多行数据
print(df.loc[['a', 'd']])
# 输出:
# A B C
# a 1 10 100
# d 4 40 400
- 一列 / 多列数据
print(df.loc[:, ['A', 'C']])
# 输出:
# A C
# a 1 100
# b 2 200
# c 3 300
# d 4 400
- 切片(包含结束标签)
print(df.loc['b':'d', 'A':'B'])
# 输出:
# A B
# b 2 20
# c 3 30
# d 4 40
- 条件索引
print(df.loc[df['B'] > 20])
# 输出:
# A B C
# c 3 30 300
# d 4 40 400
2) .iloc 的用法
通过整数索引(从 0 开始)来索引数据,类似 Python 列表的索引方式。
- 单个元素
print(df.iloc[1, 1]) # 输出: 20
- 一行数据
print(df.iloc[2])
# 输出:
# A 3
# B 30
# C 300
# Name: c, dtype: int64
- 多行数据
print(df.iloc[[0, 3]])
# 输出:
# A B C
# a 1 10 100
# d 4 40 400
- 一列 / 多列数据
print(df.iloc[:, [0, 2]])
# 输出:
# A C
# a 1 100
# b 2 200
# c 3 300
# d 4 400
- 切片(不包含结束索引)
print(df.iloc[1:3, 0:2])
# 输出:
# A B
# b 2 20
# c 3 30
注意:
- 用
.loc当你知道行或列的名称时。- 用
.iloc当你知道行或列的位置时。.loc切片是闭区间(包含结束值),而.iloc是半开区间(不包含结束值)
二、分组与聚合
我们来简单总结一下 Pandas 的分组聚合。
一句话概括: 分组聚合是指先根据一个或多个键将数据分成若干个小组,然后对每个小组独立应用一个聚合函数(如求和、求平均等),最后将结果组合起来的过程。
分组聚合是数据分析中最核心的操作之一,它模拟了 SQL 中的 GROUP BY 语句,核心思想是:拆分 -> 应用 -> 合并。
- 拆分 (Split):按照指定的键将 DataFrame 分割成若干个小组(Group)。
- 应用 (Apply):对每个小组独立地应用一个或多个函数(聚合函数)。
- 合并 (Combine):将每个小组的计算结果合并成一个新的 DataFrame 或 Series。
1.分组 (Grouping):groupby() 的详细用法
groupby() 是实现分组的核心方法。它的灵活性非常高,可以根据多种方式进行分组。
1) 按列名分组 (最常用)
这是最直接的方式,指定一个或多个列名,Pandas 会根据这些列的唯一组合来创建分组。
import pandas as pd
import numpy as np
# 示例数据
data = {
'Category': ['Electronics', 'Electronics', 'Clothing', 'Clothing', 'Electronics'],
'Product': ['Phone', 'Laptop', 'Shirt', 'Pants', 'Tablet'],
'Region': ['North', 'South', 'North', 'South', 'North'],
'Sales': [1000, 1500, 500, 750, 800],
'Quantity': [2, 1, 5, 3, 4]
}
df = pd.DataFrame(data)
print("--- 原始数据 ---")
print(df)
print("\n")
# 1. 按单个列分组
grouped_by_category = df.groupby('Category')
# grouped_by_category 是一个 GroupBy 对象,它本身不显示数据,只是一个分组结构
print("--- 按 'Category' 分组后的 GroupBy 对象 ---")
print(grouped_by_category)
# 你可以查看分组的键
print("\n--- 分组的键 ---")
print(grouped_by_category.groups.keys())
# 你可以查看某个具体的组
print("\n--- 查看 'Electronics' 组的数据 ---")
print(grouped_by_category.get_group('Electronics'))
# 2. 按多个列分组
grouped_by_category_region = df.groupby(['Category', 'Region'])
print("\n--- 按 'Category' 和 'Region' 分组后的 GroupBy 对象 ---")
print(grouped_by_category_region)
print("\n--- 分组的键 (是元组) ---")
print(grouped_by_category_region.groups.keys())
2) 对分组后的结果进行迭代
你可以像遍历字典一样遍历 GroupBy 对象,获取每个组的名称和对应的数据。
print("--- 迭代 GroupBy 对象 ---")
for group_name, group_data in grouped_by_category:
print(f"\nGroup Name: {group_name}")
print("-" * 20)
print(group_data)
2.聚合 (Aggregation):对分组结果应用函数
分组之后,最重要的就是对每个组进行计算。Pandas 提供了多种方式来实现聚合。
1) 使用预定义的聚合方法
GroupBy 对象有许多内置的聚合方法,可以直接调用。
# 按 'Category' 分组后,计算 'Sales' 列的总和
sales_sum_by_category = df.groupby('Category')['Sales'].sum()
print("\n--- 按类别分组后的销售额总和 ---")
print(sales_sum_by_category)
print(type(sales_sum_by_category)) # 结果是一个 Series
# 计算多个列的多个统计量
# 方法一:对所有数值列计算多种统计
stats_by_category = df.groupby('Category').agg(['sum', 'mean', 'count'])
print("\n--- 按类别分组后,所有数值列的 sum, mean, count ---")
print(stats_by_category)
print(type(stats_by_category)) # 结果是一个 DataFrame,列是 MultiIndex
# 方法二:对不同的列应用不同的聚合函数 (推荐,更灵活)
custom_agg_by_category = df.groupby('Category').agg(
总销售额=('Sales', 'sum'),
平均销量=('Quantity', 'mean'),
产品种类数=('Product', 'count') # 或者 'nunique'
)
print("\n--- 按类别分组后,自定义的聚合 ---")
print(custom_agg_by_category)
注意:agg 是 aggregate 的别名,用法完全一样。
3. 使用自定义函数进行聚合 (apply)
如果预定义的函数不能满足需求,你可以定义自己的函数,然后使用 .apply() 方法应用到每个组。
.apply() 会将每个组的 DataFrame 作为参数传递给你的自定义函数。
# 定义一个自定义函数
def calculate_profit_margin(group_df):
"""
假设利润率 = (销售额总和 - 成本) / 销售额总和
这里为了演示,我们假设成本是销售额的一个固定比例,比如60%
"""
total_sales = group_df['Sales'].sum()
cost = total_sales * 0.6
profit = total_sales - cost
profit_margin = profit / total_sales
return profit_margin
# 应用自定义函数
profit_margins = df.groupby('Category').apply(calculate_profit_margin)
print("\n--- 按类别分组后,使用自定义函数计算利润率 ---")
print(profit_margins)
# .apply() 也可以返回一个 Series,从而生成一个有多个列的 DataFrame
def complex_calculation(group_df):
return pd.Series({
'总销售额': group_df['Sales'].sum(),
'最高单价': group_df['Sales'].max() / group_df['Quantity'].max(), # 一个简单的估算
'商品数量': group_df['Product'].nunique()
})
complex_results = df.groupby('Category').apply(complex_calculation)
print("\n--- 按类别分组后,使用自定义函数进行复杂计算 ---")
print(complex_results)
3.分组聚合的进阶技巧
1) transform() 方法
transform() 也会对每个组应用一个函数,但它会将计算结果 ** 广播(broadcast)** 回原来的索引位置,最终返回一个与原始 DataFrame 大小相同的 Series 或 DataFrame。这在需要根据组内统计量创建新列时非常有用。
# 计算每个类别内的销售额占比
df['类别总销售额'] = df.groupby('Category')['Sales'].transform('sum')
df['销售额占比'] = df['Sales'] / df['类别总销售额']
print("\n--- 使用 transform 添加新列 ---")
print(df)
2) filter() 方法
filter() 用于根据组的聚合结果来筛选整个组。它接收一个函数,该函数对每个组的 DataFrame 进行计算并返回一个布尔值。如果返回 True,则保留该组;如果返回 False,则丢弃该组。
# 筛选出总销售额大于 2000 的类别
filtered_df = df.groupby('Category').filter(lambda x: x['Sales'].sum() > 2000)
print("\n--- 使用 filter 筛选组 ---")
print("总销售额大于 2000 的组的数据:")
print(filtered_df)
四、总结
| 方法 | 作用 | 输入 | 输出 | 核心用途 |
|---|---|---|---|---|
| .groupby() | 分组 | 列名或函数 | GroupBy 对象 |
将数据拆分为多个小组。 |
| .agg() / .aggregate() | 聚合 | 字典、列表或函数 | Series 或 DataFrame |
对每个组计算汇总统计量(如 sum, mean)。 |
| .apply() | 应用 | 自定义函数 | 标量、Series 或 DataFrame |
对每个组执行任意复杂的操作和计算。 |
| .transform() | 转换 | 函数 | Series 或 DataFrame (与原数据同形) |
根据组内统计量创建新的列,用于数据标准化、归一化等。 |
| .filter() | 筛选 | 返回布尔值的函数 | DataFrame |
根据组的整体情况过滤掉整个组。 |
- 对于简单的聚合(求和、平均等),优先使用
.agg(),因为它语法简洁且效率高。 - 当需要进行复杂的、无法用简单聚合函数组合完成的计算时,使用
.apply()。 - 当需要将组统计量广播回原始数据行时,使用
.transform()。 - 当需要根据组的属性(如大小、总和)来筛选数据时,使用
.filter()。
三、绘图
Pandas 基础绘图是数据分析中快速可视化数据的重要工具,它基于 Matplotlib 实现,语法简洁、集成度高,能直接对 DataFrame/Series 进行绘图操作。以下从核心逻辑、常用图表类型、基本用法、定制化技巧四方面简单总结:
一、核心逻辑
Pandas 绘图的核心是 “数据优先”:直接调用 DataFrame/Series 的 .plot() 方法,无需手动传递 x/y 数据(默认以索引为 x 轴、列值为 y 轴),一键生成基础图表,大幅简化 Matplotlib 的繁琐配置。
- 关键依赖:需安装
matplotlib(Pandas 绘图的底层引擎),安装命令:pip install matplotlib - 基本语法:
# Series 绘图(默认索引为 x,值为 y)
s.plot(kind="图表类型", ...)
# DataFrame 绘图(默认索引为 x,各列为 y 系列)
df.plot(kind="图表类型", x="指定x列", y=["指定y列1", "指定y列2"], ...)
二、常用图表类型及用途
通过 kind 参数指定图表类型,以下是最常用的 6 种:
| 图表类型(kind 值) | 核心用途 | 适用数据场景 |
|---|---|---|
line(默认) |
展示趋势变化 | 时间序列数据(如每日销量) |
bar/barh |
对比不同类别数据 | 分类统计(如各产品销售额) |
hist |
展示数据分布 | 连续数据的分布特征(如身高) |
pie |
展示占比关系 | 整体中各部分占比(如市场份额) |
scatter |
展示变量相关性 | 两个数值变量的关联(如身高 - 体重) |
box |
展示数据离散程度 | 异常值检测(如成绩分布) |
三、基础用法示例(带输出说明)
假设我们有一个销售数据集 df:
import pandas as pd
import matplotlib.pyplot as plt
# 示例数据:日期、产品A销量、产品B销量、利润
df = pd.DataFrame({
"date": pd.date_range("2024-01-01", periods=5),
"sales_A": [100, 120, 90, 150, 130],
"sales_B": [80, 110, 140, 120, 160],
"profit": [20, 25, 18, 30, 28]
})
df = df.set_index("date") # 以日期为索引(方便时间序列绘图)
1. 折线图(趋势)
# 绘制A/B产品销量趋势(默认line类型)
df[["sales_A", "sales_B"]].plot(
title="产品销量趋势图", # 标题
xlabel="日期", # x轴标签
ylabel="销量", # y轴标签
grid=True # 显示网格
)
plt.show() # 弹出图表窗口(必须调用才会显示)
输出:一条包含两条线的折线图,清晰展示 5 天内 A/B 产品销量的起伏趋势。
2. 柱状图(对比)
# 绘制单日利润柱状图(指定x为日期,y为利润)
df.plot(
kind="bar",
x="date",
y="profit",
color="skyblue", # 柱子颜色
rot=45 # x轴标签旋转45度(避免重叠)
)
plt.title("每日利润对比")
plt.show()
输出:5 个蓝色柱子,分别对应 5 天的利润,直观对比每日利润差异。
3. 直方图(分布)
# 绘制A产品销量的分布直方图
df["sales_A"].plot(
kind="hist",
bins=3, # 分3个区间( bins越多,区间越细)
edgecolor="black" # 柱子边框颜色(区分区间)
)
plt.title("产品A销量分布")
plt.xlabel("销量区间")
plt.ylabel("频次")
plt.show()
输出:3 个柱子,展示 A 产品销量在不同区间的出现次数(如 100-120 区间出现 2 次)。
4. 饼图(占比)
# 绘制A/B产品总销量占比(需指定y为销量列,autopct显示百分比)
df[["sales_A", "sales_B"]].sum().plot(
kind="pie",
autopct="%1.1f%%", # 显示百分比(保留1位小数)
labels=["产品A", "产品B"], # 自定义标签
startangle=90 # 饼图起始角度(90度更美观)
)
plt.title("产品销量占比")
plt.ylabel("") # 隐藏y轴标签(饼图无需y轴)
plt.show()
输出:一个饼图,显示产品 A(约 48.1%)和产品 B(约 51.9%)的总销量占比。
5. 散点图(相关性)
# 绘制销量A与利润的散点图(看相关性)
df.plot(
kind="scatter",
x="sales_A",
y="profit",
color="red",
s=50 # 点的大小
)
plt.title("销量A与利润的相关性")
plt.show()
输出:红色散点,若点呈右上趋势,说明销量 A 越高,利润越高(正相关)。
四、关键定制化技巧
- 子图布局:多个图表并排显示,用
subplots=True+layout=(行数, 列数):
df.plot(
kind="line",
subplots=True, # 每个列单独绘图
layout=(2, 2), # 2行2列布局
figsize=(12, 8) # 整体图大小
)
plt.tight_layout() # 自动调整子图间距(避免标签重叠)
plt.show()
- 样式美化:通过
style参数指定 Matplotlib 样式(如seaborn-darkgrid):
plt.style.use("seaborn-v0_8-darkgrid") # 全局设置样式
df["sales_A"].plot(kind="line", color="orange")
plt.show()
- 保存图片:用
plt.savefig()保存图表(需在plt.show()之前调用):
df.plot(kind="bar", y="profit")
plt.title("每日利润")
plt.savefig("profit_bar.png", dpi=300, bbox_inches="tight") # dpi=分辨率,bbox_inches避免标签被截断
plt.show()
更多推荐



所有评论(0)