Pandas 入门教程:数据分析的得力助手
引言:为什么选择 Pandas?
在当今数据爆炸的时代,数据分析已成为一项核心技能。无论你是数据科学家、分析师、工程师,还是仅仅想更好地理解你收集的数据,高效的数据处理工具都是必不可少的。在 Python 的生态系统中,Pandas 无疑是进行数据处理和分析的王者库。
Pandas 是一个开源的 Python 库,它提供了高性能、易于使用的数据结构和数据分析工具。它的核心是 Series 和 DataFrame 这两个强大的数据结构,能够轻松处理结构化数据(表格数据),例如我们在 Excel 或数据库中常见的数据形式。
Pandas 的主要优势:
- 强大的数据结构: Series (一维) 和 DataFrame (二维) 能够直观地表示和操作数据。
- 数据读取与写入: 轻松读取多种格式的数据(CSV, Excel, SQL 数据库, HDF5 等)并写入。
- 数据清洗与预处理: 轻松处理缺失值、重复值,进行数据转换、格式化等。
- 数据选择与过滤: 基于标签或位置快速访问、选择和过滤数据。
- 数据聚合与分组: 强大的
groupby
功能实现复杂的数据分组和汇总。 - 数据合并与连接: 类似于 SQL 的
JOIN
操作,合并不同的数据集。 - 时间序列分析: 对时间序列数据有出色的支持。
总而言之,如果你需要使用 Python 处理和分析表格数据,Pandas 是你的首选工具。本教程将带你从零开始,逐步掌握 Pandas 的基础知识和常用操作。
前置准备
在开始之前,请确保你已经安装了 Python 和 Pandas 库。如果还没有安装 Pandas,可以使用 pip 命令:
bash
pip install pandas
通常,我们还会使用 NumPy 库,因为 Pandas 在底层依赖于 NumPy,并且很多操作会用到 NumPy 的功能。Matplotlib 和 Seaborn 库常用于数据可视化,但不是 Pandas 本身的必需品。
bash
pip install numpy matplotlib seaborn
安装完成后,我们通常会按照约定俗成的习惯导入 Pandas:
python
import pandas as pd
import numpy as np # 导入 NumPy 以备用
我们将 pandas
别名为 pd
,这样写代码会更简洁方便。
Pandas 的核心数据结构
Pandas 提供了两个主要的数据结构:
- Series (序列): 一维带标签的数组。可以看作是带有索引的 NumPy 数组,或者单列的电子表格数据。
- DataFrame (数据框): 二维带标签的数据结构,由多个 Series 组成,每一列是一个 Series。可以看作是整个电子表格或数据库表。
1. Series (序列)
Series 是 Pandas 的基本数据结构之一。它类似于一维数组,但与 NumPy 数组不同的是,Series 拥有一个与之关联的索引 (Index)。这个索引可以是数字,也可以是字符串或其他可哈希的对象。
创建 Series:
最简单的创建方式是从 Python 列表或 NumPy 数组创建:
“`python
从列表创建 Series
s1 = pd.Series([1, 3, 5, np.nan, 6, 8])
print(s1)
输出:
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
“`
上面的例子中,我们创建了一个 Series,它自动生成了一个从 0 开始的默认整数索引。np.nan
表示缺失值。
我们也可以指定索引:
“`python
指定索引创建 Series
s2 = pd.Series([10, 20, 30, 40], index=[‘a’, ‘b’, ‘c’, ‘d’])
print(s2)
输出:
a 10
b 20
c 30
d 40
dtype: int64
“`
Series 的属性和操作:
values
: 获取 Series 的值(以 NumPy 数组形式)。index
: 获取 Series 的索引。- 可以通过索引进行访问:
s2['b']
会返回 20。 - 支持 NumPy 的数学运算和过滤:
s1 + 10
,s1[s1 > 5]
。
“`python
print(s2.values)
print(s2.index)
print(s2[‘b’])
print(s1[s1 > 5])
输出:
[10 20 30 40]
Index([‘a’, ‘b’, ‘c’, ‘d’], dtype=’object’)
20
4 6.0
5 8.0
dtype: float64
“`
2. DataFrame (数据框)
DataFrame 是 Pandas 中最常用的数据结构,它是一个二维的表格型数据结构,可以看作是由多个 Series 组成的字典(列名作为键,Series 作为值),或者一个带有行索引和列索引的二维数组。
创建 DataFrame:
最常见的方式是从 Python 字典创建,其中字典的键是列名,值是列表或 Series:
“`python
从字典创建 DataFrame
data = {‘姓名’: [‘张三’, ‘李四’, ‘王五’, ‘赵六’],
‘年龄’: [25, 30, 22, 28],
‘城市’: [‘北京’, ‘上海’, ‘广州’, ‘深圳’]}
df = pd.DataFrame(data)
print(df)
输出:
姓名 年龄 城市
0 张三 25 北京
1 李四 30 上海
2 王五 22 广州
3 赵六 28 深圳
“`
上面创建的 DataFrame 使用了默认的整数索引。我们也可以指定行索引和列的顺序:
“`python
指定索引和列顺序创建 DataFrame
df2 = pd.DataFrame(data, index=[‘a’, ‘b’, ‘c’, ‘d’], columns=[‘姓名’, ‘城市’, ‘年龄’, ‘身高’])
print(df2)
输出:
姓名 城市 年龄 身高
a 张三 北京 25 NaN
b 李四 上海 30 NaN
c 王五 广州 22 NaN
d 赵六 深圳 28 NaN
“`
注意,由于我们指定了 ‘身高’ 列但字典中没有对应的值,Pandas 会自动用 NaN
(Not a Number,表示缺失值)填充。
从列表的列表创建 DataFrame:
“`python
从列表的列表创建 DataFrame
data_list = [[‘张三’, 25, ‘北京’],
[‘李四’, 30, ‘上海’],
[‘王五’, 22, ‘广州’],
[‘赵六’, 28, ‘深圳’]]
df3 = pd.DataFrame(data_list, columns=[‘姓名’, ‘年龄’, ‘城市’])
print(df3)
“`
从文件读取数据
在实际应用中,我们最常从外部文件加载数据到 DataFrame。CSV (Comma Separated Values) 文件是最常见的数据格式之一。
“`python
假设你有一个名为 ‘students.csv’ 的文件
文件内容可能如下:
Name,Age,City
张三,25,北京
李四,30,上海
王五,22,广州
赵六,28,深圳
从 CSV 文件读取数据
try:
df_students = pd.read_csv(‘students.csv’)
print(“\n从 CSV 读取的数据:”)
print(df_students)
except FileNotFoundError:
print(“\n错误:找不到 students.csv 文件。请创建一个示例文件或跳过此步骤。”)
读取其他格式文件也很相似:
pd.read_excel(‘data.xlsx’)
pd.read_sql(‘SELECT * FROM my_table’, connection)
pd.read_json(‘data.json’)
“`
pd.read_csv()
是一个非常强大的函数,有很多参数可以控制分隔符、编码、跳过行、指定列的数据类型等等。
数据概览与检查
加载数据后,第一步通常是快速了解数据的基本情况。
“`python
假设我们已经成功加载了 df_students
if ‘df_students’ in locals():
# 查看前几行数据 (默认前5行)
print(“\ndf_students.head():”)
print(df_students.head())
# 查看后几行数据 (默认后5行)
print("\ndf_students.tail(2):")
print(df_students.tail(2)) # 可以指定行数
# 查看 DataFrame 的基本信息(列、非空值数量、数据类型、内存占用)
print("\ndf_students.info():")
df_students.info()
# 查看数值列的描述性统计信息(计数、均值、标准差、最小值、最大值、四分位数)
print("\ndf_students.describe():")
print(df_students.describe())
# 查看 DataFrame 的形状 (行数, 列数)
print("\ndf_students.shape:", df_students.shape)
# 查看列名
print("\ndf_students.columns:", df_students.columns)
# 查看行索引
print("\ndf_students.index:", df_students.index)
# 查看每列的数据类型
print("\ndf_students.dtypes:")
print(df_students.dtypes)
# 查看某个列的唯一值
print("\ndf_students['City'].unique():", df_students['City'].unique())
# 查看某个列的唯一值及其计数
print("\ndf_students['City'].value_counts():")
print(df_students['City'].value_counts())
“`
这些方法提供了数据的第一手信息,帮助你快速了解数据的结构、类型、是否有缺失值以及数值列的分布概况。
数据选择与访问
访问 DataFrame 中的数据是 Pandas 的核心操作之一。Pandas 提供了多种方式来选择列、行或特定的单元格。
1. 选择列:
可以通过列名直接选择一列或多列。选择单列返回一个 Series,选择多列返回一个 DataFrame。
“`python
选择单列 (返回 Series)
names = df_students[‘Name’]
print(“\n选择单列 ‘Name’:”)
print(names)
选择多列 (返回 DataFrame)
name_city = df_students[[‘Name’, ‘City’]]
print(“\n选择多列 [‘Name’, ‘City’]:”)
print(name_city)
“`
2. 选择行:loc 和 iloc
这是初学者容易混淆的地方,理解 loc
和 iloc
的区别至关重要。
.loc
: 基于标签(Label)进行索引。 适用于按行索引或列名进行选择。.iloc
: 基于整数位置(Integer Location)进行索引。 类似于 Python 的切片,按行号和列号进行选择。
“`python
假设 df_students 的索引是默认的整数索引 (0, 1, 2, 3)
使用 .loc (基于标签,这里默认标签就是整数)
选择索引为 1 的行
row_1_loc = df_students.loc[1]
print(“\n使用 .loc 选择索引为 1 的行:”)
print(row_1_loc) # 返回 Series
选择索引从 0 到 2 的行 (包含 2)
rows_0_to_2_loc = df_students.loc[0:2]
print(“\n使用 .loc 选择索引 0 到 2 (包含):”)
print(rows_0_to_2_loc) # 返回 DataFrame
选择索引 0 和 3 的行
rows_0_and_3_loc = df_students.loc[[0, 3]]
print(“\n使用 .loc 选择索引 0 和 3:”)
print(rows_0_and_3_loc) # 返回 DataFrame
使用 .iloc (基于整数位置)
选择位置为 1 的行 (即第二行)
row_1_iloc = df_students.iloc[1]
print(“\n使用 .iloc 选择位置为 1 的行 (第二行):”)
print(row_1_iloc) # 返回 Series
选择位置从 0 到 2 的行 (不包含 2)
rows_0_to_2_iloc = df_students.iloc[0:2]
print(“\n使用 .iloc 选择位置 0 到 2 (不包含 2):”)
print(rows_0_to_2_iloc) # 返回 DataFrame
选择位置 0 和 3 的行
rows_0_and_3_iloc = df_students.iloc[[0, 3]]
print(“\n使用 .iloc 选择位置 0 和 3:”)
print(rows_0_and_3_iloc) # 返回 DataFrame
“`
同时选择行和列:
loc
和 iloc
都可以接受逗号分隔的两个参数,第一个参数指定行,第二个参数指定列。
“`python
使用 .loc 选择索引 1 到 3 的行,以及 ‘Name’ 和 ‘Age’ 列
subset_loc = df_students.loc[1:3, [‘Name’, ‘Age’]]
print(“\n使用 .loc 选择行 1-3 和指定列:”)
print(subset_loc)
使用 .iloc 选择位置 1 到 3 的行 (不包含 3),以及位置 0 到 2 的列 (不包含 2)
subset_iloc = df_students.iloc[1:3, 0:2]
print(“\n使用 .iloc 选择位置 1-3 (不含) 和列位置 0-2 (不含):”)
print(subset_iloc)
选择特定的单元格
cell_loc = df_students.loc[0, ‘City’] # 第一行 ‘City’ 列的值
print(“\n使用 .loc 选择特定单元格 (第一行 ‘City’):”, cell_loc)
cell_iloc = df_students.iloc[0, 2] # 位置 0 行,位置 2 列的值
print(“使用 .iloc 选择特定单元格 (位置 0 行,位置 2 列):”, cell_iloc)
“`
数据过滤 (条件筛选)
根据某个或多个条件过滤数据是 Pandas 的常用功能。这通过布尔索引实现。
“`python
筛选年龄大于 25 的学生
younger_than_25 = df_students[df_students[‘Age’] > 25]
print(“\n年龄大于 25 的学生:”)
print(younger_than_25)
筛选城市是 ‘北京’ 的学生
from_beijing = df_students[df_students[‘City’] == ‘北京’]
print(“\n城市是 ‘北京’ 的学生:”)
print(from_beijing)
组合多个条件 (使用 & 表示与,| 表示或,~ 表示非)
筛选年龄大于 25 并且城市是 ‘上海’ 或 ‘深圳’ 的学生
complex_filter = df_students[(df_students[‘Age’] > 25) & ((df_students[‘City’] == ‘上海’) | (df_students[‘City’] == ‘深圳’))]
print(“\n年龄大于 25 且城市为上海或深圳的学生:”)
print(complex_filter)
使用 isin() 方法进行多值筛选
cities_to_filter = [‘上海’, ‘深圳’]
complex_filter_isin = df_students[(df_students[‘Age’] > 25) & (df_students[‘City’].isin(cities_to_filter))]
print(“\n使用 isin() 进行多值筛选:”)
print(complex_filter_isin)
“`
处理缺失数据
实际数据中经常包含缺失值(通常表示为 NaN
)。Pandas 提供了便捷的方法来识别、删除或填充缺失值。
“`python
创建一个包含缺失值的示例 DataFrame
data_missing = {‘A’: [1, 2, np.nan, 4],
‘B’: [5, np.nan, np.nan, 8],
‘C’: [9, 10, 11, 12]}
df_missing = pd.DataFrame(data_missing)
print(“\n包含缺失值的 DataFrame:”)
print(df_missing)
检查哪些位置是缺失值
print(“\ndf_missing.isnull():”)
print(df_missing.isnull()) # 返回布尔型 DataFrame
检查哪些位置不是缺失值
print(“\ndf_missing.notnull():”)
print(df_missing.notnull())
统计每列的缺失值数量
print(“\ndf_missing.isnull().sum():”)
print(df_missing.isnull().sum()) # 对于 DataFrame,sum(轴=0) 默认按列求和
删除包含缺失值的行
df_dropped_rows = df_missing.dropna()
print(“\n删除包含缺失值的行:”)
print(df_dropped_rows)
删除包含缺失值的列 (how=’all’ 表示只删除全为缺失值的列)
df_dropped_cols = df_missing.dropna(axis=1, how=’any’) # axis=1 表示列,how=’any’ 表示只要有缺失值就删除该列
print(“\n删除包含缺失值的列:”)
print(df_dropped_cols)
填充缺失值
用一个固定值填充
df_filled_0 = df_missing.fillna(0)
print(“\n用 0 填充缺失值:”)
print(df_filled_0)
用该列的均值填充
mean_A = df_missing[‘A’].mean()
df_filled_mean = df_missing.fillna({‘A’: mean_A, ‘B’: df_missing[‘B’].median()}) # 可以为不同列指定不同的填充值
print(“\n用列均值/中位数填充缺失值:”)
print(df_filled_mean)
使用前向填充 (用前一个非缺失值填充)
df_filled_ffill = df_missing.fillna(method=’ffill’)
print(“\n前向填充缺失值 (ffill):”)
print(df_filled_ffill)
使用后向填充 (用后一个非缺失值填充)
df_filled_bfill = df_missing.fillna(method=’bfill’)
print(“\n后向填充缺失值 (bfill):”)
print(df_filled_bfill)
``
dropna()
请注意,和
fillna()默认情况下不会修改原始 DataFrame,而是返回一个新的 DataFrame。如果想修改原始 DataFrame,可以使用
inplace=True参数,例如
df_missing.dropna(inplace=True)`。
数据处理与转换
Pandas 提供了丰富的数据处理和转换功能。
1. 添加/修改/删除列:
添加新列就像给字典添加新的键值对一样简单:
“`python
添加新列 ‘Score’
df_students[‘Score’] = [95, 88, 75, 92]
print(“\n添加 ‘Score’ 列:”)
print(df_students)
添加基于现有列计算的新列 ‘Age_in_5_Years’
df_students[‘Age_in_5_Years’] = df_students[‘Age’] + 5
print(“\n添加计算得出的 ‘Age_in_5_Years’ 列:”)
print(df_students)
修改现有列的值 (例如,将所有城市名称加上后缀)
df_students[‘City’] = df_students[‘City’] + ‘市’
print(“\n修改 ‘City’ 列:”)
print(df_students)
删除列
df_students_dropped = df_students.drop(‘Age_in_5_Years’, axis=1) # axis=1 表示删除列
print(“\n删除 ‘Age_in_5_Years’ 列:”)
print(df_students_dropped)
删除多列
df_students_dropped_multi = df_students.drop([‘Score’, ‘City’], axis=1)
print(“\n删除多列 ‘Score’ 和 ‘City’:”)
print(df_students_dropped_multi)
“`
2. 应用函数 (apply):
apply()
方法可以沿 DataFrame 的轴(行或列)应用函数。
“`python
对 ‘Age’ 列的每个值应用一个函数 (例如,判断是否成年)
df_students[‘Is_Adult’] = df_students[‘Age’].apply(lambda x: ‘Yes’ if x >= 18 else ‘No’)
print(“\n使用 apply() 添加 ‘Is_Adult’ 列:”)
print(df_students)
对 DataFrame 的每一行应用函数 (axis=1)
例如,创建一个 ‘Name_Age’ 列,格式为 “姓名 (年龄)”
df_students[‘Name_Age’] = df_students.apply(lambda row: f”{row[‘Name’]} ({row[‘Age’]})”, axis=1)
print(“\n使用 apply(axis=1) 创建 ‘Name_Age’ 列:”)
print(df_students)
“`
分组和聚合
groupby()
是 Pandas 中进行数据分组和聚合分析的强大工具,类似于 SQL 中的 GROUP BY。它将 DataFrame 按照指定的列(或多个列)分割成若干个组,然后对每个组独立地应用一个函数(聚合、转换或过滤)。
“`python
创建一个示例 DataFrame 用于分组
data_sales = {‘Category’: [‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘A’, ‘C’],
‘Product’: [‘X’, ‘Y’, ‘Z’, ‘W’, ‘V’, ‘U’, ‘T’],
‘Sales’: [100, 150, 120, 80, 200, 110, 90],
‘Quantity’: [10, 15, 12, 8, 20, 11, 9]}
df_sales = pd.DataFrame(data_sales)
print(“\n用于分组的销售数据:”)
print(df_sales)
按 ‘Category’ 列分组,并计算每组的总销售额
sales_by_category = df_sales.groupby(‘Category’)[‘Sales’].sum()
print(“\n按 ‘Category’ 分组计算总销售额:”)
print(sales_by_category)
按 ‘Category’ 分组,并计算每组的平均销售额和总数量
sales_stats_by_category = df_sales.groupby(‘Category’).agg({‘Sales’: ‘mean’, ‘Quantity’: ‘sum’})
print(“\n按 ‘Category’ 分组计算平均销售额和总数量:”)
print(sales_stats_by_category)
按多个列分组 (例如 Category 和 Product)
sales_by_category_product = df_sales.groupby([‘Category’, ‘Product’])[‘Sales’].sum()
print(“\n按 ‘Category’ 和 ‘Product’ 分组计算总销售额:”)
print(sales_by_category_product)
print(type(sales_by_category_product)) # 多层索引的 Series
“`
groupby()
返回一个 GroupBy 对象,你需要对这个对象应用一个聚合函数(如 sum()
, mean()
, count()
, size()
, min()
, max()
, std()
, var()
, first()
, last()
, agg()
等)才能得到结果。
数据保存
分析完成后,你可能需要将结果保存到文件。同样,最常见的格式是 CSV。
“`python
将 DataFrame 保存为 CSV 文件
index=False 表示不将 DataFrame 的行索引写入文件
df_students.to_csv(‘students_processed.csv’, index=False)
print(“\nDataFrame 已保存到 students_processed.csv (不包含索引)”)
如果你想包含索引
df_students.to_csv(‘students_processed_with_index.csv’, index=True)
保存为 Excel 文件 (需要安装 openpyxl 或 xlsxwriter 库)
pip install openpyxl
df_students.to_excel(‘students_processed.xlsx’, index=False)
“`
总结与下一步
恭喜你!你已经学习了 Pandas 的基础知识,包括:
- Pandas 的引入和核心数据结构 (Series 和 DataFrame)
- 创建和加载数据 (从字典、列表、CSV 文件)
- 查看数据概览 (
head
,info
,describe
,shape
,columns
,index
,dtypes
) - 数据选择和访问 (
[]
,.loc
,.iloc
) - 数据过滤 (布尔索引)
- 处理缺失值 (
isnull
,dropna
,fillna
) - 基本数据处理 (
apply
, 添加/修改/删除列) - 数据分组和聚合 (
groupby
,agg
) - 数据保存 (
to_csv
)
这只是 Pandas 功能的冰山一角。Pandas 还有更多强大的功能,例如:
- 数据合并 (
merge
,join
,concat
) - 透视表 (
pivot_table
) - 时间序列处理
- 类别数据处理
- 更高级的数据清洗和转换技术
掌握了本教程的基础知识,你已经具备了使用 Pandas 进行初步数据分析和处理的能力。接下来的学习可以深入研究 Pandas 的官方文档,通过实际的项目练习来巩固和扩展你的技能。结合 Matplotlib 和 Seaborn 进行数据可视化,能让你的数据分析结果更加直观和有说服力。
不断实践,探索更多 Pandas 的功能,它将成为你在数据世界里最强大的工具之一!