数据分析利器:Pandas 基础介绍
在当今数据爆炸的时代,数据分析能力成为了许多领域不可或缺的技能。Python 凭借其简洁的语法和强大的生态系统,成为了最受欢迎的数据分析语言之一。而在 Python 的数据分析工具箱中,pandas 无疑是最核心、最强大的库之一。它提供了高效、灵活的数据结构,使得处理结构化数据变得轻松快捷。
本文将带你深入了解 pandas 的基础知识,包括其核心数据结构、数据的创建、读取、选择、处理缺失值以及一些基本的数据操作。无论你是数据科学的初学者,还是希望提升数据处理效率的开发者,掌握 pandas 都将为你打开数据世界的大门。
1. 什么是 Pandas?为什么选择 Pandas?
Pandas 是一个开源的 Python 库,提供了高性能、易用的数据结构以及数据分析工具。它的名字来源于 “Panel Data”(面板数据),这是一个计量经济学中的术语,也与 “Python Data Analysis” 紧密相关。
Pandas 的核心目标是成为 Python 中进行实际数据分析的最高级、基础的构建模块。它在数据清洗、转换、聚合、筛选等方面表现出色,能够处理各种类型的数据,包括表格数据(如 SQL 表或 Excel 电子表格)、有序或无序的时间序列数据、以及带有行/列标签的任意矩阵数据。
为什么选择 Pandas?
- 强大的数据结构: Pandas 引入了 Series 和 DataFrame 这两个核心数据结构,它们能够优雅地处理带有标签的数据,比 Python 内置的列表或字典更适合进行结构化数据操作。
- 高效的数据操作: Pandas 底层使用了 NumPy,许多操作都在 C 语言级别进行优化,因此处理大量数据时效率很高。
- 丰富的功能: Pandas 提供了丰富的数据操作函数,包括数据对齐、缺失数据处理、数据重塑、数据切片、聚合、分组等,覆盖了数据分析的整个流程。
- 便捷的数据 I/O: 可以轻松读取各种数据格式的文件,如 CSV、Excel、SQL 数据库、HDF5 等,并将数据写入这些格式。
- 与生态系统的集成: Pandas 与 NumPy、Matplotlib、Scikit-learn 等其他科学计算库紧密集成,形成了一个强大的数据科学生态系统。
简而言之,如果你需要在 Python 中进行数据清洗、预处理、探索性分析或数据转换,pandas 是你的首选工具。
2. 安装 Pandas
如果你已经安装了 Anaconda 或 Miniconda,那么 pandas 已经默认安装好了。如果没有,可以通过 pip 安装:
bash
pip install pandas
安装完成后,你可以在 Python 环境中导入 pandas:
python
import pandas as pd
通常我们将 pandas 别名为 pd
,这是一种约定俗成的做法,方便后续代码编写。
3. Pandas 的核心数据结构:Series 和 DataFrame
Pandas 主要提供了两种核心数据结构:Series 和 DataFrame。理解它们是掌握 pandas 的关键。
3.1 Series(系列)
Series 是一种一维带标签的数组。它可以被看作是带有索引(标签)的 NumPy 数组。
- 一维: 它只有一个维度,类似于 Python 的列表或 NumPy 的一维数组。
- 带标签: 它的每个元素都有一个关联的标签,称为索引(index)。这个索引可以是数字,也可以是字符串或其他可哈希的对象。
- 同构数据类型: 默认情况下,Series 中的所有元素都具有相同的数据类型(如整数、浮点数、字符串等),尽管 pandas 在处理不同类型数据时会尝试找到一个兼容的类型(如 object)。
创建 Series:
可以从多种来源创建 Series:
-
从列表或 NumPy 数组创建:
“`python
import pandas as pd
import numpy as np从列表创建
s = pd.Series([1, 3, 5, np.nan, 6, 8])
print(s)输出:
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
从 NumPy 数组创建
arr = np.array([10, 20, 30, 40])
s_from_arr = pd.Series(arr)
print(s_from_arr)输出:
0 10
1 20
2 30
3 40
dtype: int64
“`
在上面的例子中,如果没有指定索引,pandas 会自动创建一个默认的整数索引(从 0 开始)。 -
指定索引创建:
“`python
s_indexed = pd.Series([10, 20, 30, 40], index=[‘a’, ‘b’, ‘c’, ‘d’])
print(s_indexed)输出:
a 10
b 20
c 30
d 40
dtype: int64
“`
现在,我们可以使用自定义的索引标签来访问元素。 -
从字典创建:
“`python
data_dict = {‘北京’: 1500, ‘上海’: 1800, ‘广州’: 1200, ‘深圳’: 1600}
s_from_dict = pd.Series(data_dict)
print(s_from_dict)输出:
北京 1500
上海 1800
广州 1200
深圳 1600
dtype: int64
“`
字典的键会成为 Series 的索引,值会成为 Series 的数据。
Series 的属性:
常用的 Series 属性包括:
.values
: 获取 Series 的数据(以 NumPy 数组形式)。.index
: 获取 Series 的索引。.dtype
: 获取 Series 的数据类型。.shape
: 获取 Series 的形状(元素数量)。.name
: 获取或设置 Series 的名称。
“`python
print(s_indexed.values)
输出: [10 20 30 40]
print(s_indexed.index)
输出: Index([‘a’, ‘b’, ‘c’, ‘d’], dtype=’object’)
print(s_indexed.dtype)
输出: int64
print(s_indexed.shape)
输出: (4,)
s_indexed.name = ‘城市人口’
print(s_indexed)
输出:
a 10
b 20
c 30
d 40
Name: 城市人口, dtype: int64
“`
3.2 DataFrame(数据框)
DataFrame 是 pandas 中最常用的数据结构,它可以被看作是一个二维带标签的数据结构,或者是一个带有行索引和列索引的表格。它类似于电子表格或 SQL 数据库中的表。
- 二维: 它有行和列两个维度。
- 带标签: 它同时拥有行标签(行索引)和列标签(列名)。
- 异构数据类型: DataFrame 的不同列可以包含不同的数据类型(例如,一列是整数,另一列是字符串,再一列是浮点数)。每列可以看作是一个 Series。
创建 DataFrame:
DataFrame 的创建方式更加多样:
-
从字典创建(最常用): 字典的键作为列名,字典的值作为列的数据(通常是列表或 Series)。
“`python
data = {
‘城市’: [‘北京’, ‘上海’, ‘广州’, ‘深圳’],
‘人口’: [1500, 1800, 1200, 1600],
‘面积’: [16410, 6340, 7434, 1997]
}
df = pd.DataFrame(data)
print(df)输出:
城市 人口 面积
0 北京 1500 16410
1 上海 1800 6340
2 广州 1200 7434
3 深圳 1600 1997
“`
默认情况下,行索引是整数 0, 1, 2, …。你也可以指定索引:
“`python
df_indexed = pd.DataFrame(data, index=[‘A’, ‘B’, ‘C’, ‘D’])
print(df_indexed)输出:
城市 人口 面积
A 北京 1500 16410
B 上海 1800 6340
C 广州 1200 7434
D 深圳 1600 1997
“`
-
从列表的列表创建: 列表的每个子列表代表一行数据。你需要同时提供列名。
“`python
data_list = [
[‘北京’, 1500, 16410],
[‘上海’, 1800, 6340],
[‘广州’, 1200, 7434],
[‘深圳’, 1600, 1997]
]
df_from_list = pd.DataFrame(data_list, columns=[‘城市’, ‘人口’, ‘面积’])
print(df_from_list)输出:
城市 人口 面积
0 北京 1500 16410
1 上海 1800 6340
2 广州 1200 7434
3 深圳 1600 1997
“`
-
从字典的 Series 创建:
“`python
data_series = {
‘城市’: pd.Series([‘北京’, ‘上海’, ‘广州’, ‘深圳’]),
‘人口’: pd.Series([1500, 1800, 1200, 1600]),
‘面积’: pd.Series([16410, 6340, 7434, 1997])
}
df_from_series = pd.DataFrame(data_series)
print(df_from_series)输出: (与从字典创建类似,如果 Series 有不同的索引,DataFrame 会合并索引)
城市 人口 面积
0 北京 1500 16410
1 上海 1800 6340
2 广州 1200 7434
3 深圳 1600 1997
“`
DataFrame 的属性:
常用的 DataFrame 属性包括:
.values
: 获取 DataFrame 的数据(以 NumPy 二维数组形式)。.index
: 获取 DataFrame 的行索引。.columns
: 获取 DataFrame 的列索引(列名)。.dtypes
: 获取每列的数据类型。.shape
: 获取 DataFrame 的形状(行数,列数)。.info()
: 打印 DataFrame 的摘要信息,包括索引 dtype、列 dtype 和非空值的数量。.describe()
: 生成描述性统计信息,如计数、均值、标准差、最小值、最大值等,仅针对数值列。
“`python
print(df.values)
输出:
[[‘北京’ 1500 16410]
[‘上海’ 1800 6340]
[‘广州’ 1200 7434]
[‘深圳’ 1600 1997]]
print(df.index)
输出: RangeIndex(start=0, stop=4, step=1)
print(df.columns)
输出: Index([‘城市’, ‘人口’, ‘面积’], dtype=’object’)
print(df.dtypes)
输出:
城市 object
人口 int64
面积 int64
dtype: object
print(df.shape)
输出: (4, 3)
df.info()
输出类似:
RangeIndex: 4 entries, 0 to 3
Data columns (total 3 columns):
# Column Non-Null Count Dtype
— —— ————– —–
0 城市 4 non-null object
1 人口 4 non-null int64
2 面积 4 non-null int64
dtypes: int64(2), object(1)
memory usage: 272.0+ bytes
print(df.describe())
输出:
人口 面积
count 4.000000 4.000000
mean 1525.000000 8045.250000
std 250.000000 6152.952037
min 1200.000000 1997.000000
25% 1425.000000 5255.750000
50% 1550.000000 6887.000000
75% 1650.000000 9676.500000
max 1800.000000 16410.000000
“`
4. 数据加载与读取 (I/O)
实际的数据分析往往从文件读取数据开始。Pandas 提供了强大的 I/O API,支持多种文件格式。最常见的是 CSV 文件。
4.1 读取 CSV 文件
使用 pd.read_csv()
函数:
“`python
假设有一个名为 ‘data.csv’ 的文件
内容可能是:
姓名,年龄,性别
张三,25,男
李四,30,女
王五,22,男
创建一个模拟的 CSV 文件
csv_data = “””姓名,年龄,性别
张三,25,男
李四,30,女
王五,22,男
“””
with open(‘data.csv’, ‘w’, encoding=’utf-8′) as f:
f.write(csv_data)
读取 CSV 文件
try:
df_csv = pd.read_csv(‘data.csv’)
print(“从 CSV 文件读取的数据:”)
print(df_csv)
except FileNotFoundError:
print(“错误:data.csv 文件未找到!”)
输出:
从 CSV 文件读取的数据:
姓名 年龄 性别
0 张三 25 男
1 李四 30 女
2 王五 22 男
“`
pd.read_csv()
有许多有用的参数,例如:
sep
: 指定分隔符(默认为逗号)。header
: 指定哪一行作为列名(默认为 0,即第一行)。index_col
: 指定哪一列作为行索引。names
: 如果文件没有列名,可以手动指定列名。skiprows
: 跳过文件开头的行。nrows
: 只读取文件的前 n 行。encoding
: 指定文件编码(如 ‘utf-8’, ‘gbk’)。parse_dates
: 将指定的列解析为日期时间类型。
4.2 读取 Excel 文件
使用 pd.read_excel()
函数:
“`python
假设有一个名为 ‘data.xlsx’ 的 Excel 文件
需要安装 openpyxl 或 xlrd 库来读写 Excel 文件
pip install openpyxl xlrd
创建一个模拟的 Excel 文件
df_to_excel = pd.DataFrame({
‘产品’: [‘A’, ‘B’, ‘C’],
‘价格’: [100, 150, 200]
})
try:
df_to_excel.to_excel(‘data.xlsx’, index=False)
print(“\n模拟 Excel 文件创建成功。”)
# 读取 Excel 文件
df_excel = pd.read_excel('data.xlsx')
print("从 Excel 文件读取的数据:")
print(df_excel)
except Exception as e:
print(f”\n处理 Excel 文件时发生错误: {e}”)
print(“请确保已安装 openpyxl 和 xlrd 库。”)
输出类似:
模拟 Excel 文件创建成功。
从 Excel 文件读取的数据:
产品 价格
0 A 100
1 B 150
2 C 200
“`
pd.read_excel()
也有类似的参数,如 sheet_name
(指定读取哪个工作表)。
4.3 写入文件
Pandas 对象也有相应的 .to_csv()
, .to_excel()
, .to_sql()
, .to_json()
等方法来将数据写入文件或数据库。
“`python
将 DataFrame 写入 CSV 文件
df.to_csv(‘output.csv’, index=False) # index=False 不写入行索引
将 DataFrame 写入 Excel 文件
df.to_excel(‘output.xlsx’, sheet_name=’Sheet1′, index=False)
print(“\n数据已写入 output.csv 和 output.xlsx”)
“`
5. 数据检查与探索
加载数据后,通常需要先快速浏览一下数据,了解其结构、内容和基本统计特征。
df.head(n=5)
: 显示 DataFrame 的前 n 行(默认为 5)。df.tail(n=5)
: 显示 DataFrame 的后 n 行(默认为 5)。df.info()
: 提供 DataFrame 的简要信息,包括列名、非空值数量、数据类型和内存使用情况。df.describe()
: 生成数值列的描述性统计信息(计数、均值、标准差、最小值、25%分位数、中位数、75%分位数、最大值)。对于非数值列,它会计算非空值的数量、唯一值数量、出现频率最高的词及其频率。df.shape
: 返回 DataFrame 的维度(行数,列数)。df.columns
: 返回列名列表。df.index
: 返回行索引。df['列名'].value_counts()
: 计算某个列中各个唯一值出现的次数,常用于类别型数据。df.isnull().sum()
: 计算每列中缺失值的数量。
“`python
使用之前创建的 df
print(“\n— 数据检查 —“)
print(“前两行:”)
print(df.head(2))
print(“\n后两行:”)
print(df.tail(2))
print(“\n信息概览:”)
df.info()
print(“\n描述性统计:”)
print(df.describe())
print(“\n形状:”)
print(df.shape)
print(“\n列名:”)
print(df.columns)
print(“\n行索引:”)
print(df.index)
假设df有一个’城市’列
print(“\n’城市’列值计数:”)
print(df[‘城市’].value_counts())
添加一些缺失值以演示isnull()
df_with_nan = df.copy()
df_with_nan.loc[1, ‘人口’] = np.nan
df_with_nan.loc[3, ‘面积’] = np.nan
print(“\n包含缺失值的DataFrame:”)
print(df_with_nan)
print(“\n缺失值计数:”)
print(df_with_nan.isnull().sum())
“`
6. 数据选择与切片
从 DataFrame 中选择特定的行、列或单元格是数据分析中最频繁的操作之一。Pandas 提供了多种灵活的方式来实现。
6.1 选择列
可以直接通过列名来选择一列或多列。
-
选择单列: 返回一个 Series。
“`python
print(“\n— 数据选择 —“)
print(“选择单列 ‘人口’:”)
population_series = df[‘人口’]
print(population_series)
print(type(population_series))输出:
“`
-
选择多列: 使用一个列名组成的列表,返回一个 DataFrame。
“`python
print(“\n选择多列 [‘城市’, ‘面积’]:”)
city_area_df = df[[‘城市’, ‘面积’]]
print(city_area_df)
print(type(city_area_df))输出:
``
df.列名
注意,选择单列时,语法也是可以的 (
df.人口),但这种方式在列名包含空格或特殊字符时不适用,且容易与对象方法混淆,因此推荐使用
df[‘列名’]` 语法。
6.2 选择行
选择行可以通过多种方式实现,最常用的是基于索引标签的 .loc[]
和基于整数位置的 .iloc[]
。理解这两者的区别至关重要。
-
基于标签的选择:
.loc[]
.loc[]
主要通过行索引标签和列索引标签(列名)来选择数据。-
选择单行(按行索引标签):
“`python
print(“\n用 .loc[] 选择索引标签为 1 的行:”) # 默认索引是整数 0,1,2,3…
print(df.loc[1]) # 返回一个 Series输出:
城市 上海
人口 1800
面积 6340
Name: 1, dtype: object
print(“\n用 .loc[] 选择索引标签为 ‘B’ 的行 (如果索引是自定义标签):”)
print(df_indexed.loc[‘B’])输出:
城市 上海
人口 1800
面积 6340
Name: B, dtype: object
“`
-
选择多行(按行索引标签列表):
“`python
print(“\n用 .loc[] 选择索引标签为 [0, 2] 的行:”)
print(df.loc[[0, 2]]) # 返回一个 DataFrame输出:
城市 人口 面积
0 北京 1500 16410
2 广州 1200 7434
“`
-
按行索引标签范围切片(包含起始和结束标签):
“`python
print(“\n用 .loc[] 按标签范围切片 (从索引 1 到 3, 包含 3):”)
print(df.loc[1:3]) # 返回一个 DataFrame输出:
城市 人口 面积
1 上海 1800 6340
2 广州 1200 7434
3 深圳 1600 1997
``
.loc[]` 进行标签切片是 包含 结束标签的。
注意:与 Python 列表切片不同,使用 -
同时选择行和列(按标签):
“`python
print(“\n用 .loc[] 选择索引标签为 0 和 2 的行的 ‘人口’ 列:”)
print(df.loc[[0, 2], ‘人口’]) # 返回一个 Series输出:
0 1500
2 1200
Name: 人口, dtype: int64
print(“\n用 .loc[] 选择索引标签从 1 到 3 的行的 [‘城市’, ‘面积’] 列:”)
print(df.loc[1:3, [‘城市’, ‘面积’]]) # 返回一个 DataFrame输出:
城市 面积
1 上海 6340
2 广州 7434
3 深圳 1997
使用 `:` 表示选择所有行或所有列:
python
print(“\n用 .loc[] 选择所有行的 ‘城市’ 列:”)
print(df.loc[:, ‘城市’]) # 相当于 df[‘城市’]输出:
0 北京
1 上海
2 广州
3 深圳
Name: 城市, dtype: object
“`
-
-
基于位置的选择:
.iloc[]
.iloc[]
主要通过行和列的整数位置来选择数据(类似于 NumPy 数组的索引方式)。位置从 0 开始。-
选择单行(按行位置):
“`python
print(“\n用 .iloc[] 选择位置为 1 的行:”)
print(df.iloc[1]) # 返回一个 Series输出:
城市 上海
人口 1800
面积 6340
Name: 1, dtype: object
“`
-
选择多行(按行位置列表):
“`python
print(“\n用 .iloc[] 选择位置为 [0, 2] 的行:”)
print(df.iloc[[0, 2]]) # 返回一个 DataFrame输出:
城市 人口 面积
0 北京 1500 16410
2 广州 1200 7434
“`
-
按行位置范围切片(不包含结束位置):
“`python
print(“\n用 .iloc[] 按位置范围切片 (从位置 1 到 3, 不包含 3):”)
print(df.iloc[1:3]) # 返回一个 DataFrame输出:
城市 人口 面积
1 上海 1800 6340
2 广州 1200 7434
``
.iloc[]` 进行位置切片是 不包含 结束位置的。
注意:与 Python 列表切片一样,使用 -
同时选择行和列(按位置):
“`python
print(“\n用 .iloc[] 选择位置为 0 和 2 的行的 位置为 1 的列:”)
print(df.iloc[[0, 2], 1]) # 返回一个 Series输出:
0 1500
2 1200
Name: 人口, dtype: int64
print(“\n用 .iloc[] 选择位置从 1 到 3 的行的 位置为 0 到 2 的列:”)
print(df.iloc[1:4, 0:2]) # 返回一个 DataFrame输出:
城市 人口
1 上海 1800
2 广州 1200
3 深圳 1600
使用 `:` 表示选择所有行或所有列:
python
print(“\n用 .iloc[] 选择所有行的 位置为 0 的列:”)
print(df.iloc[:, 0]) # 相当于 df[‘城市’]输出:
0 北京
1 上海
2 广州
3 深圳
Name: 城市, dtype: object
“`
-
总结 .loc[]
和 .iloc[]
的区别:
.loc[]
: 基于 标签 或 索引名称。用于按名称选择行和列。切片时包含结束标签。.iloc[]
: 基于 整数位置。用于按位置选择行和列。切片时不包含结束位置。
在大多数情况下,当你知道行或列的名称时使用 .loc
;当你知道行或列在 DataFrame 中的位置时使用 .iloc
。避免在可能的情况下混合使用整数和标签索引,明确使用 .loc
或 .iloc
可以提高代码的可读性和避免潜在的错误。
6.3 布尔索引(条件选择)
布尔索引是一种非常强大的数据筛选方式,可以根据某个条件选择数据。
-
基本布尔索引:
“`python
print(“\n— 布尔索引 —“)选择人口大于 1300 的城市数据
condition = df[‘人口’] > 1300
print(“人口大于 1300 的条件 Series:”)
print(condition)输出:
0 True
1 True
2 False
3 True
Name: 人口, dtype: bool
print(“\n人口大于 1300 的城市数据:”)
print(df[condition]) # 将布尔 Series 直接传入 DataFrame输出:
城市 人口 面积
0 北京 1500 16410
1 上海 1800 6340
3 深圳 1600 1997
“`
-
使用逻辑运算符组合条件:
可以使用
&
(AND),|
(OR),~
(NOT) 运算符组合多个条件。每个条件表达式需要用括号括起来。“`python
选择人口大于 1300 且面积小于 10000 的城市
condition1 = df[‘人口’] > 1300
condition2 = df[‘面积’] < 10000
print(“\n人口大于 1300 且面积小于 10000 的城市数据:”)
print(df[condition1 & condition2])输出:
城市 人口 面积
1 上海 1800 6340
3 深圳 1600 1997
“`
-
使用
isin()
方法:用于判断某一列的值是否在指定的列表或 Series 中。
“`python
选择城市为 ‘北京’ 或 ‘广州’ 的数据
cities_to_select = [‘北京’, ‘广州’]
print(“\n城市为 ‘北京’ 或 ‘广州’ 的数据:”)
print(df[df[‘城市’].isin(cities_to_select)])输出:
城市 人口 面积
0 北京 1500 16410
2 广州 1200 7434
“`
布尔索引返回的结果始终是 DataFrame 的一个副本(view 或 copy 取决于 pandas 版本和操作的复杂性),对其进行修改可能不会影响原 DataFrame。如果需要修改,最好使用 .loc
或 .iloc
结合布尔条件。
“`python
使用 .loc 结合布尔条件修改数据
df_copy = df.copy() # 创建副本以避免修改原始 df
df_copy.loc[df_copy[‘人口’] > 1500, ‘人口’] = df_copy[‘人口’] * 1.1 # 人口大于1500的增长10%
print(“\n人口大于 1500 的增长 10% 后:”)
print(df_copy)
输出:
城市 人口 面积
0 北京 1500.0 # 北京人口不大于1500
1 上海 1980.0 # 上海人口增长10%
2 广州 1200.0
3 深圳 1760.0 # 深圳人口增长10%
“`
7. 处理缺失数据
现实世界中的数据常常是不完整的,处理缺失值是数据清洗的重要步骤。Pandas 使用 NaN
(Not a Number) 来表示浮点数和某些数值类型的缺失值,对于对象类型,也可能使用 None
。
-
检查缺失值:
df.isnull()
或df.isna()
: 返回一个与 DataFrame 形状相同的布尔型 DataFrame,元素为 True 表示该位置是缺失值。df.notnull()
或df.notna()
: 返回布尔型 DataFrame,元素为 True 表示该位置不是缺失值。df.isnull().sum()
: 计算每列的缺失值数量。df.isnull().sum().sum()
: 计算整个 DataFrame 的缺失值总数。
“`python
print(“\n— 处理缺失数据 —“)
print(“DataFrame 包含缺失值:”)
print(df_with_nan)print(“\n是否缺失 (isnull):”)
print(df_with_nan.isnull())print(“\n每列缺失值数量:”)
print(df_with_nan.isnull().sum())输出:
城市 0
人口 1
面积 1
dtype: int64
“`
-
删除缺失值:
-
df.dropna()
: 删除包含缺失值的行或列。“`python
删除包含缺失值的行
df_dropped_rows = df_with_nan.dropna()
print(“\n删除包含缺失值的行:”)
print(df_dropped_rows)输出:
城市 人口 面积
0 北京 1500.0 16410.0
2 广州 1200.0 7434.0
删除包含缺失值的列 (axis=1)
df_dropped_cols = df_with_nan.dropna(axis=1)
print(“\n删除包含缺失值的列:”)
print(df_dropped_cols)输出:
城市
0 北京
1 上海
2 广州
3 深圳
``
dropna()默认删除包含 *任何* 缺失值的行或列 (
how=’any’)。你也可以指定
how=’all’,只删除全部为缺失值的行或列。
thresh` 参数可以指定至少有多少个非缺失值才保留该行/列。
-
-
填充缺失值:
df.fillna(value)
: 用指定的值填充缺失值。df.fillna(method='ffill')
: 使用前一个非缺失值填充(向前填充)。df.fillna(method='bfill')
: 使用后一个非缺失值填充(向后填充)。df.fillna(value=...)
结合其他方法,如均值、中位数等。
“`python
用特定值填充
df_filled_value = df_with_nan.fillna(0)
print(“\n用 0 填充缺失值:”)
print(df_filled_value)输出:
城市 人口 面积
0 北京 1500.0 16410.0
1 上海 0.0 6340.0
2 广州 1200.0 7434.0
3 深圳 1600.0 0.0
用列的均值填充 (通常是更好的做法)
mean_population = df_with_nan[‘人口’].mean()
df_filled_mean = df_with_nan.copy()
df_filled_mean[‘人口’].fillna(mean_population, inplace=True) # inplace=True 直接修改原DataFrame
print(“\n用 ‘人口’ 列均值填充缺失值:”)
print(df_filled_mean)输出:
城市 人口 面积
0 北京 1500.0 16410.0
1 上海 1433.333 6340.0 # (1500+1200+1600)/3 = 1433.333…
2 广州 1200.0 7434.0
3 深圳 1600.0 NaN # ‘面积’列的缺失值未被填充
使用向前填充
df_filled_ffill = df_with_nan.fillna(method=’ffill’)
print(“\n使用向前填充 (ffill):”)
print(df_filled_ffill)输出:
城市 人口 面积
0 北京 1500.0 16410.0
1 上海 1500.0 6340.0 # 人口被前一行的1500填充
2 广州 1200.0 7434.0
3 深圳 1600.0 7434.0 # 面积被前一行的7434填充
``
fillna()默认返回一个新的 DataFrame,使用
inplace=True` 可以直接修改原 DataFrame。
8. 基本数据操作
Pandas 提供了丰富的函数进行数据的计算、统计和转换。
8.1 数学运算和统计
DataFrame 和 Series 支持各种数学运算,并且会自动进行基于索引的对齐。
“`python
print(“\n— 基本数据操作 —“)
print(“原始 DataFrame:”)
print(df)
计算人口密度 (假设面积单位一致)
df[‘人口密度’] = df[‘人口’] / df[‘面积’]
print(“\n添加 ‘人口密度’ 列:”)
print(df)
输出:
城市 人口 面积 人口密度
0 北京 1500 16410 0.091408
1 上海 1800 6340 0.283912
2 广州 1200 7434 0.161421
3 深圳 1600 1997 0.801202
计算列的统计量
print(“\n’人口’ 列的均值:”, df[‘人口’].mean())
print(“‘面积’ 列的总和:”, df[‘面积’].sum())
print(“最大人口:”, df[‘人口’].max())
print(“最小面积:”, df[‘面积’].min())
计算整个 DataFrame 的统计量 (忽略非数值列)
print(“\nDataFrame 均值 (仅数值列):”)
print(df.mean(numeric_only=True)) # pandas 2.0+ 需要 numeric_only=True
输出:
人口 1525.000000
面积 8045.250000
人口密度 0.334486
dtype: float64
按行计算总和
print(“\n按行计算总和 (axis=1):”)
print(df[[‘人口’, ‘面积’]].sum(axis=1))
输出:
0 17910
1 8140
2 8634
3 3597
dtype: int64
``
count()
常用的统计方法包括,
mean(),
median(),
sum(),
std()(标准差),
var()(方差),
min(),
max(),
quantile()(分位数),
cumsum()(累积和),
cumprod()` (累积积) 等。
8.2 应用函数
Pandas 提供了 apply()
方法,可以将一个函数应用到 Series 或 DataFrame 的行或列上。
-
应用于 Series:
“`python
将函数应用于 ‘人口’ 列,判断人口是否大于 1500
def is_large_population(pop):
return pop > 1500df[‘人口多’] = df[‘人口’].apply(is_large_population)
print(“\n应用函数创建新列 ‘人口多’:”)
print(df)输出:
城市 人口 面积 人口密度 人口多
0 北京 1500 16410 0.091408 False
1 上海 1800 6340 0.283912 True
2 广州 1200 7434 0.161421 False
3 深圳 1600 1997 0.801202 True
lambda 函数也很常用:
python
df[‘面积等级’] = df[‘面积’].apply(lambda x: ‘大’ if x > 10000 else ‘小’)
print(“\n应用 lambda 函数创建新列 ‘面积等级’:”)
print(df)输出:
城市 人口 面积 人口密度 人口多 面积等级
0 北京 1500 16410 0.091408 False 大
1 上海 1800 6340 0.283912 True 小
2 广州 1200 7434 0.161421 False 小
3 深圳 1600 1997 0.801202 True 小
“`
-
应用于 DataFrame 的行或列:
默认情况下,
apply()
应用于每一列(axis=0)。使用axis=1
应用于每一行。“`python
计算每行的 人口+面积
df[‘人口+面积’] = df[[‘人口’, ‘面积’]].apply(sum, axis=1)
print(“\n计算每行 人口+面积:”)
print(df)输出:
城市 人口 面积 人口密度 人口多 面积等级 人口+面积
0 北京 1500 16410 0.091408 False 大 17910
1 上海 1800 6340 0.283912 True 小 8140
2 广州 1200 7434 0.161421 False 小 8634
3 深圳 1600 1997 0.801202 True 小 3597
“`
8.3 排序
可以按索引或按值对 DataFrame 进行排序。
-
按索引排序:
“`python
df_sorted_index = df.sort_index(axis=0, ascending=False) # ascending=False 为降序
print(“\n按行索引降序排序:”)
print(df_sorted_index)输出:
城市 人口 面积 人口密度 人口多 面积等级 人口+面积
3 深圳 1600 1997 0.801202 True 小 3597
2 广州 1200 7434 0.161421 False 小 8634
1 上海 1800 6340 0.283912 True 小 8140
0 北京 1500 16410 0.091408 False 大 17910
“`
-
按值排序:
“`python
按 ‘人口’ 列升序排序
df_sorted_population = df.sort_values(by=’人口’)
print(“\n按 ‘人口’ 列升序排序:”)
print(df_sorted_population)输出:
城市 人口 面积 人口密度 人口多 面积等级 人口+面积
2 广州 1200 7434 0.161421 False 小 8634
0 北京 1500 16410 0.091408 False 大 17910
3 深圳 1600 1997 0.801202 True 小 3597
1 上海 1800 6340 0.283912 True 小 8140
按多个列排序 (先按人口降序,再按面积升序)
df_sorted_multi = df.sort_values(by=[‘人口’, ‘面积’], ascending=[False, True])
print(“\n按 [‘人口’, ‘面积’] 排序 (人口降序,面积升序):”)
print(df_sorted_multi)输出:
城市 人口 面积 人口密度 人口多 面积等级 人口+面积
1 上海 1800 6340 0.283912 True 小 8140
3 深圳 1600 1997 0.801202 True 小 3597
0 北京 1500 16410 0.091408 False 大 17910
2 广州 1200 7434 0.161421 False 小 8634
“`
8.4 重命名列或索引
使用 rename()
方法可以修改列名或索引标签。
“`python
重命名列
df_renamed_cols = df.rename(columns={‘城市’: ‘City’, ‘人口’: ‘Population’})
print(“\n重命名列 ‘城市’ 和 ‘人口’:”)
print(df_renamed_cols)
输出:
City Population 面积 人口密度 人口多 面积等级 人口+面积
0 北京 1500 16410 0.091408 False 大 17910
1 上海 1800 6340 0.283912 True 小 8140
2 广州 1200 7434 0.161421 False 小 8634
3 深圳 1600 1997 0.801202 True 小 3597
重命名索引 (使用 df_indexed 演示)
df_renamed_index = df_indexed.rename(index={‘A’: ‘City A’, ‘B’: ‘City B’})
print(“\n重命名索引 ‘A’ 和 ‘B’:”)
print(df_renamed_index)
输出:
城市 人口 面积
City A 北京 1500 16410
City B 上海 1800 6340
C 广州 1200 7434
D 深圳 1600 1997
``
rename()方法也默认返回新对象,使用
inplace=True` 可以直接修改。
9. 数据分组与聚合 (Groupby)
groupby()
是 pandas 中一个非常强大且常用的操作,它实现了数据分组聚合的“split-apply-combine”(分割-应用-合并)模式:
- Split (分割): 根据某个(或多个)键将数据分割成多个组。
- Apply (应用): 对每个组独立地应用函数(如聚合、转换、过滤)。
- Combine (合并): 将结果组合成一个数据结构。
“`python
print(“\n— 数据分组与聚合 (Groupby) —“)
data_sales = {
‘商品’: [‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘C’, ‘A’, ‘B’],
‘地区’: [‘华北’, ‘华东’, ‘华北’, ‘华南’, ‘华东’, ‘华北’, ‘华南’, ‘华北’],
‘销量’: [100, 150, 120, 80, 160, 90, 110, 130]
}
df_sales = pd.DataFrame(data_sales)
print(“原始销售数据:”)
print(df_sales)
按 ‘商品’ 列分组,计算每种商品的平均销量
groupby_product = df_sales.groupby(‘商品’)
print(“\n按 ‘商品’ 分组,计算平均销量:”)
print(groupby_product[‘销量’].mean())
输出:
商品
A 110.0
B 146.666667
C 85.0
Name: 销量, dtype: float64
按 ‘地区’ 列分组,计算总销量
print(“\n按 ‘地区’ 分组,计算总销量:”)
print(df_sales.groupby(‘地区’)[‘销量’].sum())
输出:
地区
华南 190
华东 310
华北 440
Name: 销量, dtype: int64
按多个列分组,计算多个聚合函数
print(“\n按 [‘商品’, ‘地区’] 分组,计算销量总和与数量:”)
groupby_product_region = df_sales.groupby([‘商品’, ‘地区’])
print(groupby_product_region[‘销量’].agg([‘sum’, ‘count’]))
输出:
sum count
商品 地区
A 华北 220 2
华南 110 1
B 华东 310 2
华北 130 1
C 华南 80 1
华北 90 1
“`
聚合函数有很多,如 sum()
, mean()
, size()
, count()
, min()
, max()
, first()
, last()
, nth()
, median()
, std()
, var()
, agg()
(用于同时应用多个聚合函数) 等。
10. 数据合并与连接
在实际应用中,数据可能分散在多个表或文件中,需要将它们合并起来进行分析。Pandas 提供了多种合并数据的方式。
10.1 Concatenation (连接)
pd.concat()
函数可以沿着某个轴(行或列)将多个 DataFrame 或 Series 堆叠起来。
“`python
print(“\n— 数据合并与连接 —“)
df1 = pd.DataFrame({‘A’: [‘A0’, ‘A1’], ‘B’: [‘B0’, ‘B1’]}, index=[0, 1])
df2 = pd.DataFrame({‘A’: [‘A2’, ‘A3’], ‘B’: [‘B2’, ‘B3’]}, index=[2, 3])
print(“DataFrame 1:”)
print(df1)
print(“\nDataFrame 2:”)
print(df2)
按行连接 (axis=0, 默认)
result_row = pd.concat([df1, df2])
print(“\n按行连接:”)
print(result_row)
输出:
A B
0 A0 B0
1 A1 B1
2 A2 B2
3 A3 B3
按列连接 (axis=1)
result_col = pd.concat([df1, df2], axis=1)
print(“\n按列连接:”)
print(result_col)
输出:
A B A B
0 A0 B0 NaN NaN # df1 有索引 0,1; df2 有索引 2,3。合并时根据索引对齐,缺失值填充 NaN
1 A1 B1 NaN NaN
2 NaN NaN A2 B2
3 NaN NaN A3 B3
可以选择忽略原索引,重新生成索引
result_ignore_index = pd.concat([df1, df2], ignore_index=True)
print(“\n按行连接 (忽略原索引):”)
print(result_ignore_index)
输出:
A B
0 A0 B0
1 A1 B1
2 A2 B2
3 A3 B3
“`
10.2 Merging (合并/连接)
pd.merge()
函数可以根据一个或多个键将 DataFrame 组合起来,类似于数据库中的 JOIN 操作。
“`python
df_left = pd.DataFrame({‘key’: [‘K0’, ‘K1’, ‘K2’, ‘K3’],
‘A’: [‘A0’, ‘A1’, ‘A2’, ‘A3’],
‘B’: [‘B0’, ‘B1’, ‘B2’, ‘B3’]})
df_right = pd.DataFrame({‘key’: [‘K0’, ‘K1’, ‘K4’, ‘K5’],
‘C’: [‘C0’, ‘C1’, ‘C2’, ‘C3’],
‘D’: [‘D0’, ‘D1’, ‘D2’, ‘D3’]})
print(“DataFrame Left:”)
print(df_left)
print(“\nDataFrame Right:”)
print(df_right)
内连接 (inner join, 默认) – 只保留键在两个 DataFrame 中都存在的行
merged_inner = pd.merge(df_left, df_right, on=’key’)
print(“\n内连接 (on=’key’):”)
print(merged_inner)
输出:
key A B C D
0 K0 A0 B0 C0 D0
1 K1 A1 B1 C1 D1
左连接 (left join) – 保留左边 DataFrame 的所有行,右边没有匹配的用 NaN 填充
merged_left = pd.merge(df_left, df_right, on=’key’, how=’left’)
print(“\n左连接 (how=’left’):”)
print(merged_left)
输出:
key A B C D
0 K0 A0 B0 C0 D0
1 K1 A1 B1 C1 D1
2 K2 A2 B2 NaN NaN # K2, K3 只在左边,右边没有匹配
3 K3 A3 B3 NaN NaN
外连接 (outer join) – 保留两个 DataFrame 中的所有行,没有匹配的用 NaN 填充
merged_outer = pd.merge(df_left, df_right, on=’key’, how=’outer’)
print(“\n外连接 (how=’outer’):”)
print(merged_outer)
输出:
key A B C D
0 K0 A0 B0 C0 D0
1 K1 A1 B1 C1 D1
2 K2 A2 B2 NaN NaN # K2, K3 只在左边
3 K3 A3 B3 NaN NaN
4 K4 NaN NaN C2 D2 # K4, K5 只在右边
5 K5 NaN NaN C3 D3
右连接 (right join) – 保留右边 DataFrame 的所有行,左边没有匹配的用 NaN 填充
merged_right = pd.merge(df_left, df_right, on=’key’, how=’right’)
print(“\n右连接 (how=’right’):”)
print(merged_right)
输出:
key A B C D
0 K0 A0 B0 C0 D0
1 K1 A1 B1 C1 D1
2 K4 NaN NaN C2 D2 # K4, K5 只在右边
3 K5 NaN NaN C3 D3
``
pd.merge()的常用参数包括:
on(指定连接键)、
left_on和
right_on(左右 DataFrame 的不同连接键)、
left_index和
right_index(使用索引作为连接键)、
how`(连接类型:’inner’, ‘outer’, ‘left’, ‘right’)。
11. 基础可视化
Pandas 对象集成了 Matplotlib 的绘图功能,可以直接从 DataFrame 或 Series 快速生成图表进行初步的可视化探索。
“`python
print(“\n— 基础可视化 —“)
需要安装 Matplotlib
pip install matplotlib
import matplotlib.pyplot as plt
创建一个包含数值数据的 DataFrame
data_plot = {‘类别’: [‘A’, ‘B’, ‘C’, ‘D’],
‘值1’: [10, 25, 15, 30],
‘值2’: [5, 20, 10, 35]}
df_plot = pd.DataFrame(data_plot)
print(“用于绘图的 DataFrame:”)
print(df_plot)
绘制 ‘值1’ 列的柱状图
df_plot.plot(kind=’bar’, x=’类别’, y=’值1′, title=’类别 vs 值1′)
plt.ylabel(‘值1’)
plt.show() # 显示图表
绘制 ‘值1’ 和 ‘值2’ 的线形图
df_plot.plot(kind=’line’, x=’类别’, y=[‘值1’, ‘值2′], title=’类别 vs 值1 和 值2’)
plt.ylabel(‘值’)
plt.show()
绘制散点图
df_scatter = pd.DataFrame(np.random.rand(50, 2), columns=[‘X’, ‘Y’])
df_scatter.plot(kind=’scatter’, x=’X’, y=’Y’, title=’随机散点图’)
plt.show()
``
.plot()
Pandas方法的
kind参数支持多种图表类型,如
‘line’,
‘bar’,
‘hist’(直方图),
‘box’(箱线图),
‘kde’(核密度估计),
‘area’(面积图),
‘pie’(饼图),
‘scatter’` (散点图)。
12. 总结与展望
恭喜你!走到这里,你已经了解了 pandas 最核心和基础的功能:
- 理解了 Series 和 DataFrame 这两个基本数据结构。
- 学会了如何创建和检查 DataFrame。
- 掌握了如何从 CSV 和 Excel 文件读取数据。
- 熟悉了使用
.loc
、.iloc
和布尔索引进行数据选择和筛选。 - 学会了识别和处理缺失值。
- 了解了基本的数学运算、统计、函数应用和排序。
- 初步接触了
groupby
进行数据分组聚合。 - 了解了使用
concat
和merge
合并数据。 - 进行了简单的可视化。
Pandas 的功能远不止于此。在掌握了这些基础后,你可以继续深入学习:
- 更高级的
groupby
操作(transform, filter)。 - 处理时间序列数据。
- 字符串操作。
- 高级的数据重塑(pivot, melt, stack, unstack)。
- MultiIndex(多级索引)。
- 更复杂的数据合并场景。
- 与其他库(如 Seaborn, Scikit-learn)的协同使用。
数据分析是一个实践出真知的过程。最好的学习方法是在实际数据集上不断练习。从简单的数据加载和检查开始,逐步尝试更复杂的数据清洗、转换和分析任务。
Pandas 是你进行高效数据分析的强大伙伴,熟练掌握它将极大地提升你的工作效率和数据处理能力。祝你在数据分析的旅程中一切顺利!