Pandas数据分析
1.Pandas简介
1.1 Pandas是什么?
Pandas 是 Python 数据分析工具链中最核心的库,充当数据读取、清洗、分析、统计、输出的高效工具。
Pandas 是一个开源的数据分析和数据处理库,它是基于 Python 编程语言的。
Pandas 提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据(类似于Excel表格)。
Pandas 是数据科学和分析领域中常用的工具之一,它使得用户能够轻松地从各种数据源中导入数据,并对数据进行高效的操作和分析。
Pandas是基于NumPy构建的专门为处理表格和混杂数据设计的Python库,其核心设计理念包括:
• 标签化数据结构:提供带标签的轴(行索引和列名)
• 灵活处理缺失数据:内置NaN处理机制
• 智能数据对齐:自动按标签对齐数据
• 强大IO工具:支持从CSV、Excel、SQL等20+数据源读写
• 时间序列处理:原生支持日期时间处理和频率转换
名称由来
pandas这个名字源于panel data(面板数据,这是多维结构化数据集在计量经济学中的术语)以及Python data analysis(Python数据分析)。
pandas兼具numpy高性能的数组计算功能以及电子表格和关系型数据库(如SQL)灵活的数据处理功能。它提供了复杂精细的索引功能,能更加便捷地完成重塑、切片和切块、聚合以及选取数据子集等操作。
pandas功能:
• 有标签轴的数据结构
在数据结构中,每个轴都被赋予了特定的标签,这些标签用于标识和引用轴上的数据元素,使得数据的组织、访问和操作更加直观和方便
应用场景
| 工具 | 功能特色 | 适用场景 |
|---|---|---|
| Excel | 图形界面,简单上手 | 人工分析、小规模数据 |
| SQL | 高效读写,最终数据源 | 数据库查询和联表 |
| Python + Pandas | 算法和分析部署核心 | 数据清洗,统计分析,可视化等 |
-
与Excel对比:
○ 优势:▪ 处理百万级数据不卡顿(Excel约100万行限制)▪ 可复用的分析流程(脚本 vs 手工操作)▪ 支持复杂数据转换(如:分组聚合、时间重采样)
○ 局限:▪ 可视化交互性较弱▪ 学习曲线较陡峭 -
与数据库对比:
○ 优势:▪ 无需SQL知识即可分析▪ 适合探索性分析(即时反馈)▪ 丰富的数据清洗函数
○ 局限:▪ 数据量受内存限制▪ 不适合高并发访问 -
与纯Python代码对比:
○ 优势:▪ 向量化运算比for循环快10-100倍▪ 内置统计分析方法(如:相关系数计算)▪ 丰富的数据透视功能
○ 局限:▪ 需要额外学习Pandas API

行业洞见:根据2023年Kaggle调查,Pandas是数据科学家使用率最高的工具(占比93%),远超第二名Excel(占比32%)
1.2 了解 Pandas 核心数据结构:Series 和 DataFrame
Pandas 基于 Numpy,并提供了 2 大核心数据结构:
• Series:一维带有标签的数组
• DataFrame:二维表格结构,可看作多个 Series 的组合
用得最多的pandas对象是Series,一个一维的标签化数组对象,另一个是DataFrame,它是一个面向列的二维表结构。
| 特性 | Series | DataFrame |
|---|---|---|
| 维度 | 一维 | 二维 |
| 索引 | 单索引 | 行索引 + 列名 |
| 数据存储 | 同质化数据类型 | 各列可不同数据类型 |
| 类比 | Excel 单列 | 整张 Excel 工作表 |
| 创建方式 | pd.Series([1, 2, 3]) | pd.DataFrame({'col': [1, 2, 3]}) |


Pandas 与 Numpy 的关系与区别
就像学习数学要先掌握算术才能学代数一样,NumPy就是数据分析的"算术基础"。虽然可以直接用计算器(Pandas),但理解底层原理才能走得更远。
2. 核心数据结构:Series
2.1 创建与访问
什么是Series
类似于 NumPy 一维数组,但增加了 “标签”,可以理解为「一维标签化数组」
Series 是 Pandas 中的一个核心数据结构,类似于一个一维的数组,具有数据和索引。
Series 可以存储任何数据类型(整数、浮点数、字符串等),并通过标签(索引)来访问元素。Series 的数据结构是非常有用的,因为它可以处理各种数据类型,同时保持了高效的数据操作能力,比如可以通过标签来快速访问和操作数据。

Series 特点:
• 一维数组:Series 中的每个元素都有一个对应的索引值。
• 索引: 每个数据元素都可以通过标签(索引)来访问,默认情况下索引是从 0 开始的整数,但你也可以自定义索引。
• 数据类型: Series 可以容纳不同数据类型的元素,包括整数、浮点数、字符串、Python 对象等。
• 大小不变性:Series 的大小在创建后是不变的,但可以通过某些操作(如 append 或 delete)来改变。
• 操作:Series 支持各种操作,如数学运算、统计分析、字符串处理等。
• 缺失数据:Series 可以包含缺失数据,Pandas 使用NaN(Not a Number)来表示缺失或无值。
• 自动对齐:当对多个 Series 进行运算时,Pandas 会自动根据索引对齐数据,这使得数据处理更加高效。
我们可以使用 Pandas 库来创建一个 Series 对象,并且可以为其指定索引(Index)、名称(Name)以及值(Values):
Python
import pandas as pd
s = pd.Series([10, 20, 30], index=["a", "b", "c"])
创建Series
直接通过列表创建Series
Python
import pandas as pd
s = pd.Series([4, 7, -5, 3])
print(s)
# 0 4
# 1 7
# 2 -5
# 3 3
# dtype: int64
Series的字符串表现形式为:索引在左边,值在右边。由于我们没有为数据指定索引,于是会自动创建一个0到N-1(N为数据的长度)的整数型索引。
• 通过列表创建Series时指定索引
Python
s = pd.Series([4, 7, -5, 3], index=["a", "b", "c", "d"])
print(s)
# a 4
# b 7
# c -5
# d 3
# dtype: int64
• 通过列表创建Series时指定索引和名称
Python
s = pd.Series([4, 7, -5, 3], index=["a", "b", "c", "d"],name="hello_python")
print(s)
# a 4
# b 7
# c -5
# d 3
# Name: hello_python, dtype: int6
名称的作用,与变量名的区别
在 Pandas 的 Series 中,name 参数用于给整个 Series 对象赋予一个名称。这个名称有以下几个用途:
-
标识作用
• name 可以作为 Series 的标识,类似于给数据列取一个名字。
• 当你打印 Series 时,name 会显示在输出的最下方(如你的例子所示)。 -
DataFrame 列名
• 如果你将一个 Series 转换成 DataFrame 或与其他 DataFrame 合并,name 会自动成为列名。
• 例如:
Python
df = s.to_frame() # 转换为 DataFrame,列名就是 "hello_python"
print(df)
输出:
Plain Text
hello_python
a 4
b 7
c -5
d 3
- 对齐操作
• 在 Pandas 运算(如 concat、merge 等)时,name 可以帮助对齐数据。 - 导出数据
• 当你将 Series 导出为 CSV 或其他格式时,name 会成为列名。
name 的主要作用是 给 Series 一个标识,方便后续数据处理、合并或导出。如果只是单独使用 Series,name 可能看起来作用不大,但在更复杂的数据操作中(如 DataFrame 整合),它会很有用。
• 直接通过字典创建Series
Python
dic = {"a": 4, "b": 7, "c": -5, "d": 3}
s = pd.Series(dic)
print(s)
# a 4
# b 7
# c -5
# d 3
# dtype: int64
s1 = pd.Series(dic,index=["a","c"],name="aacc")
print(s1)
# a 4
# c -5
# Name: aacc, dtype: int64
Series的常用属性
| 属性 | 说明 |
|---|---|
| index | Series 的索引对象 |
| values | Series 的值 |
| dtype 或 dtypes | Series 的元素类型 |
| shape | Series 的形状 |
| ndim | Series 的维度 |
| size | Series 的元素个数 |
| name | Series 的名称 |
| loc[] | 显式索引,按标签索引或切片 |
| iloc[] | 隐式索引,按位置索引或切片 |
| at[] | 使用标签访问单个元素 |
| iat[] | 使用位置访问单个元素 |
Python
import pandas as pd
arrs = pd.Series([11,22,33,44,55],name="atguigu",index=["a","b","c","d","e"])
# print(arrs)
# index Series的索引对象
print(arrs.index)
for i in arrs.index:
print(i)
print(arrs.values) # values Series的值
print(arrs.ndim) # ndim Series的维度
print(arrs.shape) # shape Series的形状
print(arrs.size) # size Series的元素个数
# dtype或dtypes Series的元素类型
print(arrs.dtype)
print(arrs.dtypes)
# name Series的名称
print(arrs.name)
# loc[] 显式索引,按标签索引或切片
print(arrs.loc["c"])
print(arrs.loc["c":"d"])
# iloc[] 隐式索引,按位置索引或切片
print(arrs.iloc[0])
print(arrs.iloc[0:3])
# at[] 使用标签访问单个元素 不支持切片
print(arrs.at["a"])
# iat[] 使用位置访问单个元素 不支持切片
print(arrs.iat[3])
访问Series数据
以下是 Pandas 中访问 Series 数据的主要方法汇总表格:
| 方法分类 | 语法示例 | 描述 | 返回值 | 是否支持切片/布尔索引 |
|---|---|---|---|---|
| 位置索引 | s.iloc[0] | 通过整数位置访问(从 0 开始) | 标量值 | 是 |
s.iloc[1:3] | 位置切片(左闭右开) | Series | ||
| 标签索引 | s.loc['a'] | 通过索引标签访问 | 标量值 | 是 |
s.loc[['a','b']] | 通过标签列表访问 | Series | ||
| 直接索引 | s[0] | 类似 iloc(当索引非整数时可能混淆) | 标量值 / Series | 是 |
s['a'] | 类似 loc(优先标签索引) | |||
| 布尔索引 | s[s > 3] | 通过布尔条件筛选 | Series | 是 |
s[~(s > 3)] | 取反条件 | |||
| 函数访问 | s.at['a'] | 快速访问单个标签(类似 loc 但效率更高) | 标量值 | 否 |
s.iat[0] | 快速访问单个位置(类似 iloc 但效率更高) | |||
| 头部/尾部 | s.head(3) | 访问前 N 行(默认 5) | Series | 否 |
s.tail(2) | 访问后 N 行(默认 5) | |||
| 取唯一值 | s.unique() | 返回唯一值数组 | ndarray | 否 |
| 值计数 | s.value_counts() | 统计各值出现次数 | Series |
- 优先使用**loc****/**iloc:直接索引[]的行为可能因索引类型不同而变化,明确场景时建议显式使用loc(标签)或iloc(位置)。
- 切片差异:
○ loc切片为闭区间(包含两端)
○ iloc切片为左闭右开(与Python列表一致) - 布尔索引:常用于条件过滤,如s[s > 3 & s < 10]。
Python
s = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
# 位置索引
print(s.iloc[0]) # 10
# 标签访问
s["a"]
# 标签索引
print(s.loc['b']) # 20
# 布尔索引
print(s[s > 25]) # c:30, d:40
# 花式索引
print(s[['a', 'c']]) # a:10, c:30
#使用布尔索引从Series中筛选满足某些条件的值
bools = s > s.mean() # 将大于平均值的元素标记为 True
print(bools)
# a False
# b True
# c True
# d False
# dtype: bool
print(s[bools])
# b 3.5
# c 6.8
# dtype: float64
# 使用where过滤
print(s.where(s > 20, -1)) # 小于等于20的值替换为-1
2.2 Series的运算
Plain Text
s1 = pd.Series([1, 2, 3, 4])
s2 = pd.Series([10, 20, 30, 40])
# 基本运算
print(s1 + s2) # 对应位置相加
print(s1 * 2) # 标量乘法
2.3 常用方法与统计
| 用途分类 | 方法 | 说明 | 示例代码 |
|---|---|---|---|
| 数据预览 | head() | 查看前 n 行数据,默认 5 行 | s.head(3) |
| 数据预览 | tail() | 查看后 n 行数据,默认 5 行 | s.tail(2) |
| 条件判断 | isin() | 判断元素是否包含在参数集合中 | s.isin([1, 2]) |
| 缺失值处理 | isna() | 判断是否为缺失值(如 NaN 或 None) | s.isna() |
| 聚合统计 | sum() | 求和,自动忽略缺失值 | s.sum() |
| 聚合统计 | mean() | 平均值 | s.mean() |
| 聚合统计 | min() | 最小值 | s.min() |
| 聚合统计 | max() | 最大值 | s.max() |
| 聚合统计 | var() | 方差 | s.var() |
| 聚合统计 | std() | 标准差 | s.std() |
| 聚合统计 | median() | 中位数 | s.median() |
| 聚合统计 | mode() | 众数(可返回多个) | s.mode() |
| 聚合统计 | quantile(q) | 分位数,q 取 0~1 之间 | s.quantile(0.25) |
| 聚合统计 | describe() | 常见统计信息(count、mean、std、min、25%、50%、75%、max) | s.describe() |
| 频率统计 | value_counts() | 每个唯一值的出现次数 | s.value_counts() |
| 频率统计 | count() | 非缺失值数量 | s.count() |
| 频率统计 | nunique() | 唯一值个数(去重) | s.nunique() |
| 唯一处理 | unique() | 获取去重后的值数组 | s.unique() |
| 唯一处理 | drop_duplicates() | 去除重复项 | s.drop_duplicates() |
| 抽样分析 | sample() | 随机抽样 | s.sample(2) |
| 排序操作 | sort_index() | 按索引排序 | s.sort_index() |
| 排序操作 | sort_values() | 按值排序 | s.sort_values() |
| 替换值 | replace() | 替换值 | s.replace({1: 100}) |
| 转换结构 | to_frame() | 将 Series 转为 DataFrame | s.to_frame() |
| 比较判断 | equals() | 判断两个 Series 是否完全相等 | s1.equals(s2) |
| 信息提取 | keys() | 返回 Series 的索引对象 | s.keys() |
| 统计关系 | corr() | 计算相关系数(默认皮尔逊) | s1.corr(s2) |
| 统计关系 | cov() | 协方差 | s1.cov(s2) |
| 可视化 | hist() | 绘制直方图(需安装 matplotlib) | s.hist() |
| 遍历操作 | items() | 返回索引和值的迭代器 | for i, v in s.items(): print(i, v) |
Python
import pandas as pd
import numpy as np
arrs = pd.Series([11,22,np.nan,None,44,22],index=['a','b','c','d','e','f'])
# head() 查看前n行数据,默认5行
print(arrs.head())
# tail() 查看后n行数据,默认5行
print(arrs.tail(3))
# describe() 常见统计信息
print(arrs.describe())
# count() 非缺失值元素的个数
print(arrs.count())
# keys() 返回Series的索引对象
print(arrs.index)
print(arrs.keys())
# isin() 判断数组中的每一个元素是否包含在参数集合中
print(arrs.isin([11]))
# isna() 元素是否为缺失值
print(arrs.isna())
#统计
# sum() 求和,会忽略 Series 中的缺失值
print(arrs.sum())
# mean() 平均值
print(arrs.mean())
# min() 最小值
print(arrs.min())
# max() 最大值
print(arrs.max())
# var() 方差 每个元素与平均值的差 的平方 的和
print(arrs.var())
# std() 标准差 方差的平方根
print(arrs.std())
# print(arrs.var())
# median() 中位数
# 若数据集的元素个数为奇数,中位数就是排序后位于中间位置的数值。
# 若数据集的元素个数为偶数,中位数则是排序后中间两个数的平均值。
# 去除缺失值之后,arrs 就变成了 [11, 22, 44, 22]。
# 对 [11, 22, 44, 22] 进行排序,得到 [11, 22, 22, 44]
print(arrs.median())
# mode() 众数
print(arrs.mode())
# quantile() 指定位置的分位数,如quantile(0.5)
# 分位数:分位数是把一组数据按照从小到大的顺序排列后,分割成若干等份的数值点。
# 0.25 分位数就是将数据从小到大排序后,位于 25% 位置处的数值。
# 插值方法:当计算分位数时,若位置不是整数,就需要借助插值方法来确定分位数值。# "midpoint" 插值方法是指当分位数位置处于两个数据点之间时,取这两个数据点的
# 平均值作为分位数值。
# 对于有 n个数据点的有序数据集,q分位数的位置 i可以通过公式 i=(n−1)q来计
# 算。这里 n=4,q=0.25,则 i=(4−1)×0.25=0.75。这意味着 0.25 分位数处于第一个# 数据点(值为 11)和第二个数据点(值为 22)之间。使用 "midpoint" 插值方法,
# 分位数值就是这两个数据点的平均值,即 (11+22)÷2=16.5
print(arrs.quantile(0.25, interpolation="midpoint"))
print(len(arrs))
# drop_duplicates() 去重 这里可以看出,底层None也作为NaN处理
print(arrs.drop_duplicates())
# unique() 去重后的数组
print(arrs.unique())
# nunique() 去重后非缺失值元素元素个数
print(arrs.nunique())
# sample() 随机采样
print(arrs.sample())
# value_counts() 每个元素的个数
print(arrs.value_counts())
# sort_index() 按索引排序
print(arrs.sort_index())
# sort_values() 按值排序
print(arrs.sort_values())
# replace() 用指定值代替原有值
print(arrs.replace(22,"haha"))
# to_frame() 将Series转换为DataFrame
print(arrs.to_frame())
# equals() 判断两个Series是否相同
arr1 = pd.Series([1,2,3])
arr2 = pd.Series([1,2,3])
print(arr1.equals(arr2))
# corr() 计算与另一个Series的相关系数
# arr1.corr(arr2):由于 arr1 和 arr2 的值完全相同,它们之间是完全正相关的,
#因此相关系数为 1。
# arr1.corr(arr3):arr1 的值是递增的,而 arr3 的值是递减的,它们之间是完全
# 负相关的,所以相关系数为 -1。
# arr1.corr(arr4):arr1 和 arr4 的值都是递增的,且变化趋势一致,它们之间是
# 完全正相关的,相关系数为 1。
# arr5.corr(arr6):arr5 和 arr6 的值之间没有明显的线性关系,它们的相关系数
# 为 0。
arr3 = pd.Series([3,2,1])
arr4 = pd.Series([6,7,8])
arr5 = pd.Series([1, -1, 1, -1])
arr6 = pd.Series([1, 1, -1, -1])
print(arr1.corr(arr2))
print(arr1.corr(arr3))
print(arr1.corr(arr4))
print(arr5.corr(arr6))
# cov() 计算与另一个Series的协方差
# 协方差用于衡量两个变量的总体误差,其值的正负表示两个变量的变化方向关系:
# 正值表示同向变化,负值表示反向变化。
print(arr1.cov(arr3))
2.4 统计实例
学生成绩统计
创建一个包含10名学生数学成绩的Series,成绩范围在50-100之间。计算平均分、最高分、最低分,并找出高于平均分的学生人数。
Python
import pandas as pd
import numpy as np
# 生成随机成绩
np.random.seed(42)
scores = pd.Series(np.random.randint(50, 101, 10),
index=['学生'+str(i) for i in range(1, 11)])
# 计算统计量
mean_score = scores.mean()
max_score = scores.max()
min_score = scores.min()
above_avg = scores[scores > mean_score].count()
print(f"平均分: {mean_score:.1f}")
print(f"最高分: {max_score}")
print(f"最低分: {min_score}")
print(f"高于平均分的学生人数: {above_avg}")
温度数据分析
给定某城市一周每天的最高温度Series,完成以下任务:
找出温度超过30度的天数
计算平均温度
将温度从高到低排序
找出温度变化最大的两天
Python
temperatures = pd.Series([28, 31, 29, 32, 30, 27, 33],
index=['周一', '周二', '周三', '周四', '周五', '周六', '周日'])
# 1. 找出温度超过30度的天数
hot_days = temperatures[temperatures > 30].count()
# 2. 计算平均温度
avg_temp = temperatures.mean()
# 3. 将温度从高到低排序
sorted_temp = temperatures.sort_values(ascending=False)
# 4. 找出温度变化最大的两天
temp_diff = temperatures.diff().abs()
max_diff_days = temp_diff.nlargest(2).index.tolist()
print(f"超过30度的天数: {hot_days}")
print(f"平均温度: {avg_temp:.1f}")
print("温度排序:\n", sorted_temp)
print(f"温度变化最大的两天: {max_diff_days}")
股票价格分析
给定某股票连续10个交易日的收盘价Series:
计算每日收益率(当日收盘价/前日收盘价 - 1)
找出收益率最高和最低的日期
计算波动率(收益率的标准差)
Python
prices = pd.Series([102.3, 103.5, 105.1, 104.8, 106.2, 107.0, 106.5, 108.1, 109.3, 110.2],
index=pd.date_range('2023-01-01', periods=10))
# 1. 计算每日收益率
returns = prices.pct_change()
# 2. 找出收益率最高和最低的日期
max_return_date = returns.idxmax()
min_return_date = returns.idxmin()
# 3. 计算波动率
volatility = returns.std()
print("每日收益率:\n", returns)
print(f"收益率最高的日期: {max_return_date}")
print(f"收益率最低的日期: {min_return_date}")
print(f"波动率: {volatility:.4f}")
销售数据分析
某产品过去12个月的销售量Series:
计算季度平均销量(每3个月为一个季度)
找出销量最高的月份
计算月环比增长率
找出连续增长超过2个月的月份
Python
sales = pd.Series([120, 135, 145, 160, 155, 170, 180, 175, 190, 200, 210, 220],
index=pd.date_range('2022-01-01', periods=12, freq='M'))
# 1. 计算季度平均销量
quarterly_avg = sales.resample('Q').mean()
# 2. 找出销量最高的月份
max_sales_month = sales.idxmax()
# 3. 计算月环比增长率
mom_growth = sales.pct_change()
# 4. 找出连续增长超过2个月的月份
growth_mask = mom_growth > 0
consecutive_growth = growth_mask.rolling(3).sum() >= 2
growth_months = sales[consecutive_growth].index
print("季度平均销量:\n", quarterly_avg)
print(f"销量最高的月份: {max_sales_month}")
print("月环比增长率:\n", mom_growth)
print("连续增长超过2个月的月份:", growth_months.tolist())
数据合并与计算
有两个Series分别记录了某产品在两个城市的日销量:
计算两个城市的总日销量
找出哪个城市的销量更高
计算两个城市销量的相关系数
Python
city_a = pd.Series([120, 135, 140, 130, 145],
index=pd.date_range('2023-01-01', periods=5))
city_b = pd.Series([110, 125, 150, 140, 130],
index=pd.date_range('2023-01-01', periods=5))
# 1. 计算两个城市的总日销量
total_sales = city_a + city_b
# 2. 找出哪个城市的销量更高
city_a_total = city_a.sum()
city_b_total = city_b.sum()
higher_sales_city = 'A' if city_a_total > city_b_total else 'B'
# 3. 计算两个城市销量的相关系数
correlation = city_a.corr(city_b)
print("总日销量:\n", total_sales)
print(f"总销量更高的城市: {higher_sales_city}")
print(f"两个城市销量的相关系数: {correlation:.2f}")
3. 核心数据结构:DataFrame
3.1 创建与访问
什么是DateFrame?
DataFrame 是 Pandas 中的核心数据结构之一,多行多列表格数据,类似于 Excel 表格 或 SQL 查询结果。
它是一个 二维表格结构,具有行索引(index)和列标签(columns)。
Python
df = pd.DataFrame({
"name": ["Alice", "Bob"],
"score": [90, 80]
})

DataFrame中的数据是以一个或多个二维块存放的(而不是列表、字典或别的一维数据结构)。它可以被看做由Series组成的字典(共同用一个索引)。提供了各种功能来进行数据访问、筛选、分割、合并、重塑、聚合以及转换等操作,广泛用于数据分析、清洗、转换、可视化等任务。

DataFrame的创建
Python
# 通过series来创建
import pandas as pd
import numpy as np
np.random.seed(42)
s1 = pd.Series(np.random.randint(0,10,6))
np.random.seed(41)
s2 = pd.Series(np.random.randint(0,20,6))
df = pd.DataFrame({"s1":s1,"s2":s2})
直接通过字典创建DataFrame
Python
import pandas as pd
df = pd.DataFrame({ "name": ["Alice", "Bob"], "score": [90, 80]})
print(df)
df = pd.DataFrame({"id": [101, 102, 103],
"name": ["张三", "李四", "王五"], "age": [20, 30, 40]})
print(df)
# id name age
# 0 101 张三 20
# 1 102 李四 30
# 2 103 王五 40
通过字典创建时指定列的顺序和行索引
Python
df = pd.DataFrame(
data={"age": [20, 30, 40],
"name": ["张三", "李四", "王五"]},
columns=["name", "age"], index=[101, 102, 103]
)
print(df)
# name age
# 101 张三 20
# 102 李四 30
# 103 王五 40
常用属性
| 属性 | 说明 |
|---|---|
| index | DataFrame 的行索引 |
| columns | DataFrame 的列标签 |
| values | DataFrame 的值 |
| ndim | DataFrame 的维度 |
| shape | DataFrame 的形状 |
| size | DataFrame 的元素个数 |
| dtypes | DataFrame 的元素类型 |
| T | 行列转置 |
| loc[] | 显式索引,按行列标签索引或切片 |
| iloc[] | 隐式索引,按行列位置索引或切片 |
| at[] | 使用行列标签访问单个元素 |
| iat[] | 使用行列位置访问单个元素 |
Python
import pandas as pd
df = pd.DataFrame(data={"id": [101, 102, 103], "name": ["张三", "李四", "王五"], "age": [20, 30, 40]},index=["aa", "bb", "cc"])
# index DataFrame的行索引
print(df.index)
# columns DataFrame的列标签
print(df.columns)
# values DataFrame的值
print(df.values)
# ndim DataFrame的维度
print(df.ndim)
# shape DataFrame的形状
print(df.shape)
# size DataFrame的元素个数
print(df.size)
# dtypes DataFrame的元素类型
print(df.dtypes)
# T 行列转置
print(df.T)
# loc[] 显式索引,按行列标签索引或切片 逗号前是行切片规则,后是列切片规则
print(df.loc["aa":"cc"])
print(df.loc[:,["id","name"]])
# iloc[] 隐式索引,按行列位置索引或切片
print(df.iloc[0:1])
print(df.iloc[0:3,2])
print("----------")
# at[] 使用行列标签访问单个元素
print(df.at["aa","name"])
# iat[] 使用行列位置访问单个元素
print(df.iat[0,1])
获取DataFrame数据
| 方法分类 | 语法示例 | 描述 | 返回值类型 | 是否支持切片/条件索引 |
|---|---|---|---|---|
| 列选择 | df['col'] | 选择单列(返回Series) | Series | ❌ |
df[['col1', 'col2']] | 选择多列(返回DataFrame) | DataFrame | ❌ | |
| 行选择 | df.loc[row_label] | 通过行标签选择单行(返回Series) | Series | ✅(标签切片) |
df.loc[start:end] | 通过标签切片选择多行(闭区间) | DataFrame | ✅ | |
df.iloc[row_index] | 通过行位置选择单行(从0开始) | Series | ✅(位置切片) | |
df.iloc[start:end] | 通过位置切片选择多行(左闭右开) | DataFrame | ✅ | |
| 行列组合选择 | df.loc[row_labels, col_labels] | 通过标签选择行和列(如df.loc['a':'b', ['col1','col2']]) | Series/DataFrame | ✅ |
df.iloc[row_idx, col_idx] | 通过位置选择行和列(如df.iloc[0:2, [1,3]]) | Series/DataFrame | ✅ | |
| 条件筛选 | df[df['col'] > 3] | 通过布尔条件筛选行 | DataFrame | ✅ |
df.query("col1 > 3 & col2 < 10") | 使用表达式筛选(需字符串表达式) | DataFrame | ✅ | |
| 快速访问 | df.at[row_label, 'col'] | 快速访问单个值(标签索引,高效) | 标量值 | ❌ |
df.iat[row_idx, col_idx] | 快速访问单个值(位置索引,高效) | 标量值 | ❌ | |
| 头部/尾部 | df.head(n) | 返回前n行(默认5) | DataFrame | ❌ |
df.tail(n) | 返回后n行(默认5) | DataFrame | ❌ | |
| 样本抽样 | df.sample(n=3) | 随机抽取n行 | DataFrame | ❌ |
| 索引重置 | df.reset_index() | 重置索引(原索引变为列) | DataFrame | ❌ |
| 设置索引 | df.set_index('col') | 指定某列作为新索引 | DataFrame | ❌ |
-
loc** vs **iloc
○ loc:基于标签(index/column names),切片为闭区间(如df.loc[‘a’:‘c’]包含’c’)。
○ iloc:基于整数位置(从0开始),切片为左闭右开(如df.iloc[0:2]不包含索引2)。 -
布尔条件筛选
○ 支持组合条件(需用&、|,并用括号分隔条件):
Python
df[(df['col1'] > 3) & (df['col2'] == 'A')]
- at**/iat vs loc/**iloc
○ at/iat:仅用于访问单个值,速度更快。
○ loc/iloc:支持多行/列选择,功能更灵活。
获取一列数据
Python
# 访问数据
print(df['name']) #访问某列数据
print(df.score)
# df["col"] / df.col
df["name"] # 返回 Series
df.name
df[["name"]] # 返回 DataFrame
获取多列数据
Python
df[["date", "temp_max", "temp_min"]] # 获取多列数据
print(df[['name','score']]) # 访问多列数据
获取行数据loc:通过行标签获取数据
Python
df.loc[1] # 获取行标签为1的数据
df.loc[[1, 10, 100]] # 获取行标签分别为1、10、100的数据
iloc:通过行位置获取数据
Python
df.iloc[0] # 获取行位置为0的数据
df.iloc[-1] # 获取行位置为最后一位的数据
获取指定单元格
Python
df.loc[101, "name"] # 标签访问
df.iloc[0, 1] # 位置访问
df.loc[1, "precipitation"] # 获取行标签为1,列标签为precipitation的数据
df.loc[:, "precipitation"] # 获取所有行,列标签为precipitation的数据
df.iloc[:, [3, 5, -1]] # 获取所有行,列位置为3,5,最后一位的数据
df.iloc[:10, 2:6] # 获取前10行,列位置为2、3、4、5的数据
df.loc[:10, ["date", "precipitation", "temp_max", "temp_min"]] # 通过行列标签获取数据
查看部分数据
通过head()、tail()获取前n行或后n行
Python
print(df.head())
print(df.tail(10))
使用布尔索引筛选数据
Python
# 条件筛选
df['score']>70
print(df[df.score>70])
print(df[(df['score']>70) & (df['age']<20)])
# 随机抽样
df.sample(2)
3.2 常用方法与统计
| 方法 | 说明 |
|---|---|
| 数据查看 | |
head(n=5) | 查看前n行数据(默认5行) |
tail(n=5) | 查看后n行数据(默认5行) |
| 条件筛选 | |
isin(values) | 判断元素是否包含在参数集合中 |
isna() | 检测元素是否为缺失值(等价于isnull()) |
| 统计计算 | |
sum() | 求和(默认按列计算,axis=1按行) |
mean() | 计算平均值 |
min()/max() | 返回最小值/最大值 |
var(ddof=1) | 计算方差(默认样本方差,ddof=0为总体方差) |
std(ddof=1) | 计算标准差 |
median() | 返回中位数 |
mode() | 返回众数(可能有多个) |
quantile(q=0.5) | 返回指定分位数(如q=0.5为中位数) |
| 数据描述 | |
describe() | 返回数值列的统计信息(计数、均值、标准差、最小值、四分位数等) |
info() | 显示DataFrame的基本信息(列名、非空计数、数据类型等) |
| 值计数与去重 | |
value_counts() | 统计每个唯一值的出现次数(normalize=True返回比例) |
count() | 返回非空元素数量 |
drop_duplicates() | 去重(keep='first'保留首次出现,keep=False删除所有重复) |
| 抽样与替换 | |
sample(n) | 随机抽取n行数据 |
replace(old, new) | 替换指定值(支持字典映射或正则表达式) |
| 比较与累计计算 | |
equals(df) | 判断两个DataFrame是否完全相同 |
cummax()/cummin() | 计算累计最大值/最小值 |
cumsum()/cumprod() | 计算累计和/累计积 |
| 差分与排序 | |
diff(periods=1) | 计算一阶差分(periods控制步长,正数向前,负数向后) |
sort_index() | 按行或列索引排序(axis=0按行索引,axis=1按列名) |
sort_values(by) | 按指定列值排序(ascending=False降序,支持多列排序) |
nlargest(n, cols) | 返回某列最大的n条数据(等价于sort_values(ascending=False).head(n)) |
nsmallest(n, cols) | 返回某列最小的n条数据 |
Python
import pandas as pd
df = pd.DataFrame(data={"id": [101, 102, 103,104,105,106,101], "name": ["张三", "李四", "王五","赵六","冯七","周八","张三"], "age": [10, 20, 30, 40, None, 60,10]},index=["aa", "bb", "cc", "dd", "ee", "ff","aa"])
# head() 查看前n行数据,默认5行
print(df.head())
# tail() 查看后n行数据,默认5行
print(df.tail())
# isin() 元素是否包含在参数集合中
print(df.isin([103,106]))
# isna() 元素是否为缺失值
print(df.isna())
# sum() 求和
print(df["age"].sum())
# mean() 平均值
print(df["age"].mean())
# min() 最小值
print(df["age"].min())
# max() 最大值
print(df["age"].max())
# var() 方差
print(df["age"].var())
# std() 标准差
print(df["age"].std())
# median() 中位数
print(df["age"].median())
# mode() 众数
print(df["age"].mode())
# quantile() 指定位置的分位数,如quantile(0.5)
print(df["age"].quantile(0.5))
# describe() 常见统计信息
print(df.describe())
# info() 基本信息
print(df.info())
# value_counts() 每个元素的个数
print(df.value_counts())
# count() 非空元素的个数
print(df.count())
# drop_duplicates() 去重 duplicated()判断是否为重复行
print(df.duplicated(subset="age"))
# sample() 随机采样
print(df.sample())
# replace() 用指定值代替原有值
print("----------------")
print(df.replace(20,"haha"))
# cummax() 累计最大值
df3 = pd.DataFrame({'A': [2, 5, 3, 7, 4],'B': [1, 6, 2, 8, 3]})
# 按列 等价于axis=0 默认
print(df3.cummax(axis="index"))
# 按行 等价于axis=1
print(df3.cummax(axis="columns"))
# cummin() 累计最小值
print(df3.cummin())
# cumsum() 累计和
print(df3.cumsum())
# cumprod() 累计积
print(df3.cumprod())
# diff() 一阶差分
print(df3.diff())
# sort_index() 按行索引排序
print(df.sort_index())
# sort_values() 按某列的值排序,可传入列表来按多列排序,并通过ascending参数设置升序或降序
print(df.sort_values(by="age"))
# nlargest() 返回某列最大的n条数据
print(df.nlargest(n=2,columns="age"))
# nsmallest() 返回某列最小的n条数据
print(df.nsmallest(n=1,columns="age"))
在Pandas的 DataFrame 方法里,axis 是一个非常重要的参数,它用于指定操作的方向。
axis 参数可以取两个主要的值,即 0 或 ‘index’,以及 1 或 ‘columns’ ,其含义如下:
• axis=0 或 axis=‘index’:表示操作沿着行的方向进行,也就是对每一列的数据进行处理。例如,当计算每列的均值时,就是对每列中的所有行数据进行计算。
• axis=1 或 axis=‘columns’:表示操作沿着列的方向进行,也就是对每行的数据进行处理。例如,当计算每行的总和时,就是对每行中的所有列数据进行计算。
3.3 运算
标量运算
标量与每个元素进行计算。
Python
df = pd.DataFrame(data={"age": [20, 30, 40, 10], "name": ["张三", "李四", "王五", "赵六"]},
columns=["name", "age"],
index=[101, 104, 103, 102],
)
print(df * 2)
# name age
# 101 张三张三 40
# 104 李四李四 60
# 103 王五王五 80
# 102 赵六赵六 20
df1 = pd.DataFrame(
data={"age": [10, 20, 30, 40], "name": ["张三", "李四", "王五", "赵六"]},
columns=["name", "age"],
index=[101, 102, 103, 104],
)
df2 = pd.DataFrame(
data={"age": [10, 20, 30, 40], "name": ["张三", "李四", "王五", "田七"]},
columns=["name", "age"],
index=[102, 103, 104, 105],
)
print(df1 + df2)
# name age
# 101 NaN NaN
# 102 李四张三 30.0
# 103 王五李四 50.0
# 104 赵六王五 70.0
# 105 NaN NaN
3.4 案例练习
案例1:学生成绩分析
场景:某班级的学生成绩数据如下,请完成以下任务:
- 计算每位学生的总分和平均分。
- 找出数学成绩高于90分或英语成绩高于85分的学生。
- 按总分从高到低排序,并输出前3名学生。
Python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六', '钱七'],
'数学': [85, 92, 78, 88, 95],
'英语': [90, 88, 85, 92, 80],
'物理': [75, 80, 88, 85, 90]
}
df = pd.DataFrame(data)
# 1. 计算总分和平均分
df['总分'] = df[['数学', '英语', '物理']].sum(axis=1)
df['平均分'] = df['总分'] / 3
# 2. 找出数学>90或英语>85的学生
high_scores = df[(df['数学'] > 90) | (df['英语'] > 85)]
# 3. 按总分排序并输出前3名
top3 = df.sort_values('总分', ascending=False).head(3)
print("总分和平均分:\n", df)
print("\n数学>90或英语>85的学生:\n", high_scores)
print("\n总分前3名学生:\n", top3)
案例2:销售数据分析
场景:某公司销售数据如下,请完成以下任务:
- 计算每种产品的总销售额(销售额 = 单价 × 销量)。
- 找出销售额最高的产品。
- 按销售额从高到低排序,并输出所有产品信息。
Python
import pandas as pd
data = {
'产品名称': ['A', 'B', 'C', 'D'],
'单价': [100, 150, 200, 120],
'销量': [50, 30, 20, 40]
}
df = pd.DataFrame(data)
# 1. 计算总销售额
df['销售额'] = df['单价'] * df['销量']
# 2. 找出销售额最高的产品
max_sales = df[df['销售额'] == df['销售额'].max()]
# 3. 按销售额排序
sorted_df = df.sort_values('销售额', ascending=False)
print("销售额计算:\n", df)
print("\n销售额最高的产品:\n", max_sales)
print("\n按销售额排序:\n", sorted_df)
案例3:员工考勤统计
场景:某部门员工考勤数据如下,请完成以下任务:
- 计算每位员工的出勤率(出勤率 = 出勤天数 / 工作日总数)。
- 标记出勤率低于80%的员工。
- 按出勤率从高到低排序。
Python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'出勤天数': [20, 15, 18, 22],
'工作日总数': [25, 20, 25, 25]
}
df = pd.DataFrame(data)
# 1. 计算出勤率
df['出勤率'] = (df['出勤天数'] / df['工作日总数']).round(2)
# 2. 标记出勤率<80%的员工
df['需关注'] = df['出勤率'] < 0.8
# 3. 按出勤率排序
sorted_df = df.sort_values('出勤率', ascending=False)
print("出勤率统计:\n", df)
print("\n出勤率排序:\n", sorted_df)
案例4:电影评分分析
场景:某电影评分数据如下,请完成以下任务:
- 计算每部电影的平均评分。
- 找出评分高于8.5的电影。
- 按平均评分从高到低排序。
Python
import pandas as pd
data = {
'电影名称': ['电影A', '电影B', '电影C', '电影D'],
'评分1': [9.0, 8.5, 8.0, 7.5],
'评分2': [8.5, 9.0, 8.5, 8.0],
'评分3': [9.5, 8.0, 7.5, 7.0]
}
df = pd.DataFrame(data)
# 1. 计算平均评分
df['平均评分'] = df[['评分1', '评分2', '评分3']].mean(axis=1).round(2)
# 2. 找出评分>8.5的电影
high_rated = df[df['平均评分'] > 8.5]
# 3. 按平均评分排序
sorted_df = df.sort_values('平均评分', ascending=False)
print("平均评分:\n", df)
print("\n评分>8.5的电影:\n", high_rated)
print("\n按评分排序:\n", sorted_df)
案例5:股票价格分析
场景:某股票价格数据如下,请完成以下任务:
- 计算每日股价的涨跌幅(涨跌幅 = (当日收盘价 - 前一日收盘价) / 前一日收盘价)。
- 找出涨幅超过5%的日期。
- 按日期排序,并输出涨跌幅最高的日期。
Python
import pandas as pd
data = {
'日期': ['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04'],
'收盘价': [100, 105, 110, 102]
}
df = pd.DataFrame(data)
df['日期'] = pd.to_datetime(df['日期'])
# 1. 计算涨跌幅
df['涨跌幅'] = df['收盘价'].pct_change().round(4)
# 2. 找出涨幅>5%的日期
high_increase = df[df['涨跌幅'] > 0.05]
# 3. 按日期排序并输出最高涨跌幅日期
sorted_df = df.sort_values('日期')
max_increase_date = df.loc[df['涨跌幅'].idxmax(), '日期']
print("涨跌幅计算:\n", df)
print("\n涨幅>5%的日期:\n", high_increase)
print("\n涨跌幅最高的日期:\n", max_increase_date)
案例6:电商用户行为分析(基础版)
场景:某电商平台的用户行为数据如下,请完成以下任务:
- 计算每位用户的总消费金额(消费金额 = 商品单价 × 购买数量)
- 找出消费金额最高的用户,并输出其所有信息
- 计算所有用户的平均消费金额(保留2位小数)
- 统计电子产品的总购买数量
Python
import pandas as pd
data = {
'用户ID': [101, 102, 103, 104, 105],
'用户名': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
'商品类别': ['电子产品', '服饰', '电子产品', '家居', '服饰'],
'商品单价': [1200, 300, 800, 150, 200],
'购买数量': [1, 3, 2, 5, 4]
}
df = pd.DataFrame(data)
# 1. 计算总消费金额
df['消费金额'] = df['商品单价'] * df['购买数量']
# 2. 找出消费金额最高的用户
max_spend_user = df[df['消费金额'] == df['消费金额'].max()]
# 3. 计算平均消费金额
avg_spend = round(df['消费金额'].mean(), 2)
# 4. 统计电子产品的总购买数量
electronic_total = df[df['商品类别'] == '电子产品']['购买数量'].sum()
print("用户消费分析:\n", df)
print("\n消费金额最高的用户:\n", max_spend_user)
print("\n平均消费金额:", avg_spend)
print("电子产品总购买数量:", electronic_total)
输出示例:
Plain Text
用户消费分析:
用户ID 用户名 商品类别 商品单价 购买数量 消费金额
0 101 Alice 电子产品 1200 1 1200
1 102 Bob 服饰 300 3 900
2 103 Charlie 电子产品 800 2 1600
3 104 David 家居 150 5 750
4 105 Eve 服饰 200 4 800
消费金额最高的用户:
用户ID 用户名 商品类别 商品单价 购买数量 消费金额
2 103 Charlie 电子产品 800 2 1600
平均消费金额: 1050.0
电子产品总购买数量: 3
3.5 数据的导入与导出
导出数据
| 方法 | 说明 |
|---|---|
| to_csv() | 将数据保存为 CSV 格式文件,数据之间以逗号分隔。可通过 sep 参数设置其他分隔符,通过 index 参数设置是否保存行标签,通过 header 参数设置是否保存列标签。 |
| to_pickle() | 若要保存计算的中间结果或需在 Python 中复用的对象,可保存为 .pickle 文件。此格式仅能在 Python 中使用,扩展名可为 .p、.pkl、.pickle。 |
| to_excel() | 保存为 Excel 文件,需安装 openpyxl 包。 |
| to_clipboard() | 将数据保存到剪切板。 |
| to_dict() | 保存为字典格式。 |
| to_hdf() | 保存为 HDF 格式文件,需安装 tables 包。 |
| to_html() | 保存为 HTML 格式,需安装 lxml、html5lib、beautifulsoup4 包。 |
| to_json() | 保存为 JSON 格式。 |
| to_feather() | 保存为 Feather 二进制格式文件。该格式可在 Python 与 R 之间高效读写,速度快于 CSV。通常用于中间数据传递而非最终数据保存。需安装 pyarrow 包。 |
| to_sql() | 将数据保存到数据库中。 |
Python
import os
import pandas as pd
os.makedirs("data", exist_ok=True)
df = pd.DataFrame({"age": [20, 30, 40, 10], "name": ["张三", "李四", "王五", "赵六"], "id": [101, 102, 103, 104]})
df.set_index("id", inplace=True)
df.to_csv("data/df.csv")
df.to_csv("data/df.tsv", sep="\t") # 设置分隔符为 \t
df.to_csv("data/df_noindex.csv", index=False) # index=False 不保存行索引
df.to_pickle("data/df.pkl")
df.to_excel("data/df.xlsx")
df.to_clipboard()
df_dict = df.to_dict()
df.to_hdf("data/df.h5", key="df")
df.to_html("data/df.html")
df.to_json("data/df.json")
df.to_feather("data/df.feather")
导入数据
| 方法 | 说明 |
|---|---|
| read_csv() | 加载 CSV 格式的数据。可通过 sep 参数指定分隔符,可通过 index_col 参数指定行索引。 |
| read_pickle() | 加载 Pickle 格式的数据。 |
| read_excel() | 加载 Excel 格式的数据。 |
| read_clipboard() | 加载剪切板中的数据。 |
| read_hdf() | 加载 HDF 格式的数据。 |
| read_html() | 加载 HTML 格式的数据。 |
| read_json() | 加载 JSON 格式的数据。 |
| read_feather() | 加载 Feather 格式的数据。 |
| read_sql() | 加载数据库中的数据。 |
Python
df_csv = pd.read_csv("data/df.csv", index_col="id") # 指定行索引
df_tsv = pd.read_csv("data/df.tsv", sep="\t") # 指定分隔符
df_pkl = pd.read_pickle("data/df.pkl")
df_excel = pd.read_excel("data/df.xlsx", index_col="id")
df_clipboard = pd.read_clipboard(index_col="id")
df_from_dict = pd.DataFrame(df_dict)
df_hdf = pd.read_hdf("data/df.h5", key="df")
df_html = pd.read_html("data/df.html", index_col=0)[0]
df_json = pd.read_json("data/df.json")
df_feather = pd.read_feather("data/df.feather")
print(df_csv)
print(df_tsv)
print(df_pkl)
print(df_excel)
print(df_clipboard)
print(df_from_dict)
print(df_hdf)
print(df_html)
print(df_json)
print(df_feather)
当json数据比较复杂时,可以先用json库导入,再导入DataFrame
import json
with open('data/test.json') as f:
data = json.load(f)
print(data)
print(type(data))
df = pd.DataFrame(data['users'])
3.6 数据清洗与预处理
| 章节 | 核心内容 | 关键知识点 |
|---|---|---|
| 1. 缺失值处理 | 检测、删除和填充缺失值的方法 | isna(), dropna(), fillna(), 前向/后向填充, 均值/中位数填充 |
| 2. 重复数据处理 | 识别和删除重复行 | duplicated(), drop_duplicates(), 按列去重, 保留首次/最后一次出现 |
| 3. 数据类型转换 | 强制类型转换、日期/分类数据处理 | astype(), to_datetime(), 分类数据优化, 数值格式化 |
| 4. 数据重塑与变形 | 行列转置、宽表长表转换、分列操作 | T 转置, melt(), pivot(), str.split() 分列 |
| 5. 文本数据处理 | 字符串清洗、正则提取、大小写转换 | str.lower(), str.replace(), str.extract(), 空格处理 |
| 6. 数据分箱与离散化 | 数值分箱(等宽/等频) | pd.cut(), pd.qcut(), 离散化应用场景 |
| 7. 其他常用转换 | 重命名列、索引操作、函数应用、内存优化 | rename(), set_index(), apply(), 类型优化减少内存占用 |
1. 缺失值处理
| 方法/操作 | 语法示例 | 描述 |
|---|---|---|
| 检测缺失值 | df.isna() 或 df.isnull() | 返回布尔矩阵,标记缺失值(NaN 或 None) |
| 统计缺失值 | df.isna().sum() | 统计每列缺失值数量 |
| 删除缺失值(行) | df.dropna() | 删除包含缺失值的行(默认) |
| 删除缺失值(列) | df.dropna(axis=1) | 删除包含缺失值的列 |
| 删除指定列的缺失值行 | df.dropna(subset=['col1']) | 仅删除指定列中存在缺失值的行 |
| 填充缺失值(固定值) | df.fillna(value) | 用固定值填充(例如:df.fillna(0)) |
| 前向填充 | df.fillna(method='ffill') | 用前一个非缺失值填充(向前填充) |
| 后向填充 | df.fillna(method='bfill') | 用后一个非缺失值填充(向后填充) |
| 均值填充 | df.fillna(df.mean()) | 用各列的均值填充缺失值 |
pandas中的缺失值
• NaN (Not a Number) 是缺失值的标志
• 方法: isna(), notna()
pandas使用浮点值NaN(Not a Number)表示缺失数据,使用NA(Not Available)表示缺失值。可以通过isnull()、isna()或notnull()、notna()方法判断某个值是否为缺失值。
Nan通常表示一个无效的或未定义的数字值,是浮点数的一种特殊取值,用于表示那些不能表示为正常数字的情况,如 0/0、∞-∞等数学运算的结果。nan与任何值(包括它自身)进行比较的结果都为False。例如在 Python 中,nan == nan返回False。
NA一般用于表示数据不可用或缺失的情况,它的含义更侧重于数据在某种上下文中是缺失或不存在的,不一定特指数字类型的缺失。
na和nan都用于表示缺失值,但nan更强调是数值计算中的特殊值,而na更强调数据的可用性或存在性。
判断缺失值
Python
s = pd.Series([np.nan, None, pd.NA])
print(s)
# 0 NaN
# 1 None
# 2 <NA>
# dtype: object
print(s.isnull())
# 0 True
# 1 True
# 2 True
# dtype: bool
加载数据中包含缺失值
Python
df = pd.read_csv("data/weather_withna.csv")
print(df.tail(5))
# date precipitation temp_max temp_min wind weather
# 1456 2015-12-27 NaN NaN NaN NaN NaN
# 1457 2015-12-28 NaN NaN NaN NaN NaN
# 1458 2015-12-29 NaN NaN NaN NaN NaN
# 1459 2015-12-30 NaN NaN NaN NaN NaN
# 1460 2015-12-31 20.6 12.2 5.0 3.8 rain
可以通过keep_default_na参数设置是否将空白值设置为缺失值。
Python
df = pd.read_csv("data/weather_withna.csv", keep_default_na=False)
print(df.tail(5))
# date precipitation temp_max temp_min wind weather
# 1456 2015-12-27
# 1457 2015-12-28
# 1458 2015-12-29
# 1459 2015-12-30
# 1460 2015-12-31 20.6 12.2 5.0 3.8 rain
可通过na_values参数将指定值设置为缺失值。
Python
df = pd.read_csv("data/weather_withna.csv", na_values=["2015-12-31"])
print(df.tail(5))
# date precipitation temp_max temp_min wind weather
# 1456 2015-12-27 NaN NaN NaN NaN NaN
# 1457 2015-12-28 NaN NaN NaN NaN NaN
# 1458 2015-12-29 NaN NaN NaN NaN NaN
# 1459 2015-12-30 NaN NaN NaN NaN NaN
# 1460 NaN 20.6 12.2 5.0 3.8 rain
查看缺失值
通过isnull()查看缺失值数量
Python
df = pd.read_csv("data/weather_withna.csv")
print(df.isnull().sum())
# date 0
# precipitation 303
# temp_max 303
# temp_min 303
# wind 303
# weather 303
# dtype: int64
剔除缺失值
通过dropna()方法来剔除缺失值。
Series剔除缺失值
Python
s = pd.Series([1, pd.NA, None])
print(s)
# 0 1
# 1 <NA>
# 2 None
# dtype: object
print(s.dropna())
# 0 1
# dtype: object
DataFrame剔除缺失值
无法从DataFrame中单独剔除一个值,只能剔除缺失值所在的整行或整列。默认情况下,dropna()会剔除任何包含缺失值的整行数据。
Python
df = pd.DataFrame([[1, pd.NA, 2], [2, 3, 5], [pd.NA, 4, 6]])
print(df)
# 0 1 2
# 0 1 <NA> 2
# 1 2 3 5
# 2 <NA> 4 6
print(df.dropna())
# 0 1 2
# 1 2 3 5
可以设置按不同的坐标轴剔除缺失值,比如axis=1(或 axis=‘columns’)会剔除任何包含缺失值的整列数据。
df = pd.DataFrame([[1, pd.NA, 2], [2, 3, 5], [pd.NA, 4, 6]])
print(df)
# 0 1 2
# 0 1 <NA> 2
# 1 2 3 5
# 2 <NA> 4 6
print(df.dropna(axis=1))
# 2
# 0 2
# 1 5
# 2 6
有时只需要剔除全部是缺失值的行或列,或者绝大多数是缺失值的行或列。这些需求可以通过设置how或thresh参数来满足,它们可以设置剔除行或列缺失值的数量阈值。
df = pd.DataFrame([[1, pd.NA, 2], [pd.NA, pd.NA, 5], [pd.NA, pd.NA, pd.NA]])
print(df)
# 0 1 2
# 0 1 <NA> 2
# 1 <NA> <NA> 5
# 2 <NA> <NA> <NA>
print(df.dropna(how="all")) # 如果所有值都是缺失值,则删除这一行
# 0 1 2
# 0 1 <NA> 2
# 1 <NA> <NA> 5
print(df.dropna(thresh=2)) # 如果至少有2个值不是缺失值,则保留这一行
# 0 1 2
# 0 1 <NA> 2
可以通过设置subset参数来设置某一列有缺失值则进行剔除。
df = pd.DataFrame([[1, pd.NA, 2], [pd.NA, pd.NA, 5], [pd.NA, pd.NA, pd.NA]])
print(df)
# 0 1 2
# 0 1 <NA> 2
# 1 <NA> <NA> 5
# 2 <NA> <NA> <NA>
print(df.dropna(subset=[0])) # 如果0列有缺失值,则删除这一行
# 0 1 2
# 0 1 <NA> 2
填充缺失值
使用固定值填充
通过fillna()方法,传入值或字典进行填充。
df = pd.read_csv("data/weather_withna.csv")
print(df.fillna(0).tail()) # 使用固定值填充
#
print(df.fillna({"temp_max": 60, "temp_min": -60}).tail()) # 使用字典来填充
# date precipitation temp_max temp_min wind weather
# 1456 2015-12-27 NaN 60.0 -60.0 NaN NaN
# 1457 2015-12-28 NaN 60.0 -60.0 NaN NaN
# 1458 2015-12-29 NaN 60.0 -60.0 NaN NaN
# 1459 2015-12-30 NaN 60.0 -60.0 NaN NaN
# 1460 2015-12-31 20.6 12.2 5.0 3.8 rain
使用统计值填充
通过fillna()方法,传入统计后的值进行填充。
print(df.fillna(df[["precipitation", "temp_max", "temp_min", "wind"]].mean()).tail()) # 使用平均值填充
# date precipitation temp_max temp_min wind weather
# 1456 2015-12-27 3.052332 15.851468 7.877202 3.242055 NaN
# 1457 2015-12-28 3.052332 15.851468 7.877202 3.242055 NaN
# 1458 2015-12-29 3.052332 15.851468 7.877202 3.242055 NaN
# 1459 2015-12-30 3.052332 15.851468 7.877202 3.242055 NaN
# 1460 2015-12-31 20.600000 12.200000 5.000000 3.800000 rain
使用前后的有效值填充
通过ffill()或bfill()方法使用前面或后面的有效值填充。
print(df.ffill().tail()) # 使用前面的有效值填充
# date precipitation temp_max temp_min wind weather
# 1456 2015-12-27 0.0 11.1 4.4 4.8 sun
# 1457 2015-12-28 0.0 11.1 4.4 4.8 sun
# 1458 2015-12-29 0.0 11.1 4.4 4.8 sun
# 1459 2015-12-30 0.0 11.1 4.4 4.8 sun
# 1460 2015-12-31 20.6 12.2 5.0 3.8 rain
print(df.bfill().tail()) # 使用后面的有效值填充
# date precipitation temp_max temp_min wind weather
# 1456 2015-12-27 20.6 12.2 5.0 3.8 rain
# 1457 2015-12-28 20.6 12.2 5.0 3.8 rain
# 1458 2015-12-29 20.6 12.2 5.0 3.8 rain
# 1459 2015-12-30 20.6 12.2 5.0 3.8 rain
# 1460 2015-12-31 20.6 12.2 5.0 3.8 rain
通过线性插值填充
通过interpolate()方法进行线性插值填充。线性插值操作,就是用于在已知数据点之间估算未知数据点的值。interpolate 方法支持多种插值方法,可通过 method 参数指定,常见的方法有:
• ‘linear’:线性插值,基于两点之间的直线来估算缺失值,适用于数据呈线性变化的情况。
• ‘time’:适用于时间序列数据,会考虑时间间隔进行插值。
• ‘polynomial’:多项式插值,通过拟合多项式曲线来估算缺失值,可通过 order 参数指定多项式的阶数。
# 创建包含缺失值的 Series
s = pd.Series([1, np.nan, 3, 4, np.nan, 6])
# 使用默认的线性插值方法填充缺失值
s_interpolated = s.interpolate()
print(s_interpolated)
# 0 1.0
# 1 2.0
# 2 3.0
# 3 4.0
# 4 5.0
# 5 6.0
# dtype: float64
总结
Python
# 缺失值
import numpy as np
# 缺失值的类型 nan na
s = pd.Series([np.nan, None, pd.NA,2,4])
df = pd.DataFrame([[1, pd.NA, 2], [2, 3, 5], [pd.NA, 4, 6]])
print(s)
print(s.isnull()) #查看是否是缺失值
print(s.isna()) #查看是否是缺失值
print(s.isna().sum()) # 缺失值的个数
# 剔除缺失值
print(s.dropna()) #series剔除缺失值
print(df.dropna()) #只要有缺失值,就剔除一整条记录
print(df.dropna(how="all")) # 如果所有值都是缺失值,则删除这一行
print(df.dropna(thresh=2)) # 如果至少有2个值不是缺失值,则保留这一行
print(df.dropna(axis=1)) #剔除一列中含缺失值的列
#可以通过设置subset参数来设置某一列有缺失值则进行剔除。
print(df.dropna(subset=[0]))# 如果0列有缺失值,则删除这一行
#填充缺失值
print('********')
df = pd.read_csv("data/weather_withna.csv")
# df = df.fillna({"temp_max": 60, "temp_min": -60}) # 使用字典来填充
print(df['temp_max'].mean())
df.fillna(df[["precipitation", "temp_max", "temp_min", "wind"]].mean()).tail() # 使用平均值填充
print(df.ffill().tail()) # 使用前面的有效值填充
print(df.bfill().tail()) # 使用后面的有效值填充
df1 = pd.read_csv("data/weather_withna.csv")
df2 = pd.read_csv("data/weather_withna.csv", keep_default_na=False)
print(df1.temp_max.count())
print(df1.isnull().sum())
print(df2.temp_max.count())
print(df2.isnull().sum())
# 将
df = pd.read_csv("data/weather_withna.csv", na_values=["2015-12-31"])
# print(df.tail(5))
print(df.isnull().sum())
2.重复数据处理
| 方法/操作 | 语法示例 | 描述 |
|---|---|---|
| 检测重复行 | df.duplicated() | 返回布尔序列,标记重复行(首次出现的行标记为 False) |
| 删除重复行(默认) | df.drop_duplicates() | 删除重复行,保留首次出现的行(默认检查所有列) |
| 根据指定列去重 | df.drop_duplicates(subset=['col1']) | 仅根据指定列判断并删除重复行 |
| 保留最后一次出现的行 | df.drop_duplicates(keep='last') | 删除重复行,仅保留最后一次出现的记录 |
1. 检测重复行
Python
import pandas as pd
# 创建包含重复数据的DataFrame
data = {
'Name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob'],
'Age': [25, 30, 25, 35, 30],
'City': ['NY', 'LA', 'NY', 'SF', 'LA']
}
df = pd.DataFrame(data)
# 检测重复行(默认检查所有列)
print("重复行标记(False表示首次出现,True表示重复):")
print(df.duplicated())
输出:
Plain Text
0 False
1 False
2 True
3 False
4 True
dtype: bool
2. 删除重复行
Python
# 默认保留首次出现的行
df_unique = df.drop_duplicates()
print("去重后的DataFrame:")
print(df_unique)
输出:
Plain Text
Name Age City
0 Alice 25 NY
1 Bob 30 LA
3 Charlie 35 SF
3. 按指定列去重
Python
# 仅根据'Name'列去重(保留首次出现)
df_name_unique = df.drop_duplicates(subset=['Name'])
print("按Name列去重:")
print(df_name_unique)
输出:
Plain Text
Name Age City
0 Alice 25 NY
1 Bob 30 LA
3 Charlie 35 SF
4. 保留最后一次出现的重复行
Python
# 保留最后一次出现的行
df_last = df.drop_duplicates(keep='last')
print("保留最后一次出现的行:")
print(df_last)
输出:
Plain Text
Name Age City
2 Alice 25 NY
4 Bob 30 LA
3 Charlie 35 SF
5. 综合案例:处理真实数据
Python
# 加载包含重复值的数据(示例)
df_sales = pd.read_csv("sales_data.csv")
# 检查重复行数量
print("原始数据重复行数:", df_sales.duplicated().sum())
# 按'Order_ID'列去重,保留最后一次记录
df_clean = df_sales.drop_duplicates(subset=['Order_ID'], keep='last')
# 验证结果
print("去重后数据行数:", len(df_clean))
注意事项
- 性能优化:对大数据集去重时,可通过 subset 指定关键列以减少计算量。
- 逻辑一致性:确保 keep=‘last’ 或 keep=False(删除所有重复)符合业务需求。
- 多列去重:subset=[‘col1’, ‘col2’] 可联合多列判断重复。
通过以上案例,可以灵活应对实际数据清洗中的重复值问题!
3. 数据类型转换
| 方法/操作 | 语法示例 | 描述 |
|---|---|---|
| 查看数据类型 | df.dtypes | 显示每列的数据类型 |
| 强制类型转换 | df['col'].astype('int') | 将列转换为指定类型(如 int、float、str、datetime) |
| 转换为日期时间 | pd.to_datetime(df['col']) | 将字符串列转换为 datetime 类型 |
| 转换为分类数据 | df['col'].astype('category') | 将列转换为分类类型(节省内存,提高性能) |
| 数值格式化 | df['col'].round(2) | 对数值保留 2 位小数 |
核心方法
| 操作 | 方法/函数 | 描述 |
|---|---|---|
| 查看数据类型 | df.dtypes | 显示每列的数据类型(如 int64、float64、object 等)。 |
| 强制类型转换 | df['col'].astype('type') | 将列转换为指定类型(如 int、float、str、bool 等)。 |
| 转换为日期时间 | pd.to_datetime(df['col']) | 将字符串或数值列转换为 datetime 类型(支持自定义格式)。 |
| 转换为分类数据 | df['col'].astype('category') | 将列转换为分类类型(节省内存,提高性能,适用于有限取值的列,如性别、省份等)。 |
| 数值格式化 | df['col'].round(2) | 保留指定小数位数(如 2 位)。 |
1. 查看数据类型
Python
import pandas as pd
# 加载数据(以sleep.csv为例)
df = pd.read_csv("sleep.csv")
print(df.dtypes)
输出示例:
Plain Text
person_id int64
gender object
age int64
occupation object
sleep_duration float64
sleep_quality float64
... ...
说明:object通常为字符串或混合类型,需检查是否需要转换。
2. 强制类型转换
将数值列转换为整数或字符串:
Python
# 将sleep_duration从float转为int(丢失小数部分)
df['sleep_duration_int'] = df['sleep_duration'].astype('int32')
# 将gender转为字符串
df['gender_str'] = df['gender'].astype('str')
print(df[['sleep_duration', 'sleep_duration_int', 'gender_str']].head())
输出:
Plain Text
sleep_duration sleep_duration_int gender_str
0 7.4 7 Male
1 4.2 4 Female
2 6.1 6 Male
3. 转换为分类数据
优化内存和性能(适用于低基数列):
Python
# 将gender列转为分类类型
df['gender'] = df['gender'].astype('category')
print(df['gender'].dtypes)
输出:
Plain Text
category
优势:
• 减少内存占用(尤其对重复值多的列)。
• 加速groupby、sort等操作。
4. 数值格式化
控制小数位数:
Python
# 保留sleep_quality的2位小数
df['sleep_quality_rounded'] = df['sleep_quality'].round(2)
print(df[['sleep_quality', 'sleep_quality_rounded']].head())
输出:
Plain Text
sleep_quality sleep_quality_rounded
0 7.0 7.00
1 4.9 4.90
2 6.0 6.00
常见问题与技巧
- 处理转换错误:使用errors='coerce’将无效值转为NaN,避免报错:
Python
df['age'] = pd.to_numeric(df['age'], errors='coerce')
- 处理转换错误:使用errors='coerce’将无效值转为NaN,避免报错:
Python
df['age'] = df['age'].astype('int32')
- 布尔类型转换:将字符串(如"Yes"/“No”)转为布尔值:
Python
df['is_active'] = df['active_flag'].map({'Yes': True, 'No': False})
- 自定义格式化:使用apply实现复杂转换(如百分比):
Python
df['score_percent'] = df['score'].apply(lambda x: f"{x*100:.1f}%")
实战案例:处理penguins.csv
Python
df_penguins = pd.read_csv("penguins.csv")
# 1. 转换sex为分类类型
df_penguins['sex'] = df_penguins['sex'].astype('category')
# 2. 补全缺失值后转换bill_length_mm为float32
df_penguins['bill_length_mm'] = df_penguins['bill_length_mm'].fillna(0).astype('float32')
# 3. 检查并输出结果
print(df_penguins[['species', 'sex', 'bill_length_mm']].dtypes)
输出:
Plain Text
species object
sex category
bill_length_mm float32
4. 数据重塑与变形
| 方法/操作 | 语法示例 | 描述 |
|---|---|---|
| 行列转置 | df.T | 转置 DataFrame(行变列,列变行) |
| 宽表转长表 | pd.melt(df, id_vars=['id']) | 将多列合并为键值对形式(生成 variable 和 value 两列) |
| 长表转宽表 | df.pivot(index='id', columns='var', values='val') | 将长表转换为宽表(类似 Excel 数据透视表操作) |
| 分列操作 | df['col'].str.split(',', expand=True) | 按分隔符拆分字符串为多个列 |
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)