「pandas 与 numpy」数据分析与处理全流程【数据分析全栈攻略:爬虫+处理+可视化+报告】
- 第 106 篇 -
Date: 2025 - 06 - 12
Author: 郑龙浩(仟墨)
文中使用的所有文件在文章顶部的资源展示
数据分析与处理 「pandas 与 numpy」
文章目录
- 数据分析与处理 「pandas 与 numpy」
- 一了解数据处理
- 1 数据处理
- 2 数据分析第三方库
- 二 numpy
- 1 基本介绍
- 2 导入库
- 3 数组与列表的区别
- 4 ndarray 对象 (numpy的数组对象)
- 5 **创建方式:**
- 6 数组`ndarray`的常用属性
- 7 `numpy` 的随机函数
- 7.1 基本随机函数(固定区间范围)
- 7.2 分布随机函数
- 7.3 代码示例
- 8 `numpy` 常用统计函数
- 8.1 函数统计:
- 8.2 `axis`怎么用:
- 三 `pandas`
- 1 `pandas`主要介绍
- 2 `pandas`与`numpy`的区别:
- 3 导入 pandas 库
- 4 pandas 的数据结构
- 4.1 区别
- 4.2 二者联系
- 5 `Series` 数据类型
- 5.1 基本介绍
- 5.2 创建方式
- 5 `DataFrame`数据类型
- 5.1 基本介绍
- 5.2 `DataFrame`的创建
- 5.3 `DataFrame`的属性
- 5.4 访问某行或某列
- 6 `DataFrame`与`Series`相互转换
- 6.1 `DataFrame`转`Series`
- 6.2 `Series`转`DataFrame`
- 7 简单运算
- 7.1 行方向求和/平均值 –> 横向求
- 7.2 列方向求和/平均值 –> 纵向求
- 8 常用函数
- 8.1`rename()`
- 8.2`insert()`
- 8.3`drop()`
- 8.4 `head()`
- 8.5`tail()`
- 四 pandas数据读写
- 1 Excel 文件读取
- 1.1 函数语法
- 1.2 具体实现
- 2 Excel文件写入 – `pd.to_excel(...)`
- 2.1 函数语法
- 2.2 具体实现
- 3 Excel文件写入 – `df.ExcelWriter(...)`
- 3.1 函数语法
- 3.2 具体实现
- 3 toexcel 与 ExcelWriter 的区别与联系
- 3.1 区别
- 3.2 联系
- 4 CSV 文件写入与读取
- 4.1 函数语法
- 4.2 具体实现
- 1. **写入CSV文件(`df.to_csv`)**
- 2. **读取CSV文件(`pd.read_csv`)**
- 5 其他格式文件读写
- 五 pandas 数据筛选
- 1 基本介绍
- 2 直接筛选
- 2.1 介绍
- 2.2 实现
- 3 条件筛选
- 2.1 介绍
- 2.2 实现
- 4 索引器筛选 - loc索引器
- 4.1 介绍
- 4.2 loc 索引 (location的缩写)
- 4.3 loc 索引 (integer location的缩写)
- 六 pandas 数据特征分析
- 1 简单统计分析
- 1.1 基本介绍
- 1.3 示例
- 2 数据排序
- 2.1 基本介绍
- 2.2 函数的常用参数
- 2.3 示例1 – sort_values
- 2.3 示例1 – sort_values
- 3 累计函数
- 3.1 基本介绍
- 3.2 示例
- 七 数据合并
- 1 基本了解
- 2 merge() 函数
- 2.1 介绍
- 2.2 连接键时参数的使用冲突
- **1. 连接键指定冲突**
- **2. 索引参数冲突**
- **3. 多列连接规则**
- 2.3 示例1
- 3 concat() 函数
- 3.1 介绍
- 3.2 示例1 – 纵向连接
- 3.3 示例2 – 横向连接
- 八 数据清洗
- 1 基本介绍
- 2 重复值处理
- 2.1 duplicated () 函数
- 2.2 `drop_duplicates()`函数
- 2.2.1 基本介绍
- 2.2.2 示例
- 3 缺失值处理
- 3.1 介绍
- 3.2 `isna()`函数
- 3.3 `dropna` 函数
- 3.3 `fillna` 函数
- 4 特殊字符删除
- 5 数据类型更改
- 6 示例
- 九 pandas 高阶函数
- 1 Series.map() 方法
- 1.1 介绍
- 2.2 示例1
- 2.3 示例2
- 2 Series/DataFrame.apply(function,args,axis) 方法
- 2.1 介绍
- 2.2 示例1
- 2.3 示例2
- 3 DataFrame.applymap(function)
- 3.1 介绍
- 3.2 示例
- 十 pandas数据分组聚合
- 1 介绍
- 2 分组 – DataFrame.groupby() 函数
- 2.1 介绍
- 2.2 查看分组结果的方法
- 2.3 函数语法
- 2.4 示例
- 3 聚合 – DataFrame.agg() 函数
- 3.1 介绍
- 3.2 函数语法
- 3.3 怎么用这个函数
- 3.4 示例1
- 十一 pandas 数据透视表与轴向转换
- 1 基本介绍
- 1.1 数据透视表
- 1.2 轴向转换
- 2 `DataFrame.pivot_table()`
- 2.1 介绍
- 2.2 函数语法
- 2.3 示例
- 3 `DataFrame.stack 与 DataFrame.unstack`
- 3.1 介绍
- 3.2 函数语法
- 3.3 两者区别
- 3.4 示例
一了解数据处理
1 数据处理
数据处理,通常值数据预处理
- 流程:数据清洗->数据建模->数据加工
在真实世界中,数据通常是不完整的(缺少某些感兴趣的属性值)、不一致的(包含代码或者名称的差异)、极易受到噪声(错误或异常值)的侵扰的。因为数据库太大,而且数据集经常来自多个异构数据源,低质量的数据将导致低质量的数据分析挖掘结果,因此必须对采集(爬取)到的数据进行预处理。
2 数据分析第三方库
该笔记主要记录 numpy
与pandas
库
数据分析库 | 说明 |
---|---|
NumPy | 提供了数组功能以及对数据进行快速处理的函数,是Python中相当成熟和常用的库。 |
Pandas | 是Python下最强大的数据分析和探索工具,包含高级的数据结构和精巧的工具,支持数据增、删、查、改,并有丰富的数据处理函数;支持时间序列分析功能;灵活处理缺失数据等。 |
SciPy | 依赖于NumPy,安装前需先安装NumPy。SciPy包含的功能有最优化、线性代数、积分、插值、拟合、特殊函数、快速傅里叶变换、信号处理和图像处理等其他科学与工程中常用的计算。 |
Matplotlib | 是最著名的绘图库,主要用于二维绘图。它提供了一整套丰富的命令,让我们可以非常快捷地用Python可视化数据,而且允许输出达到出版质量的多种图像格式。 |
二 numpy
1 基本介绍
numpy 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。NumPy是SciPy、Pandas、Matplotlib等数据处理或科学计算库的基础。
- 一个强大的N维数组对象
ndarray
; - 广播功能函数,矩阵的加减法、点乘等:
- 线性代数、傅里叶变换、随机数生成等功能
2 导入库
引入库的注意事项:
通过输入代码import numpy as np
方式引入NumPy
库,其中 np
为引入模块的别名,别名可以省略或更改,但建议使用约定的别名(np
)。引入后,Numpy中的函数就可以直接使用了。
举例
# 导入 numpy 库
import numpy as np
arr = np.arange(5) # [0, 1, 2, 3, 4]
3 数组与列表的区别
在Python语言中已经有了列表类型,并且列表类型可以表示一维数据和多维数据,为什么还需要数组类型(ndarray
)呢?
根本原因当然是,numpy 的运行速度要比 Python 自带的列表要快很多,当数据量大的时候更加明显!
举例:计算 a**6 + b**6 + c**6
,a,b,c均为一维数组
代码
import numpy as np
import pandas as pd
import time# 传统方法 -- 使用普通列表 List 的形式
def SumCheck1():a = [x for x in range(1, 6)]b = [x for x in range(6, 11)]c = [x for x in range(11, 16)]SUM = [] # 设为空列表for i in range(len(a)):SUM.append(a[i]**6 + b[i]**6 + c[i]**6)return SUM# 新方法 -- 使用 numpy 的函数
def SumCheck2():a = np.arange(1, 6)b = np.arange(6, 11)c = np.arange(11, 16)SUM = a**6 + b**6 + c**6 # 不需要 写循环挨个去加了,直接利用numpy的运算规则,就可以算出来return SUMstart = time.time()
SumCheck1()
end = time.time()start2 = time.time()
SumCheck1()
end2 = time.time()print(f'纯python:\n{end - start:.20f}秒', )
print(f'用numpy:\n{end2 - start2:.20f}秒', )
打印结果
使用的计算时间的方法可能精度并不高,很容易有误差,但是大概率也能看出来用numpy比列表速度要快
纯python:
0.00001263618469238281秒
用numpy:
0.00000619888305664062秒
4 ndarray 对象 (numpy的数组对象)
介绍:
NumPy是Python科学计算的核心库,其最重要的特性就是提供了ndarray(N-dimensional array)对象,用于高效存储和处理同类型数据的多维数组。
ndarray的组成:
组成部分 | 说明 |
---|---|
数据缓冲区 | 实际存储数据的连续内存块(固定类型的二进制数据) |
元数据 | 描述数据的属性: • shape (形状,如(3,4)) • dtype (数据类型) • strides (内存步长) |
5 创建方式:
注意:numpy库的核心就是ndarray对象!!
ndarray对象不能直接创建(如arr = np.ndarray()
是错误的),而是需要通过NumPy提供的工厂函数生成,如np.arrange(), np.array(), np.zeros()
等等返回的内容都是ndarray
对象,也就是ndarray只是一种抽象的表达,告诉我们这是个对象
# 创建方式(返回的都是ndarray对象)
arr1 = np.array([1, 2, 3]) # 从现有数据创建
arr2 = np.arange(5) # 生成序列 [0,1,2,3,4]
arr3 = np.zeros((2,3)) # 创建全零数组
arr4 = np.random.rand(4,4) # 生成随机数组
arr = np.ndarray() # 是错误的!!不能直接实例化ndarray类
-
np.array(list/tuple)
– 创建方式1使用列表/元组/其他数组来创建
- 参数可以是 列表 或者 元组
- 会自动将列表或者元组转换为 ndarray对象
# 从列表创建 lis = [x for x in range(1, 6)] arr1 = np.array(lis) print(arr1)# 从元组创建 tup = tuple(lis) arr2 = np.array(tup) print(arr2) ''' 打印结果 [1 2 3 4 5] [1 2 3 4 5] '''
-
np.arange(start,stop,step, dtype=None)
– 创建方式2生成序列来创建
-
start
(可选,默认为0)- 序列的起始值(包含该值)
-
stop
(必需)- 序列的结束值(不包含该值)
- 例如
np.arange(3)
生成[0, 1, 2]
-
step
(可选,默认为1)- 相邻数值的间隔(步长),支持整数和浮点数
- 可以是正数(递增)或负数(递减)
- 例如
np.arange(1, 5, 0.5)
生成[1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]
-
dtype
(可选)-
输出数组的数据类型(如
np.float32
、np.int64
、float
、int
)若Python类型会自动映射:
float
(–>np.float64
)、int
(–>np.int64
)、 -
未指定时自动推断
-
-
6 数组ndarray
的常用属性
注意:属性不是方法,下面的属性和方法是两种不同的概念,不要混淆。
- 属性不带括号
- 方法带着括号
举例代码
import numpy as np # 导入numpy库
arr = np.array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])
'''
[[0 1 2 3 4][5 6 7 8 9]]
'''
属性 | 说明 | 实例 | 结果 |
---|---|---|---|
ndim | 轴的数量或维度的数量**(行数)** | arr.ndim | 2 |
shape | 数组的形状,对于矩阵,n 行m 列,形状为(n,m) | arr.shape | (2, 5) |
size | 数组元素的总个数,相当于shape 中n*m 的值 | arr.size | 10 |
dtype | ndarray 对象的元素类型 | arr.dtype | dtype('int32') |
itemsize | ndarray 对象中每个元素的大小,以字节为单位 | arr.itemsize | 4 |
7 numpy
的随机函数
7.1 基本随机函数(固定区间范围)
返回固定区间范围的随机数
函数 | 范围 | 说明 | 作用 | 返回内容 |
---|---|---|---|---|
random.rand(d0, d1, ..., dn) | [0, 1) | d0 表示第0轴元素个数,依此类推至dn | 生成[0,1) 范围内的浮点随机数 | ndarray |
random.randint(low, high=None, size=None) | [low, high) | size 为输出数组的形状 | 生成[low, high) 范围内的整型随机数范围左闭右开 | ndarray |
7.2 分布随机函数
返回按照某些分布概率统计规则来产生的随机数
函数 | 分布类型 | 说明 | 作用 | 返回内容 |
---|---|---|---|---|
random.randn(d0, d1, ..., dn) | 标准正态分布 | d0 表示第0轴元素个数,依此类推至dn | 生成均值为0、方差为1的标准正态分布 | ndarray |
random.uniform(low=0.0, high=1.0) | [low, high] | 输出与size 参数一致的数组 | 生成[low, high] 范围内的均匀分布随机数范围闭区间 | ndarray |
7.3 代码示例
# 生成范围: [1, 61) 形状: 3 * 3(行列都为3)
a = np.random.randint(low=1, high=61, size=3)
print(a)
'''
[22 25 52]'''
# 生成范围: [0, 1) 形状: 默认 d0 = 1, 即默认第1维度列数为1
b = np.random.rand()
print(b)
'''
0.5183023304438559
'''
# 生成范围: [0, 1) 形状: 1 * 3(列为3) --> d0 = 3, 第1维度列数为3
b2 = np.random.rand(3)
print(b2)
'''
[0.81005766 0.02184353 0.71997443]
'''
# 生成范围: [0, 1) 形状: 2 * 3(行为2,列为3)
b3 = np.random.rand(2, 3)
print(b3)
'''
[[0.4406283 0.09805051 0.38513889][0.08358786 0.63029173 0.02462558]]
'''
8 numpy
常用统计函数
8.1 函数统计:
numpy 有很多统计函数,用于查找数组中的最大最小元素…
函数语法 | 功能说明 | 关键参数说明 |
---|---|---|
sum(a, axis=None) | 计算数组a 沿指定轴的元素和 | axis :可为整数或元组(如0 按列,1 按行,None 全局计算) |
average(a, axis=None, weights=None) | 计算数组a 沿指定轴的加权平均值 | weights :权重数组(需与a 形状匹配) |
std(a, axis=None) | 计算数组a 沿指定轴的标准差 | 默认计算总体标准差(等效于ddof=0 ) |
var(a, axis=None) | 计算数组a 沿指定轴的方差 | 可通过ddof 参数调整自由度(如ddof=1 计算样本方差) |
min(a) / max(a) | 返回数组a 的最小值/最大值(全局) | 等价于np.min(a) /np.max(a) |
median(a) | 计算数组a 的中位数(全局) | 对多维数组会先扁平化处理 |
8.2 axis
怎么用:
axis=0
:垂直方向(跨行操作)axis=1
:水平方向(跨列操作)axis=None
:全局操作(忽略所有轴)
操作 | axis=0 (跨行) | axis=1 (跨列) | axis=None |
---|---|---|---|
np.sum(a=arr, axis=?, dtype=int) | [5, 7, 9] | [6, 15] | 21 |
np.mean(a=arr, axis=?, dtype=float) | [2.5, 3.5, 4.5] | [2., 5.] | 3.5 |
图示 | 列方向求和 | 行方向求和 | 全部元素求和 |
代码演示:
arr = np.array([[1, 2, 3],[4, 5, 6]])
print('对所有的“行”求和:', np.sum(a=arr, axis=0))
print('对所有的“列”求和:', np.sum(a=arr, axis=1))
print('对“全部元素”求和:', np.sum(a=arr, axis=None))
'''
对所有的“行”求和: [5 7 9]
对所有的“列”求和: [ 6 15]
对“全部元素”求和: 21
'''
print('对所有的“行”求平均值:',np.mean(a=arr, axis=0, dtype=float))
print('对所有的“列”求平均值:',np.mean(a=arr, axis=1, dtype=float))
print('对“全部元素”求平均值:',np.mean(a=arr, axis=None, dtype=float))
'''
对所有的“行”求平均值: [2.5 3.5 4.5]
对所有的“列”求平均值: [2. 5.]
对“全部元素”求平均值: 3.5
'''
三 pandas
注意:
在pandas的函数中,经常有axis的函数,而axis=0或axis=1表示的含义容易让人混淆
axis
参数的方向,axis=0
有时看起来是“行”,有时又像是“列”。根本原因在于axis
的定义是基于“操作的方向”而非“行或列本身”官方定义:
axis=0
是“沿行方向”操作(跨行操作)
axis=0
表示操作是垂直方向(从上到下),即跨行(row-wise)操作。axis=1
表示操作是水平方向(从左到右),即跨列(column-wise)操作。
1 pandas
主要介绍
概括
pandas
(python+data+analysis的缩写)、numpy
、matplotlib
三者共同构成了Python数据分析的基础工具包,称为“数据分析三剑客”pandas
是在numpy
的基础上实现的,其核心数据结构与numpy
的ndarray
十分相似,但是pandas
与numpy
的关系不是替代,而是相互补充。
主要特色
- 科学计算
- 类比NumPy的通用函数和广播机制
- 数据读写
- 便捷的数据读写操作
- 分组聚合
- 类比SQL的join和groupby操作
- 数据透视
- 类比Excel的数据透视表操作
- 数据筛选
- 自带正则表达式的字符向量化操作
- 数据统计
- 简单的数据分析统计
- 数据可视化
- 集成matplotlib的基本可视化接口
2 pandas
与numpy
的区别:
-
NumPy:科学计算基础包,专注多维数组高效运算 – 数值计算
NumPy主要用于数值计算,继承了大量矩阵计算模块(短阵运算、线性代数、生成随机数)
-
Pandas:数据分析工具包,基于NumPy构建,专注表格数据处理 – 数值处理
Pandas主要用于数据处理与分析(数据读写、数值计算、数据处理数据分析和数据可视化全套数据分析流程操作)
特性 | NumPy ndarray | Pandas DataFrame/Series |
---|---|---|
数据维度 | 支持任意维度 | 主要处理1D(Series)/2D(DataFrame) |
索引系统 | 纯数字下标 | 可自定义标签索引(行/列名) |
数据类型 | 单数组内类型必须一致 | 支持每列不同数据类型 |
缺失值处理 | 需用np.nan 特殊值 | 原生支持NaN 处理机制 |
3 导入 pandas 库
import pandas as pd # 导入pandas库并使用pd作为别名
4 pandas 的数据结构
4.1 区别
Series
是一个 1 维 ** 数据结构,它由index
(索引)和value
(值)组成。也就是它只有一行或一列**,可以理解为Excel表格的一行或一列。DataFrame
是一个 2 维 结构,除了拥有index
和value
之外,还拥有column
(列)。可以理解为excel表格
数据结构 | 维度 | 访问 |
---|---|---|
series | 1维 | 通过index 索引访问 |
dataframe | 2维 | 通过index 行索引和columns 列索引访问 |
4.2 二者联系
DataFrame
由多个Series
组成,无论是行还是列,单独拆分出来都是一个Series
5 Series
数据类型
5.1 基本介绍
Series
类型一维数据结构,能够保存任何数据类型(整数、浮点数、字符串等),相当于excel表中的一行或一列。由索引和数据组成。
5.2 创建方式
pd.Series(data=None, # 数据源(必选)index=None, # 索引标签dtype=None, # 强制数据类型name=None, # Series名称copy=False, # 是否拷贝数据fastpath=False # 内部优化参数(通常忽略)
)
pd.Serise(list/tuple)
–> 自动索引
import pandas as pd
import numpy as nplis = [1, 2, 3, 4, 5] # 列表
tup = tuple(lis) # 元组print(pd.Series(lis)) # 将列表转换为Series
'''
0 1
1 2
2 3
3 4
4 5
dtype: int64
'''
print(pd.Series(tup)) # 将元组转换为Series
'''
0 1
1 2
2 3
3 4
4 5
dtype: int64
'''
解释输出结果
0 1
1 2
2 3
3 4
4 5
dtype: int64
- 第一列为索引
- 第二列为数值
- 最后一行表示数据类型
-
pd.Serise([1, 2, 3], index=['a', 'b', 'c'])
–> 自定义索引因为
index
为第二个参数,所以可以省略写index=
,直接写['a', 'b', 'c']
也是OK的
import pandas as pd
import numpy as np
lis = [1, 2, 3, 4, 5] # 列表
# 创建方式2 指定索引
print(pd.Series(lis, index=['a', 'b', 'c', 'd', 'e']))
'''
a 1
b 2
c 3
d 4
e 5
dtype: int64
'''
# 如果自定义索引的数量小于数值数量,那么会报错
# 创建方式2 指定索引
print(pd.Series(lis, index=['a', 'b', 'c'])) # 报错!!!!
5 DataFrame
数据类型
5.1 基本介绍
DataFrame
是一个表格型数据类型,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型)- **
DataFrame
既有行索引和有列索引,它可以被看作由Series
**组成的字典(共用一个索引)
行索引 \ 列索引 | 书 | 手机 | 苹果 |
---|---|---|---|
0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 |
2 | 3 | 2 | 1 |
5.2 DataFrame
的创建
pd.DataFrame(data=None, # 数据源(必需)index=None, # 行索引(可选)columns=None, # 列名(可选)dtype=None, # 强制数据类型(可选)copy=False # 是否复制数据(可选)
)
可以由如下类型创建:
- 二维
ndarray
对象 - 由1维
ndarray
、列表、元组、字典或Series
构成的字典 Series
类型- 其他
DataFrame
类型
- **
DataFrame
**具体实现 – 使用参数columns
import pandas as pd# 创建二维列表(2025年3-5月水果销售额数据)
data = [['2025年3月', 8500, 4800, 6200], # 苹果8500元,香蕉4800元,芒果6200元['2025年4月', 9200, 5200, 6800], # 苹果9200元,香蕉5200元,芒果6800元['2025年5月', 7800, 4500, 7500] # 苹果7800元,香蕉4500元,芒果7500元
]# 创建DataFrame
df = pd.DataFrame(data,columns=['日期', '苹果', '香蕉', '芒果'],index=range(1, 4) # 行索引1-3
)# 计算每月总销售额(新增列)
df['月总销售额'] = df[['苹果', '香蕉', '芒果']].sum(axis=1)# 计算三个月总销售额(单独输出)
SUM = df['月总销售额'].sum()# 显示结果
print("2025年3-5月水果销售数据:")
print(df)
print(f"\n三个月总销售额:{SUM}元")''' 打印结果
2025年3-5月水果销售数据:日期 苹果 香蕉 芒果 月总销售额
1 2025年3月 8500 4800 6200 19500
2 2025年4月 9200 5200 6800 21200
3 2025年5月 7800 4500 7500 19800三个月总销售额:60500元
'''
- 具体实现 – 使用「字典」创建
mport pandas as pd# 使用字典形式创建数据
data = {'日期': ['2025年3月', '2025年4月', '2025年5月'],'苹果': [8500, 9200, 7800],'香蕉': [4800, 5200, 4500],'芒果': [6200, 6800, 7500]
}# 创建DataFrame(不再需要columns参数)
df = pd.DataFrame(data, index=range(1, 4))# 计算总销售额(新增列)
df['月总销售额'] = df['苹果'] + df['香蕉'] + df['芒果']# 计算三个月总销售额(单独输出)
SUM = df['月总销售额'].sum()print("2025年3-5月水果销售数据:")
print(df)
print(f"\n三个月总销售额:{SUM}元")
''' 打印结果
2025年3-5月水果销售数据:日期 苹果 香蕉 芒果 月总销售额
1 2025年3月 8500 4800 6200 19500
2 2025年4月 9200 5200 6800 21200
3 2025年5月 7800 4500 7500 19800三个月总销售额:60500元
'''
5.3 DataFrame
的属性
属性不是方法,两者完全不同
- 属性无括号
- 方法有括号
属性 | 说明 | 实例 | 结果示例 |
---|---|---|---|
index | DataFrame的行索引 | df.index | RangeIndex(start=1, stop=4, step=1) |
columns | DataFrame的列索引 | df.columns | Index(['日期', '苹果', '香蕉', '芒果', '月总销售额'], dtype='object') |
values | 存储数据的numpy原生二维数组 | df.values | array([['2025年3月',8500,4800,6200,19500], ['2025年4月',9200,5200,6800,21200], ['2025年5月',7800,4500,7500,19800]], dtype=object) |
dtypes | 各列数据类型 | df.dtypes | 日期:object 苹果:int64 香蕉:int64 芒果:int64 月总销售额:int64 dtype: object |
size | 元素总数(行×列) | df.size | 15 (3行×5列) |
shape | 维度形状(行数,列数) | df.shape | (3, 5) |
ndim | 维度数量 | df.ndim | 2 (始终为2维表格) |
print('\n行索引:', df.index)
print('列索引:', df.columns)
print('原生数组值:', df.values)
print('\n数据类型:\n', df.dtypes)
print('\n元素总数(行数*列数):', df.size)
print('维度形状(行数,列数):', df.shape)
print('维度数量:', df.ndim)
''' 打印结果
行索引: RangeIndex(start=1, stop=4, step=1)
列索引: Index(['日期', '苹果', '香蕉', '芒果', '月总销售额'], dtype='object')
原生数组值: [['2025年3月' 8500 4800 6200 19500]['2025年4月' 9200 5200 6800 21200]['2025年5月' 7800 4500 7500 19800]]数据类型:日期 object
苹果 int64
香蕉 int64
芒果 int64
月总销售额 int64
dtype: object元素总数(行数*列数): 15
维度形状(行数,列数): (3, 5)
维度数量: 2
'''
5.4 访问某行或某列
数据
import pandas as pd
# 使用字典形式创建数据
data = {'日期': ['2025年3月', '2025年4月', '2025年5月'],'苹果': [8500, 9200, 7800],'香蕉': [4800, 5200, 4500],'芒果': [6200, 6800, 7500]
}
df = pd.DataFrame(data, index=['a', 'b', 'c'])
''' 日期 苹果 香蕉 芒果
a 2025年3月 8500 4800 6200
b 2025年4月 9200 5200 6800
c 2025年5月 7800 4500 7500
'''
- 访问列:
单列访问1(返回Series)
# 按列
# 单列(返回Series)
S1 = df['苹果'] # 通过列名
print('苹果:\n', S1)
''' 打印结果
苹果:a 8500
b 9200
c 7800
Name: 苹果, dtype: int64
'''
单列访问2(返回Series)
SS1 = df.苹果 # 属性式访问(仅限列名无空格时) 如果列名是string,不需要加引号,直接写文字就行,加上引号反而错了
print('苹果:\n', SS1)
''' 打印结果
苹果:a 8500
b 9200
c 7800
Name: 苹果, dtype: int64
'''
多列访问1(返回DataFrame)
# 多列(返回DataFrame)
SSS1 = df[['苹果', '芒果']] # 列名列表
print("['苹果', '芒果']\n", SS1)
''' 打印结果
['苹果', '芒果']a 8500
b 9200
c 7800
Name: 苹果, dtype: int64
'''
- 访问行:
切记:使用的是[]
而不是()
单行访问1(返回Series)
# 按行
# 单行(返回Series)
S2 = df.loc['a'] # 通过行标签(index值)
print("a:\n",S2)
''' 打印结果
a:日期 2025年3月
苹果 8500
香蕉 4800
芒果 6200
Name: a, dtype: object
'''
单行访问2(返回Series)
SS2 = df.iloc[0] # 通过行位置(从0开始)
print('0:\n', SS2)
''' 打印结果
0:日期 2025年3月
苹果 8500
香蕉 4800
芒果 6200
Name: a, dtype: object
'''
多行访问1(返回DataFrame)
# 多行(返回DataFrame)
SSS2 = df.loc[['a','b']] # 按标签选择多行
print("['a','b']:\n", SSS2)
''' 打印结果
['a','b']:日期 苹果 香蕉 芒果
a 2025年3月 8500 4800 6200
b 2025年4月 9200 5200 6800
'''
多行访问2(返回DataFrame)
SSSS2 = df.iloc[0:2] # 按位置切片(前两行)--> 标签不能使用切片
print("[0:2]:\n", SSSS2)
''' 打印结果
[0:2]:日期 苹果 香蕉 芒果
a 2025年3月 8500 4800 6200
b 2025年4月 9200 5200 6800
'''
- 行列组合访问方法
使用df.loc()
方法1
# 同时指定行列 --> 使用 loc
a = df.loc['a', '苹果'] # 1行的苹果列(返回标量)
print("'a'行,'苹果'列: ",a) # 打印结果: 'a'行,'苹果'列: 8500
使用df.loc()
方法2
b = df.loc[['a','c'], ['香蕉']] # a c行的香蕉列(返回DataFrame)
print("'a'行与'b'行的'香蕉'列:\n",b)
''' 打印结果
'a'行与'b'行的'香蕉'列:香蕉
a 4800
c 4500
'''
使用df.iloc()
方法1
# 同时指定行列 --> 使用 iloc
# 切记:使用Iloc的使用,列名也要用索引,而不是标签名
c = df.iloc[0, 0] # 1行的苹果列(返回标量)
print("0行,0列: ",a) # 打印结果: 0行,0列: 8500
使用df.iloc()
方法2
d = df.iloc[[0,2], 0] # 0 和 2行的香蕉列(返回DataFrame)
print("0行和2行 * 0列:\n",b)
''' 打印结果
0行和2行 * 0列:香蕉
a 4800
c 4500
'''
使用df.iloc()
方法3
e = df.iloc[0:2, 2] # 0 到 2行的香蕉列(返回DataFrame)
print("0行~2行 * 2列:\n",e)
''' 打印结果
0行~2行 * 2列:a 4800
b 5200
Name: 香蕉, dtype: int64
'''
6 DataFrame
与Series
相互转换
DataFrame
单取出来一行或一列时,都是一个Series
,也可以将Series
转换成DataFreme
6.1 DataFrame
转Series
5.4中访问单行和单列的方式,就是将 DataFrame
转换为Series
S1 = df['苹果'] # 将苹果列转换为 Series
SS1 = df.苹果 # 将苹果列转换为 Series
S2 = df.loc['a']`# 将'a'行转换为 Series
SS2 = df.iloc[0]`# 将 0 行转换为 Series
6.2 Series
转DataFrame
data = [120, 230, 600]
S = pd.Series(data, index=['1月','2月','3月'], name='月销售量')
print(S)df = pd.DataFrame(S)
print(df)''' 打印结果
1月 120
2月 230
3月 600
Name: 月销售量, dtype: int64月销售量
1月 120
2月 230
3月 600
'''
7 简单运算
在Pandas中
-
axis=1 表示按行计算(横向计算),即对每一行的多个列数据进行操作。
-
axis=0(默认值)则表示按列计算(纵向计算),即对每一列的所有行数据进行操作。–> 可省
-
Pandas底层使用NumPy的向量化计算,避免Python循环开销,且内存连续存储,CPU缓存利用率更高。
# 数据
data = {'苹果': [85000, 92000, 78000],'香蕉': [48000, 52000, 45000],'芒果': [62000, 68000, 75000]
}
df = pd.DataFrame(data, index=['1月销售', '2月销售', '3月销售'])
print(df)
''' 打印结果苹果 香蕉 芒果
1月销售 85000 48000 62000
2月销售 92000 52000 68000
3月销售 78000 45000 75000
'''
7.1 行方向求和/平均值 –> 横向求
# 新添加两列
# 在表格最后添加名为“总销量”的列,并且值为,苹果、香蕉、芒果列 --> 相加
df['月总销售量'] = df['苹果'] + df['香蕉'] + df['芒果'] # (向量化操作,无需循环)
df['月总销售量(法2)'] = df[['苹果', '香蕉', '芒果']].sum(axis=1) # axis=1 意思是 按行计算(横向计算),0反之
# 计算每行平均值(每月平均销量)
df['月均销量'] = df[['苹果', '香蕉', '芒果']].mean(axis=1) # axis=1 意思是 按行计算(横向计算),0反之print(df)
''' 打印结果苹果 香蕉 芒果 月总销售量 月总销售量(法2) 月均销量
1月销售 85000 48000 62000 195000 195000 65000.000000
2月销售 92000 52000 68000 212000 212000 70666.666667
3月销售 78000 45000 75000 198000 198000 66000.000000
'''
7.2 列方向求和/平均值 –> 纵向求
# 新添加两行
# 计算每列总和(各水果总销量)
df.loc['3个月总销售'] = df.iloc[0:3, 0:3].sum() # 前3行,前3列(最后一列的“月均销量”不算) -- 使用iloc[]
df.loc['3个月均销售'] = df[['苹果', '香蕉', '芒果']].mean() # 使用标签print(df)
''' 打印结果苹果 香蕉 芒果 月总销售量 月总销售量(法2) 月均销量
1月销售 85000.0 48000.0 62000.0 195000.0 195000.0 65000.000000
2月销售 92000.0 52000.0 68000.0 212000.0 212000.0 70666.666667
3月销售 78000.0 45000.0 75000.0 198000.0 198000.0 66000.000000
3个月总销售 255000.0 145000.0 205000.0 NaN NaN NaN
3个月均销售 127500.0 72500.0 102500.0 NaN NaN NaN
'''
8 常用函数
基本函数 | 描述 |
---|---|
rename() | 修改行索引/列索引 |
insert() | 插入列 |
drop() | 删除行或列 |
head() | 返回DataFrame前n行 |
tail() | 返回DataFrame后n行 |
8.1rename()
对行索引(index)及列索引(columns)重命名
df.rename(mapper=None, # 通用重命名方式(字典/函数)index=None, # 指定行索引修改(字典/函数)columns=None, # 指定列名修改(字典/函数)axis=None, # 替代index/columns(0:行, 1:列)inplace=False, # 是否原地修改level=None # 多级索引的层级
)
-
index
: 行索引index = {X:X, X:X} 以字典或函数的形式
-
columns
: 列索引 -
inplace
: 是否就地修改,即是否在原数据生效inplace=False(默认)
或inplase=True
具体用法:(修改行、列索引)
重命名苹果为 apple, 香蕉为 banana
df.rename(index = {'3个月总销售':'仨月总销售'}, columns={'苹果':'apple', '香蕉':'banana'}, inplace=True)
print(df.index, df.columns, sep='\n')
''' 打印结果
Index(['1月销售', '2月销售', '3月销售', '仨月总销售', '3个月均销售'], dtype='object')
Index(['apple', 'banana', '芒果', '月总销售量', '月总销售量(法2)', '月均销量'], dtype='object')
'''
8.2insert()
给表格插入一列,插入时需要明确位置、列名、值
df.insert(loc, # 插入位置(0-based索引),表示第几列,第一列为loc=0column, # 新列名value, # 插入的数据(int/数组/Series)allow_duplicates=False # 是否允许列名重复,默认为 False,如果列名存在则报错,设置为Ture表示允许列名重复
)
具体用法:
添加水杯列。并且给值
df.insert(0, '水杯', np.array([41, 23, 61, 41+23+61, (41+23+61)/3]))
print(df)
''' 水杯 apple banana ... 月总销售量 月总销售量(法2) 月均销量
1月销售 41.000000 85000.0 48000.0 ... 195000.0 195000.0 65000.000000
2月销售 23.000000 92000.0 52000.0 ... 212000.0 212000.0 70666.666667
3月销售 61.000000 78000.0 45000.0 ... 198000.0 198000.0 66000.000000
仨月总销售 125.000000 255000.0 145000.0 ... NaN NaN NaN
3个月均销售 41.666667 127500.0 72500.0 ... NaN NaN NaN
'''
8.3drop()
删除指定的列或行
提示:
- 如果写了
index=标签
相当于是labels,axis=0
,二者写其一即可 - 如果写了
index=标签
相当于是labels,axis=1
,二者写其一即可
pd.DataFrame.drop(labels=None, # 单个标签或标签列表axis=0, # 0/'index':行标签 | 1/'columns':列标签index=None, # 单个标签或标签列表 index=labels等效于labels,axis=0columns=None, # 单个标签或标签列表 index=labels等效于labels,axis=1level=None, # 多级索引的层级inplace=False, # 是否原地修改 还可以写Trueerrors='raise' # 'ignore'跳过不存在的标签
)
具体用法:
删除 ‘水杯’ 列
df.drop(columns='水杯', inplace=True) # 方法1
df.drop('水杯', axis=1, inplace=True) # 方法2
print(df)
''' 打印结果apple banana 芒果 月总销售量 月总销售量(法2) 月均销量
1月销售 85000.0 48000.0 62000.0 195000.0 195000.0 65000.000000
2月销售 92000.0 52000.0 68000.0 212000.0 212000.0 70666.666667
3月销售 78000.0 45000.0 75000.0 198000.0 198000.0 66000.000000
仨月总销售 255000.0 145000.0 205000.0 NaN NaN NaN
3个月均销售 127500.0 72500.0 102500.0 NaN NaN NaN
'''
8.4 head()
返回前n行
pd.DataFrame.head(n=5 # 显示的行数,默认为5
)
具体用法:
打印前2行
print(df.head(2))
''' 打印结果apple banana 芒果 月总销售量 月总销售量(法2) 月均销量
1月销售 85000.0 48000.0 62000.0 195000.0 195000.0 65000.000000
2月销售 92000.0 52000.0 68000.0 212000.0 212000.0 70666.666667
'''
8.5tail()
返回其后n行
pd.DataFrame.tail(n=5 # 显示的行数,默认为5
)
具体用法:
打印后2行
print(df.tail(2))
''' 打印结果apple banana 芒果 月总销售量 月总销售量(法2) 月均销量
仨月总销售 255000.0 145000.0 205000.0 NaN NaN NaN
3个月均销售 127500.0 72500.0 102500.0 NaN NaN NaN
'''
四 pandas数据读写
门店销售数据-练手.xlsx的数据如下:
年 | 月 | 门店 | 销售收入 | 销售成本 | 销售毛利 |
---|---|---|---|---|---|
2025 | Jan | 门店01 | 478803.28 | 295586.23 | 183217.05 |
2025 | Jan | 门店02 | 442108.16 | 330633.55 | 111474.61 |
2025 | Jan | 门店03 | 367875.06 | 190555.04 | 177320.02 |
2025 | Jan | 门店04 | 174356.94 | 138562.72 | 35794.22 |
2025 | Jan | 门店05 | 182158.68 | 113867.90 | 68290.78 |
2025 | Jan | 门店06 | 413329.60 | 241064.13 | 172265.47 |
2025 | Jan | 门店07 | 343982.98 | 233408.43 | 110574.55 |
2025 | Jan | 门店08 | 352803.32 | 179906.26 | 172897.06 |
2025 | Jan | 门店09 | 228984.02 | 150709.13 | 78274.89 |
… | |||||
… | |||||
该表格中的数据还有很多,显示不开,就展示一部分
-
Pandas数据分析需要处理各种类型的数据,读/写数据也是Pandas的重要功能,Pandas提供了多种接口函数用于支持多种类型数据读取(如CSV、Excel、SQL等)
-
Pandas可以将读取到的表格型数据转换为DataFrame数据,然后通过操作DataFrame进行数据分析、数据预处理及行列操作。
-
该文主要记录的是Pandas最常用的Excel表格的读写
-
pandas 需要openpyxl库来读取 .xlsx 格式的 Excel 文件,所以一定要安装这个库之后再读取Excel文件,否则报错
# 终端输入 pip install openpyxl
1 Excel 文件读取
1.1 函数语法
pd.read_excel(# 必选参数 io, # 【常用】文件路径(如:'data.xlsx')或文件对象# 「核心控制参数」 sheet_name=0, # 【常用】工作表选择(0=首个表,'Sheet1'=表名,None=全读)header=0, # 【常用】表头行(0=首行,None=无表头,[0,1]=多行表头)names=None, # 【常用】自定义列名(如:['姓名','年龄'])index_col=None, # 【常用】索引列(0=第1列,'ID'=列名,[0,1]=多级索引)usecols=None, # 【常用】读取列('A:C'=列范围,[0,2]=列位置,lambda x: '价' in x=筛选)# 「数据类型处理」 dtype=None, # 【常用】列数据类型(如:{'ID': str, '价格': float})parse_dates=False, # 【常用】日期解析(True=自动,['下单时间']=指定列)converters=None, # 列转换函数(如:{'电话': lambda x: str(x)})# 「数据清理」na_values=None, # 【常用】空值标记(如:['NA', 'NULL', ''])keep_default_na=True, # 是否保留默认空值(NaN/None等)skiprows=None, # 【常用】跳过行(5=前5行,[0,2]=第1,3行)nrows=None, # 读取行数(如:100=仅读前100行)# 「性能相关」 engine=None, # 引擎('openpyxl'=xlsx,'xlrd'=xls)memory_map=False, # 内存映射优化大文件# 其他参数(按需使用)skipfooter=0, # 跳过末尾行decimal='.', # 小数点符号(欧洲数据可改为',')**kwds
)
参数 | 解释 | 实例 |
---|---|---|
io | 文件路径 | r'D:\数据资料\data.xlsx' (r写在字符串前面,防止字符转义) |
sheet_name=0 | 导入的sheet页 | 1.sheet_name=0 :默认值,即导入第一个页签,sheet序号从0开始;2. sheet_name='表名’ :直接输入目标sheet的名称,中英文皆可;3. sheet_name='SheetN' :代表第N个sheet,S要大写;4. sheet_name=None :即导入所有页签,返回字典类型的数据。 |
header=0 | 用哪一行做列名 | 1.header=0 ,默认值,表格第一行作为列名;2. header=[0,1] 表示前两行作为列名。 |
names | 自定义列名 | 1.names=['销售量','销售单价','销售总额'] 2.一般适用于Excel缺少列名,或者需要重新定义列名的情况, names 的长度必须和Excel列长度一致,否则会报错 |
index_col | 设置行索引 | 1.index_col=None :默认数据不带行索引,自动分配从0开始的索引号;2. index_col=0 :以第一列作为行索引;3. index_col=[0,1] 表示将前两列作为多重索引 |
usecols | 需要读取哪些列 | 1.usecols=None :默认取所有列;2. usecols=[0,2,3] :以列号代表要取的列;3. usecols=['年','月'] :以列名代表要取的列 |
converters | 强制规定列的数据类型 | converters={'时间':str,'销售单价':float} :将’时间’列数据类型强制规定为字符串类型,'销售单价’列强制规定为浮点型。 |
1.2 具体实现
打印前5行数据
打印前5行数据
import pandas as pd
file_path = r"门店销售数据-练手.xlsx" # 该文件已在根目录# 打开Excel文件,并且将“年、月、门店”这三列的数据强制转换为string类型
df = pd.read_excel(file_path, sheet_name=0, converters={'年':str, '月':str, '门店':str})
print(df.head(5))
''' 打印结果年 月 门店 销售收入 销售成本 销售毛利
0 2025 Jan 门店01 478803.28 295586.23 183217.05
1 2025 Jan 门店02 442108.16 330633.55 111474.61
2 2025 Jan 门店03 367875.06 190555.04 177320.02
3 2025 Jan 门店04 174356.94 138562.72 35794.22
4 2025 Jan 门店05 182158.68 113867.90 68290.78
'''
打印前5行数据(设置行索引为‘门店’列中的数据)
打印前5行,以‘门店’列为表格的行索引
- 下面可以看出来,门店这一列到了最左侧
- 门店这一列是行索引
df2 = pd.read_excel(file_path, sheet_name=0, converters={'年':str, '月':str, '门店':str}, index_col='门店',)
print(df2.head(5))
''' 打印结果年 月 销售收入 销售成本 销售毛利
门店
门店01 2025 Jan 478803.28 295586.23 183217.05
门店02 2025 Jan 442108.16 330633.55 111474.61
门店03 2025 Jan 367875.06 190555.04 177320.02
门店04 2025 Jan 174356.94 138562.72 35794.22
门店05 2025 Jan 182158.68 113867.90 68290.78
'''
2 Excel文件写入 – pd.to_excel(...)
将 df
数据写入到 Excel 文件
- 若路径文件不存在,会自动创建;
- 若存在,则覆盖原内容。
2.1 函数语法
如果找不到这个表格,则新建一个。
df.to_excel(# 「必选参数」excel_writer, # 【常用】文件路径或ExcelWriter对象(如:'output.xlsx')# 「核心控制参数」 sheet_name='Sheet1', # 【常用】工作表名称(默认'Sheet1')na_rep='', # 【常用】空值填充符号(默认空字符串)float_format=None, # 浮点数格式(如:'%.2f'保留两位小数)columns=None, # 【常用】导出的列(默认所有列)header=True, # 【常用】是否写入列名(True/False/[列名列表])index=True, # 【常用】是否写入行索引(True/False)index_label=None, # 索引列标题(字符串或列表,用于多级索引)startrow=0, # 起始行位置(默认0)startcol=0, # 起始列位置(默认0)# 「引擎与格式」 engine=None, # 引擎('openpyxl'或'xlsxwriter')merge_cells=True, # 是否合并单元格(适用于多级索引)inf_rep='inf', # 无穷大值的表示方式freeze_panes=None, # 冻结窗格位置(如:(1,1)冻结首行首列)# 存储控制storage_options=None, # 云存储参数(如S3路径)**kwargs # 其他引擎特定参数
)
参数 | 解释 | 实例 |
---|---|---|
excel_write | 文件路径 | r'D:\数据资料\data2.xlsx' (r 写在字符串前面,防止字符转义) |
sheet_name | 导出的excel表名 | 1. 默认sheet名是'Sheet1' 2. sheet_name='表名' |
index | 是否写入索引 | index=True ,写入索引index=False ,不写入索引 |
2.2 具体实现
将处理好的DataFrame导出为新Excel文件,要求如下:
- 文件命名为“门店销售表2”
- 表名为“毛利率”
import pandas as pdfile_path = r"门店销售数据-练手.xlsx" # 该文件已在根目录
# 打开Excel文件,并且将“年、月、门店”这三列的数据强制转换为string类型,并且以门店为行索引
df2 = pd.read_excel(file_path, sheet_name=0, converters={'年':str, '月':str, '门店':str}, index_col='门店')
print(df2.head(5))# 将df2中的数据存入新表“门店销售表2.xlsx”中去
df2.to_excel('门店销售表2.xlsx', sheet_name='毛利率')
门店销售表2.xlsx表格如下:
门店 | 年 | 月 | 销售收入 | 销售成本 | 销售毛利 |
---|---|---|---|---|---|
门店01 | 2025 | Jan | 478803.28 | 295586.23 | 183217.05 |
门店02 | 2025 | Jan | 442108.16 | 330633.55 | 111474.61 |
门店03 | 2025 | Jan | 367875.06 | 190555.04 | 177320.02 |
门店04 | 2025 | Jan | 174356.94 | 138562.72 | 35794.22 |
门店05 | 2025 | Jan | 182158.68 | 113867.9 | 68290.78 |
门店06 | 2025 | Jan | 413329.6 | 241064.13 | 172265.47 |
门店07 | 2025 | Jan | 343982.98 | 233408.43 | 110574.55 |
… | |||||
… | |||||
还有很多 | 写不开了 |
3 Excel文件写入 – df.ExcelWriter(...)
将 df 数据写入到 Excel 文件
- 若路径文件不存在,会自动创建
- 若存在,则覆盖原内容
3.1 函数语法
pd.ExcelWriter(# 「必选参数」path, # 【必须】文件路径(如:'output.xlsx')engine=None, # 引擎(默认自动选择,可选'openpyxl'/'xlsxwriter')date_format=None, # 日期格式(如:'YYYY-MM-DD')datetime_format=None, # 时间格式(如:'HH:MM:SS')mode='w', # 【常用】写入模式('w'覆盖/'a'追加)if_sheet_exists=None, # 【常用】【追加模式】同名表处理('error'报错/'replace'覆盖/'new'新建)storage_options=None, # 云存储参数(如S3路径)**kwargs # 其他引擎参数
)
3.2 具体实现
将df数据写入“门店销售表2.xlsx”中的新表格”sheet2“中
方法1
手动保存关闭
# pd.Excel() 与 df.to_excel() 结合使用
# 以 a 追加的形式打开'门店销售表2.xlsx'
writer = pd.ExcelWriter('门店销售表2.xlsx', mode='a')
# 将 df 写入到 '门店销售表2.xlsx' 的 'sheet2' 中
df.to_excel(writer, sheet_name='sheet2')
writer._save() # 保存
writer.close() # 关闭
方法2
自动保存关闭
with pd.ExcelWriter('门店销售表2.xlsx', mode='a'):df.to_excel(writer, sheet_name='sheet2')
3 toexcel 与 ExcelWriter 的区别与联系
3.1 区别
对比项 | df.to_excel() | pd.ExcelWriter |
---|---|---|
功能 | 快速写入单个Sheet | 高级控制(多Sheet/追加/格式) |
多Sheet支持 | 只能写1个Sheet | 可写入多个Sheet |
文件模式 | 仅覆盖 | 支持追加 (mode='a'/'w' ) |
代码复杂度 | 单行代码 | 需配合 with 语句 |
适用场景 | 简单导出单个表 | 复杂需求(保留原数据、多表、格式) |
一句话总结:
- 简单导出用
to_excel
,复杂操作(多Sheet/追加)用ExcelWriter
。
3.2 联系
简单需求用 df.to_excel()
,复杂需求(追加/多Sheet/格式)才需配合 ExcelWriter
df.to_excel()
和 pd.ExcelWriter
的关系说明:
-
独立使用
df.to_excel("文件.xlsx")
:直接覆盖写入(默认),适合简单导出单个表。pd.ExcelWriter("文件.xlsx")
:仅创建写入器,需后续操作(如结合to_excel()
)才能写入数据。
-
配合使用
当需要以下功能时才需结合两者:
- 追加数据到已有文件(
mode='a'
)。 - 写入多个Sheet到同一文件。
- 精细控制格式(如单元格样式、公式)。
with pd.ExcelWriter("文件.xlsx", mode='a') as writer:df1.to_excel(writer, sheet_name="Sheet1") # 追加写入df2.to_excel(writer, sheet_name="Sheet2") # 再写一个Sheet
- 追加数据到已有文件(
4 CSV 文件写入与读取
4.1 函数语法
pd.read_csv
pd.read_csv(# 「必选参数」filepath_or_buffer, # 【必须】文件路径/URL/文件对象(如:'data.csv')# 「核心控制参数」sep=',', # 【常用】分隔符(默认逗号,可设为'\t'等)header='infer', # 【常用】列名行(默认首行,None为无列名)names=None, # 自定义列名列表(如:['列1', '列2'])index_col=None, # 【常用】指定索引列(列名或位置)dtype=None, # 列数据类型(如:{'列1': 'float64'})parse_dates=False, # 【常用】是否解析日期列skiprows=None, # 跳过指定行数(如:跳过前3行填3)na_values=None, # 指定缺失值标识(如:['NA', 'NULL'])# 「读取控制」encoding=None, # 【常用】文件编码(如:'utf-8'/'gbk')engine=None, # 引擎('c'/'python'/'pyarrow')converters=None, # 列自定义转换函数true_values=None, # 强制视为True的值false_values=None, # 强制视为False的值# 「存储优化」memory_map=False, # 内存映射读取大文件storage_options=None, # 云存储参数(如S3路径)**kwargs # 其他参数
)
参数 | 解释 | 实例 |
---|---|---|
filepath or buffer | 文件路径或网址 | r'D:\数据资源\data1.csv' |
sep | 指定分隔符,默认’号 | |
header | 指定行数来作为列名; 若文件中没有列名,则默认为0 | |
dtype | 每列数据的数据类型 | {'a':np.float64, 'b':np.int32} |
encoding | 指定字符集类型,通常指为’utf-8’ | |
encoding | 指定文件编码 | 如utf-8 / gbk |
pd.to_csv
df.to_csv(# 「必选参数」path_or_buf, # 【必须】文件路径/URL/文件对象(如:r'D:\数据资源\output.csv')# 「核心控制参数」sep=',', # 【常用】分隔符(默认逗号,可设为'\t'等)na_rep='', # 缺失值填充符号(默认空字符串)float_format=None, # 浮点数格式(如:'%.2f'保留两位小数)columns=None, # 指定导出的列(默认所有列)header=True, # 【常用】是否写入列名(True/False/[列名列表])index=True, # 【常用】是否写入行索引index_label=None, # 索引列标题(字符串或列表)# 「编码与格式」encoding=None, # 【常用】字符集类型(如:'utf-8',图片中明确提及)date_format=None, # 日期格式(如:'%Y-%m-%d')# 「写入控制」mode='w', # 写入模式('w'覆盖/'a'追加)compression='infer', # 压缩格式(如:'gzip')quoting=None, # 引用规则(0-3对应csv.QUOTE_*常量)quotechar='"', # 引用符号line_terminator=None, # 行结束符# 「存储优化」chunksize=None, # 分块写入大小storage_options=None, # 云存储参数(如S3路径)**kwargs # 其他引擎参数
)
参数 | 解释 | 实例 |
---|---|---|
filepath or buffer | 文件路径或网址 | r'D:\数据资源\data1.csv' |
sep | 指定分隔符,默认’号 | |
header | 指定行数来作为列名; 若文件中没有列名,则默认为0 | |
dtype | 每列数据的数据类型 | {'a':np.float64, 'b':np.int32} |
encoding | 指定字符集类型,通常指为’utf-8’ |
4.2 具体实现
1. 写入CSV文件(df.to_csv
)
import pandas as pd
import numpy as np# 创建DataFrame(自动推断类型:a-float64, b-int64, text-object)
data = {'a': [1.1, 2.2, 3.3],'b': [4, 5, 6],'text': ['测试', 'data', 'csv']
}
df = pd.DataFrame(data)# 正确写入CSV(去掉dtype)
df.to_csv(r'测试.csv',sep=',',header=True,index=False,encoding='utf-8'
)
2. 读取CSV文件(pd.read_csv
)
# 正确读取CSV(仅对数值列指定dtype)
df_read = pd.read_csv(r'测试.csv',sep=',',header=0,dtype={'a': np.float64, 'b': np.int32}, # 确保与实际数据保持一致encoding='utf-8'
)print(df_read)
''' 打印结果a b text
0 1.1 4 测试
1 2.2 5 data
2 3.3 6 csv
'''
5 其他格式文件读写
数据源类型 | 读取函数 | 写入函数 |
---|---|---|
Text | read_csv() | to_csv() |
CSV | read_csv() | to_csv() |
Clipboard | read_clipboard() | to_clipboard() |
JSON | read_json() | to_json() |
HTML | read_html() | to_html() |
MS Excel | read_excel() | to_excel() |
HDFS | read_hdf() | to_hdf() |
Msgpack | read_msgpack() | to_msgpack() |
Python Pickle | read_pickle() | to_pickle() |
Stata | read_stata() | to_stata() |
SAS | read_sas() | to_sas() |
SQL | read_sql() | to_sql() |
五 pandas 数据筛选
1 基本介绍
从各类数据源导入的数据DataFrame的数据量会有冗余,
Pandas通过直接筛选、条件筛选、索引器筛选,选择DataFrame中分析所需的数据。
- 直接筛选:直接筛选行列,代码难度最小。
- 条件筛选:筛选满足某些条件的行,如:销售收入高于100万的销售渠道
- 索引器筛选:通过DataFrame索引灵活的筛选数据,灵活性最高。
2 直接筛选
2.1 介绍
返回的都是 DataFrame
使用df[]
直接筛选,根据中括号内容不同可筛选以下数据:
-
选取一列
df['列名']
-
选取多列
df[['列名1', '列名2']]
-
选取连续的行
df[0 : 3] # 选取0~2行的数据
2.2 实现
import numpy as np
import pandas as pd# 打开Excel文件,并且将“年、月、门店”这三列的数据强制转换为string类型
df = pd.read_excel('门店销售数据-练手.xlsx', sheet_name=0)
print(df.head(5))
print(df['年'].head(5)) # 打印列名为'年'的前5行
print(df[['门店', '销售收入']].head(5)) # 打印列名为'门店''销售收入'的前5行
print(df[3:6]) # 打印行索引3到行索引5的数据(索引左闭右开)''' 打印结果年 月 门店 销售收入 销售成本 销售毛利
0 2025 Jan 门店01 478803.28 295586.23 183217.05
1 2025 Jan 门店02 442108.16 330633.55 111474.61
2 2025 Jan 门店03 367875.06 190555.04 177320.02
3 2025 Jan 门店04 174356.94 138562.72 35794.22
4 2025 Jan 门店05 182158.68 113867.90 68290.78
0 2025
1 2025
2 2025
3 2025
4 2025
Name: 年, dtype: int64门店 销售收入
0 门店01 478803.28
1 门店02 442108.16
2 门店03 367875.06
3 门店04 174356.94
4 门店05 182158.68年 月 门店 销售收入 销售成本 销售毛利
3 2025 Jan 门店04 174356.94 138562.72 35794.22
4 2025 Jan 门店05 182158.68 113867.90 68290.78
5 2025 Jan 门店06 413329.60 241064.13 172265.47
'''
3 条件筛选
2.1 介绍
条件筛选,也称为带条件判断的数据筛选,可以通过条件在数据中筛选出条件为True的数据,常见的形式有:
-
在某列中选取满足条件的行
df[df['某列'] == 条件]
-
在多列中选取满足条件的行
df[(df['列1'] == 条件) & (df['列2'] == 条件)]
2.2 实现
print(df[df['门店'] == '门店01'])
print(df[(df['销售收入'] >= 500000) & (df['销售成本'] <= 500000)])
''' 打印结果年 月 门店 销售收入 销售成本 销售毛利
0 2025 Jan 门店01 478803.28 295586.23 183217.05
40 2025 Feb 门店01 475701.16 293574.20 182126.96
80 2025 Mar 门店01 472599.04 291562.17 181036.87
120 2025 Apr 门店01 480996.59 296207.34 184789.25
160 2025 May 门店01 477894.47 294195.31 183699.16
200 2025 Jun 门店01 474792.35 292183.28 182609.07
240 2025 Jul 门店01 483189.90 296828.45 186361.45
280 2025 Aug 门店01 480087.78 294816.42 185271.36
320 2025 Sep 门店01 476985.66 292804.39 184181.27
360 2025 Oct 门店01 485383.21 297449.56 187933.65
400 2025 Nov 门店01 482281.09 295437.53 186843.56
440 2025 Dec 门店01 479178.97 293425.50 185753.47年 月 门店 销售收入 销售成本 销售毛利
145 2025 Apr 门店26 501048.17 319842.42 181205.75
265 2025 Jul 门店26 503241.48 320463.53 182777.95
305 2025 Aug 门店26 500139.36 318451.50 181687.86
385 2025 Oct 门店26 505434.79 321084.64 184350.15
425 2025 Nov 门店26 502332.67 319072.61 183260.06
'''
4 索引器筛选 - loc索引器
4.1 介绍
通过DataFrame索引灵活的筛选数据,灵活性最高。
主要分为:
- loc索引: 自定义索引
- iloc索引: 原始索引
4.2 loc 索引 (location的缩写)
loc()
中既可以写 行索引值(原始索引),也可以用于 行名(自定义索引)
- loc索引器默认使用自定义索引
- 如数据中没有自定义索引,使用原始索引
且无论使用自定义索引还是原始索引,在loc中使用的切片,全是闭区间
根据index和column进行选取,先index后column,中间使用','
隔开。
具体用法
['行1','行2'], '行1':'行2', df['列']>条件
可以和['列1','列2'], '列1':'列2', ['列1','列2']]
任意组合
筛选类型 | 语法示例 | 说明 |
---|---|---|
单行筛选 | df.loc['行'] | 筛选指定单行 |
行列组合筛选 | df.loc[['行1','行2'], ['列1','列2']] | 筛选指定的多行多列组合 |
连续行列筛选 | df.loc['行1':'行2', '列1':'列2'] | 筛选连续范围内的行和列 |
条件筛选+指定列 | df.loc[df['列']>条件, ['列1','列2']] | 筛选满足条件的行,并只保留指定列 |
实操
print(df.loc[2]) # 1:获取行索引为2的数据(返回Series对象)
''' 打印结果
年 2025
月 Jan
门店 门店03
销售收入 367875.06
销售成本 190555.04
销售毛利 177320.02
Name: 2, dtype: object
'''
print(df.loc[[0, 3]]) # 2:获取行索引为0和3的数据(返回DataFrame)
''' 打印结果年 月 门店 销售收入 销售成本 销售毛利
0 2025 Jan 门店01 478803.28 295586.23 183217.05
3 2025 Jan 门店04 174356.94 138562.72 35794.22
'''
print(df.loc[[0, 3], ['年', '门店', '销售收入']]) # 3:获取行索引为0和3的行,且只显示'年'、'门店'、'销售收入'三列
''' 打印结果年 门店 销售收入
0 2025 门店01 478803.28
3 2025 门店04 174356.94
'''
print(df.loc[0:3, ['年', '门店', '销售收入']]) # 4:获取行索引0到3(闭区间),且只显示'年'、'门店'、'销售收入'三列
''' 打印结果年 门店 销售收入
0 2025 门店01 478803.28
1 2025 门店02 442108.16
2 2025 门店03 367875.06
3 2025 门店04 174356.94
'''
print(df.loc[0:3, '年':'销售收入']) # 5:获取行索引0到3(闭区间),且显示从'年'到'销售收入'的所有连续列
''' 打印结果年 月 门店 销售收入
0 2025 Jan 门店01 478803.28
1 2025 Jan 门店02 442108.16
2 2025 Jan 门店03 367875.06
3 2025 Jan 门店04 174356.94
'''
print(df.loc[df['门店']=='门店01', '年':'销售毛利']) # 6:条件筛选门店为'门店01'的所有行,并显示从'年'到'销售毛利'的所有列
''' 打印结果年 月 门店 销售收入 销售成本 销售毛利
0 2025 Jan 门店01 478803.28 295586.23 183217.05
40 2025 Feb 门店01 475701.16 293574.20 182126.96
80 2025 Mar 门店01 472599.04 291562.17 181036.87
120 2025 Apr 门店01 480996.59 296207.34 184789.25
160 2025 May 门店01 477894.47 294195.31 183699.16
200 2025 Jun 门店01 474792.35 292183.28 182609.07
240 2025 Jul 门店01 483189.90 296828.45 186361.45
280 2025 Aug 门店01 480087.78 294816.42 185271.36
320 2025 Sep 门店01 476985.66 292804.39 184181.27
360 2025 Oct 门店01 485383.21 297449.56 187933.65
400 2025 Nov 门店01 482281.09 295437.53 186843.56
440 2025 Dec 门店01 479178.97 293425.50 185753.47
'''
4.3 loc 索引 (integer location的缩写)
- iloc与loc的使用几乎相同
不同的两点
- 不同是
iloc
索引器,只能使用原始索引,不能使用自定义索引。 iloc()
索引器,原始索引初值从0开始,切片前闭后开
六 pandas 数据特征分析
处理数据时,值读取和筛选数据往往是不够的。
还需要对读取和筛选处理啊的数据进行统计
1 简单统计分析
1.1 基本介绍
函数 | 描述 | 函数 | 描述 |
---|---|---|---|
sum() | 所有值求和 | max() | 所有值中的最大值 |
count() | 非空数据数量 | abs() | 绝对值 |
mean() | 所有值的平均值 | prod() | 数组元素的乘积 |
median() | 所有值的中位数 | cumsum() | 累计总和 |
std() | 所有值的标准差 | cumprod() | 累计乘积 |
min() | 所有值的最小值 | describe() | 统计信息摘要 |
以上函数除describe()函数外均有一个axis参数
axis=0
时横向计算 (按列统计)–> (默认)axis=1
时纵向计算 (按行统计)
1.3 示例
import pandas as pd
import numpy as np# 读取数据(假设列名为:年,月,门店,销售收入,销售成本,销售毛利)
df = pd.read_excel('门店销售数据-练手.xlsx', sheet_name=0)# 1 基本统计函数(axis=0 默认列方向)即横向计算
print("==== 列方向计算,即横向计算(各门店横向对比)====")
print(f"总销售收入: {df['销售收入'].sum():.2f}") # 所有门店销售收入总和
print(f"平均销售成本: {df['销售成本'].mean():.2f}") # 所有门店成本平均值
print(f"销售毛利的中位数: {df['销售毛利'].median():.2f}") # 所有门店毛利中位数
print(f"销售收入标准差: {df['销售收入'].std():.2f}") # 销售收入离散程度
print(f"销售成本最低的门店: {df['销售成本'].min():.2f}") # 最低成本值# 2 行方向计算(axis=1 按门店纵向计算)即纵向计算
print("\n==== 行方向计算,即纵向计算(单门店纵向分析)====")
df['利润率'] = df['销售毛利'] / df['销售收入'] # 新增利润率列
print(df[['门店', '利润率']].head(3)) # 显示前3门店利润率# 3 累计计算 -- axis=0,默认的横向计算
df['累计销售收入'] = df['销售收入'].cumsum() # 累计销售收入
print("\n累计销售前3名:")
print(df[['门店', '累计销售收入']].head(3))# 4 统计信息摘要(describe自动忽略非数值列)
print("\n==== 全表统计摘要 ====")
print(df.describe())
打印结果
E:\dev\python\python3.13.2\python.exe E:\code\爬虫\爬虫练习\pandas数据特征分析.py
==== 列方向计算,即横向计算(各门店横向对比)====
总销售收入: 143920482.12
平均销售成本: 191512.75
销售毛利的中位数: 100302.29
销售收入标准差: 103919.78
销售成本最低的门店: 67211.50==== 行方向计算,即纵向计算(单门店纵向分析)====门店 利润率
0 门店01 0.382656
1 门店02 0.252143
2 门店03 0.482012累计销售前3名:门店 累计销售收入
0 门店01 478803.28
1 门店02 920911.44
2 门店03 1288786.50==== 全表统计摘要 ====年 销售收入 ... 利润率 累计销售收入
count 480.0 480.000000 ... 480.000000 4.800000e+02
mean 2025.0 299834.337750 ... 0.359177 7.226684e+07
std 0.0 103919.782544 ... 0.078693 4.159336e+07
min 2025.0 100024.530000 ... 0.199902 4.788033e+05
25% 2025.0 214257.017500 ... 0.318648 3.594607e+07
50% 2025.0 286253.440000 ... 0.332734 7.167544e+07
75% 2025.0 378582.420000 ... 0.403608 1.076669e+08
max 2025.0 505434.790000 ... 0.581908 1.439205e+08[8 rows x 6 columns]进程已结束,退出代码为 0
2 数据排序
2.1 基本介绍
数据的排序是比较常用的操作,Pandas 提供的排序分为两种:
- 对索引进行排序:
sort_index()
进行索引的排序。- 通过
axis
参数指定对行索引排序还是对列索引排序,默认为 0,表示对行索引排序,设置为 1 表示对列索引进行排序; ascending
参数指定升序还是降序,默认为True
表示升序,设置为False
表示降序。
- 通过
- 对值进行排序:
sort_values()
进行值的排序。- 相比
sort_index()
方法,多了一个by
参数,按字符串或者列表,来指定要排序的行或者列名,其余基本一致。
- 相比
2.2 函数的常用参数
by参数在DataFrame中使用,Series不需要
# 按索引排序
sort_index(axis=0, # 0表示按行索引排序,1表示按列索引排序ascending=True, # True升序,False降序inplace=False, # 是否原地修改:False返回新对象,True直接修改原对象na_position='last' # 缺失值位置:'last'放最后,'first'放最前
)# 按值排序
sort_values(by='column_name' # 按指定列名排序(当axis=0时)axis=0, # 0表示按行排序,1表示按列排序ascending=True, # True升序,False降序inplace=False, # 是否原地修改kind='quicksort', # 排序算法:'quicksort'快速排序,'mergesort'归并排序等na_position='last', # 缺失值位置
)
2.3 示例1 – sort_values
题目:筛选销售收入“前3”名的数据
代码
# 5 筛选销售收入”前3“名的数据
print("\n==== 筛选销售收入”前3“名的数据 ====")
# 按照列‘销售收入’,按行,降序,不修改原值,前3行
print(df.sort_values('销售收入', axis=0, ascending=False, inplace=False).head(3))
打印结果
==== 筛选销售收入”前3“名的数据 ====年 月 门店 销售收入 销售成本 销售毛利 利润率 累计销售收入
385 2025 Oct 门店26 505434.79 321084.64 184350.15 0.364736 1.161108e+08
265 2025 Jul 门店26 503241.48 320463.53 182777.95 0.363201 7.994210e+07
425 2025 Nov 门店26 502332.67 319072.61 183260.06 0.364818 1.282792e+08
2.3 示例1 – sort_values
题目:
计算销售毛利前5的数据,如销售毛利相同,根据销售收入升序排序。
代码
# 6 计算销售毛利前5的数据(如销售毛利相同,根据销售收入升序排序)
print("\n==== 计算销售毛利前5的数据,如销售毛利相同,根据销售收入升序排序 ====")
# 按照列['销售毛利', '销售收入'],按行,按[降序, 升序],不修改原值,前3行
print(df.sort_values(['销售毛利','销售收入'], ascending=[False, True], inplace=False).head())
打印结果
==== 计算销售毛利前5的数据,如销售毛利相同,根据销售收入升序排序 ====年 月 门店 销售收入 销售成本 销售毛利 利润率 累计销售收入
376 2025 Oct 门店17 438155.62 225334.80 212820.82 0.485720 1.133156e+08
416 2025 Nov 门店17 435053.50 223322.77 211730.73 0.486677 1.255119e+08
256 2025 Jul 门店17 435962.31 224713.69 211248.62 0.484557 7.716657e+07
456 2025 Dec 门店17 431951.38 221310.74 210640.64 0.487649 1.375841e+08
296 2025 Aug 门店17 432860.19 222701.66 210158.53 0.485511 8.927516e+07
3 累计函数
3.1 基本介绍
对于Pandas的dataframe的某一列数据,想要获取该列的数值从大到小依次的累积占比(如累计加和,累计乘积,累计最大值,累计最小值)时,会用到累计函数
作用就是逐行累加/乘/最大/最小
函数 | 作用 |
---|---|
cumsum | 计算前 1/2/3/…/n 个数的和 |
cumprod | 计算前 1/2/3/…/n 个数的积 |
cummax | 计算前 1/2/3/…/n 个数的最大值 |
cummin | 计算前 1/2/3/…/n 个数的最小值 |
3.2 示例
题目
- 筛选出各家门店一月份(
'Jan'
)的数据,根据销售收入降序排列 - 使用cumsum计算累计销售收入,打印前5行
累计销售收入什么意思?
- 计算的是前n家门店的销售总额
- 比如门店1只有门店1的销售额,门店2 是门店1加门店2的销售额, 依次类推
代码
# 筛选出各家门店一月份(`'Jan'`)的数据,根据“销售收入”降序排列
# 使用cumsum计算累计销售收入
# 打印前5行
print("\n==== 筛选出各家门店一月份(`'Jan'`)的数据,根据销售收入降序排列 ====\n==== 使用cumsum计算累计销售收入,打印前5行 ====")
data = df.loc[df['月']=='Jan'].sort_values('销售收入', ascending=False, inplace=False)
data['累计销售收入'] = data['销售收入'].cumsum()
print(data.head())
'''
==== 筛选出各家门店一月份(`'Jan'`)的数据,根据销售收入降序排列 ====
==== 使用cumsum计算累计销售收入,打印前5行 ====年 月 门店 销售收入 销售成本 销售毛利 利润率 累计销售收入
25 2025 Jan 门店26 498854.86 319221.31 179633.55 0.360092 498854.86
0 2025 Jan 门店01 478803.28 295586.23 183217.05 0.382656 977658.14
20 2025 Jan 门店21 448175.59 305059.40 143116.19 0.319331 1425833.73
1 2025 Jan 门店02 442108.16 330633.55 111474.61 0.252143 1867941.89
33 2025 Jan 门店34 440549.17 299973.43 140575.74 0.319092 2308491.06
'''
七 数据合并
1 基本了解
在实际处理数据业务需求中,经常会遇到将多个表连接起来再进行数据处理的事情,类似于SQL查询。数据合并是数据处理过程中的必须要做的步骤
经常使用的数据合并方法有两种
merge()
函数:只能用于两个DataFrame对象之间列方向(横向)进行内联或外联合并操作,默认取交集concat()
函数:可用于两个或多个DataFrame对象之间行/列方向进行内联或外联合并操作,默认对行(沿y轴)取并集
列连接连接是【横向连接】
行连接是【纵向连接】
2 merge() 函数
2.1 介绍
作用及使用
- 具有表连接功能,可以根据一个或多个键(列值)将不同DataFrame连接起来
- 两个DataFrame存在相同的键(列值),根据键(列值)整合到一张表里面。
函数语法
pandas.merge(left, # 【常用】左侧DataFrame对象right, # 【常用】右侧DataFrame对象how='inner', # 【常用】合并方式:'inner'(默认), 'left', 'right', 'outer', 'cross'on=None, # 【常用】用于连接的列名(两个表共有的列名)left_on=None, # 【常用】左侧表中用作连接键的列right_on=None, # 【常用】右侧表中用作连接键的列left_index=False, # 是否使用左侧表的索引作为连接键right_index=False, # 是否使用右侧表的索引作为连接键sort=False, # 是否按连接键排序结果suffixes=('_x', '_y'), # 【常用】重复列名的后缀(默认_x和_y)copy=True, # 是否复制数据(默认True)indicator=False, # 是否添加_merge列显示合并来源validate=None # 检查合并类型:"one_to_one", "one_to_many", "many_to_one", "many_to_many"
)
- how
'inner'
:内连接(默认),只保留键匹配的行 –> 取交集'left'
:左连接,保留左表所有行 –> 以左表为主'right'
:右连接,保留右表所有行 –> 以右表为主'outer'
:全外连接,保留所有行 –> 取并集'cross'
:笛卡尔积连接
- on/left_on/right_on
on
:当左右两个表的连接键列名完全相同时使用left_on
和right_on
:当左右两个表的连接键列名不同时使用- 要么只用用
on
,要么就用left_on
和right_on
两个,不要混用
- suffixes
- 当两个DF有相同非连接列时,自动添加后缀区分
参数 | 说明 |
---|---|
left、right | 两个不同的 DataFrame |
how | 连接方式,有 inner、outer、left、right,默认为 inner 内连接 |
on | 用于连接的列索引名称,左右两个 DataFrame 中必须同时存在,如果没有指定且 left_index 和 right_index 为 False,则以两个 DataFrame 列名交集作为连接键 |
left_on | 左侧 DataFrame 中用于连接键的列名,该参数在左右列名不同但代表的含义相同时非常有用 |
right_on | 右侧 DataFrame 中用于连接键的列名,该参数在左右列名不同但代表的含义相同时非常有用 |
left_index | 默认 False,设置为 True 代表使用左侧 DataFrame 中的行索引作为连接键 |
right_index | 默认 False,设置为 True 代表使用右侧 DataFrame 中的行索引作为连接键 |
sort | 默认为 False,是否将合并的数据进行排序,设置为 False 可以提高性能 |
suffixes | 字符串值组成的元组,两个表存在除主键之外的相同列名时,在列名后面附加的后缀名称用以区分数据来源于哪个表,默认为 (‘_x’,‘_y’) |
2.2 连接键时参数的使用冲突
以下是 pandas.merge()
中不能混用的参数组合总结: 互斥关系
1. 连接键指定冲突
on
+left_on
/right_on
–> 冲突- 用
on
(列名相同)时,不能同时指定left_on
/right_on
- 用
left_on
/right_on
(列名不同)时,不能同时指定on
- 用
left_on
/right_on
+left_index
/right_index
–> 冲突left_on
/right_on
–> 用列名作为键时,不能同时用索引作为键left_index
/right_index
–> 用索引作为键时,不能同时使用列名作为键
2. 索引参数冲突
left_index
+left_on
- 左表不能同时用索引和列作为键
right_index
+right_on
- 右表同理
3. 多列连接规则
- 允许用列表指定多列(如
on=['col1', 'col2']
)。 - 但列表中的列名不能和索引混用!!!
2.3 示例1
门店销售数据.xlsx文件中Sheet1页签**(销售收入、销售成本、销售毛利),Sheet2页签(销售量),按照年、月和门店进行合并,并存入新表销售新表中的Sheet1**中,以使进行后续销售业务数据分析。
分析
- 首先这两个表,都有年、月、门店,合并也就是需要将销售量合并到Sheet1中,所以使用左连接
- 以年、月、门店三列作为复合键
- 目标:将sheet2合并到sheet1中,并存入新表销售新表的Sheet1中
Sheet1
年 | 月 | 门店 | 销售收入 | 销售成本 | 销售毛利 |
---|---|---|---|---|---|
2025 | Jan | 门店01 | 478803.28 | 295586.23 | 183217.05 |
2025 | Jan | 门店02 | 442108.16 | 330633.55 | 111474.61 |
2025 | Jan | 门店03 | 367875.06 | 190555.04 | 177320.02 |
2025 | Jan | 门店04 | 174356.94 | 138562.72 | 35794.22 |
…… | … |
Sheet2
年 | 月 | 门店 | 销售量 |
---|---|---|---|
2025 | Jan | 门店01 | 1970 |
2025 | Jan | 门店02 | 2204 |
2025 | Jan | 门店03 | 1270 |
2025 | Jan | 门店04 | 923 |
…… | … |
代码
import pandas as pd
import numpy as npdf_Sheet1 = pd.read_excel('门店销售数据-练手.xlsx', sheet_name=0)
df_Sheet2 = pd.read_excel('门店销售数据-练手.xlsx', sheet_name=1)# 将 Sheet2 合并到 Sheet1 中 --> 左连接
merge_df = pd.merge(left=df_Sheet1, right=df_Sheet2, how='left', on=['年','月','门店'])
# 创建新表,并将合并结果存入新的表格中
merge_df.to_excel('销售新表.xlsx', sheet_name='Sheet1')
New = pd.read_excel('销售新表.xlsx', sheet_name=0)
# 检查新表是否正确 --> 打印前五行
print(New.head(5))
''' 打印结果Unnamed: 0 年 月 门店 销售收入 销售成本 销售毛利 销售量
0 0 2025 Jan 门店01 478803.28 295586.23 183217.05 1970
1 1 2025 Jan 门店02 442108.16 330633.55 111474.61 2204
2 2 2025 Jan 门店03 367875.06 190555.04 177320.02 1270
3 3 2025 Jan 门店04 174356.94 138562.72 35794.22 923
4 4 2025 Jan 门店05 182158.68 113867.90 68290.78 759
'''
新表数据
年 | 月 | 门店 | 销售收入 | 销售成本 | 销售毛利 | 销售量 | |
---|---|---|---|---|---|---|---|
0 | 2025 | Jan | 门店01 | 478803.28 | 295586.23 | 183217.05 | 1970 |
1 | 2025 | Jan | 门店02 | 442108.16 | 330633.55 | 111474.61 | 2204 |
2 | 2025 | Jan | 门店03 | 367875.06 | 190555.04 | 177320.02 | 1270 |
3 | 2025 | Jan | 门店04 | 174356.94 | 138562.72 | 35794.22 | 923 |
4 | 2025 | Jan | 门店05 | 182158.68 | 113867.9 | 68290.78 | 759 |
… |
3 concat() 函数
3.1 介绍
- merge()是横向连接,也就是将左边的表和右边的表按照**键(列值)**进行拼接
- 如果对于相似结构的DataFrame,使用concat()函数可以实现纵向合并
- 可以连接两个或两个以上的DataFrame对象
函数原型
pandas.concat(objs, # 【常用】要连接的DataFrame或Series对象序列(列表或字典)axis=0, # 【常用】:0/'index'(纵向拼接),1/'columns'(横向拼接)join='outer', # 【常用】连接方式:'outer'(默认,保留所有列/行),'inner'(只保留共有列/行)ignore_index=False, # 是否忽略原索引重建新索引(默认False保留原索引)keys=None, # 创建分层索引的键(用于标识不同来源数据)levels=None, # 指定用作分层索引的级别(配合keys使用)names=None, # 分层索引的名称(配合keys使用)verify_integrity=False, # 是否检查索引重复(默认False不检查)sort=False, # 【常用】是否对非连接轴排序(默认False)copy=True # 是否复制数据(默认True)
)
-
objs 【必需】
- 连接对象:接受包含多个DataFrame/Series的列表或字典
- 示例:
[df1, df2]
或{'A': df1, 'B': df2}
-
axis 【常用】
0
或'index'
:行连接,即纵向连接(默认)1
或'columns'
:列连接,即横向连接
-
join 【常用】
连接方式
'outer'
:保留所有列/行(缺值填充NaN)'inner'
:只保留共有列/行
-
ignore_index
True
:丢弃原索引,生成新数字索引(0,1,2…)False
:保留原索引(可能导致索引重复)
-
sort
True
:默认,将合并后的数据进行排序。性能会降低False
:不排,可以提高性能
3.2 示例1 – 纵向连接
# 纵向连接 -- 行连接
concat_df1 = pd.concat(objs=[df_Sheet1,df_Sheet2], axis=0, sort=False)
print(concat_df1)
''' 打印结果年 月 门店 销售收入 销售成本 销售毛利 销售量
0 2025 Jan 门店01 478803.28 295586.23 183217.05 NaN
1 2025 Jan 门店02 442108.16 330633.55 111474.61 NaN
2 2025 Jan 门店03 367875.06 190555.04 177320.02 NaN
3 2025 Jan 门店04 174356.94 138562.72 35794.22 NaN
4 2025 Jan 门店05 182158.68 113867.90 68290.78 NaN
.. ... ... ... ... ... ... ...
475 2025 Dec 门店36 NaN NaN NaN 1169.0
476 2025 Dec 门店37 NaN NaN NaN 815.0
477 2025 Dec 门店38 NaN NaN NaN 706.0
478 2025 Dec 门店39 NaN NaN NaN 1001.0
479 2025 Dec 门店40 NaN NaN NaN 669.0[960 rows x 7 columns]
'''
3.3 示例2 – 横向连接
# 横向连接 -- 列连接
concat_df2 = pd.concat(objs=[df_Sheet1,df_Sheet2], axis=0, sort=False)
print(concat_df2)
''' 打印结果年 月 门店 销售收入 销售成本 销售毛利 销售量
0 2025 Jan 门店01 478803.28 295586.23 183217.05 NaN
1 2025 Jan 门店02 442108.16 330633.55 111474.61 NaN
2 2025 Jan 门店03 367875.06 190555.04 177320.02 NaN
3 2025 Jan 门店04 174356.94 138562.72 35794.22 NaN
4 2025 Jan 门店05 182158.68 113867.90 68290.78 NaN
.. ... ... ... ... ... ... ...
475 2025 Dec 门店36 NaN NaN NaN 1169.0
476 2025 Dec 门店37 NaN NaN NaN 815.0
477 2025 Dec 门店38 NaN NaN NaN 706.0
478 2025 Dec 门店39 NaN NaN NaN 1001.0
479 2025 Dec 门店40 NaN NaN NaN 669.0[960 rows x 7 columns]
'''
八 数据清洗
1 基本介绍
介绍和作用
- 数据清洗是数据分析的必要和重要步骤,只有经过数据清洗,数据才是干净的, 分析的结果才是有价值的
- 数据清洗是指发现并纠正数据文件中可是别的错误操作,包括检查数据一致性,处理无效值缺失值
清洗内容:缺失值数据、重复值数据、其他异常数据
2 重复值处理
针对清洗重复数据,Pandas提供了duplicated()
和 drop_duplicates()
两种方法
- 前者查找重复行
- 后者删除查找到的重复行
2.1 duplicated () 函数
查找重复值,如果对应的数据是重复的,duplicated()
会返回 True
,否则返回 False
,返回类型是bool
函数语法
DataFrame.duplicated(subset=None, keep='first')
-
subset
-
设置需要检查的列。如果是多个列,可以使用列名的 list 作为参数。
-
示例:
subset=['列1', '列2']
-
-
keep
-
确定要标记的重复项:
-
'first'
(默认)标记除第一次出现的重复项 -
'last'
:标记除最后一次出现的重复项 -
False
:标记所有的重复项
-
示例
返回True的行表示有重复值
# 引入库
import numpy as np
import pandas as pd
# 读取数据
df=pd.read_excel('表.xlsx')
# 查找重复项
print(df.duplicated().head())
''' 打印结果
0 True
1 False
2 False
3 False
4 False
dtype: bool
'''
2.2 drop_duplicates()
函数
2.2.1 基本介绍
返回内容:返回删除重复行的DataFrame
函数语法:
DataFrame.drop_duplicates(subset=None, keep='first', inplace=False, ignore_index=True
)
参数 | 说明 |
---|---|
subset | 设置想要检查的列。如果是多个列,可以使用列名的 list 作为参数 |
keep | 确定要标记的重复项:'first' (默认):标记除第一次出现的重复项'last' :标记除最后一次出现的重复项False :标记所有的重复项 |
inplace | 默认为False ,True 时直接在原数据上删除 |
ignore_index | 是否重置索引:True :删除重复行后生成新索引(0,1,2…)False :保留原始索引(可能导致索引不连续) |
2.2.2 示例
删除**‘去重复项_练习.xlsx’**中的重复项,并保留第一次出现的重复项
‘去重复项_练习.xlsx’
索引 | |
---|---|
0 | 1 |
1 | 1 |
2 | 1 |
3 | 1 |
4 | 2 |
5 | 3 |
6 | 4 |
7 | 5 |
8 | 6 |
9 | 7 |
10 | 8 |
11 | 8 |
12 | 9 |
13 | 10 |
代码 – 不重置索引
# 引入库
import numpy as np
import pandas as pd
# 读取数据
df=pd.read_excel('去重复项_练习.xlsx')
# 查找重复项 不重置索引
df.drop_duplicates(subset=None,keep='first', # 标记第一个出现的重复项inplace=True, # 在原数据修改ignore_index=False # 不重置索引
)
print(df.head(5))
结果
左侧行索引是不连续的,采取的原数据的索引
索引
0 1
4 2
5 3
6 4
7 5
8 6
9 7
10 8
12 9
13 10
代码 – 重置索引
# 引入库
import numpy as np
import pandas as pd
# 读取数据
df=pd.read_excel('去重复项_练习.xlsx')
# 查找重复项 不重置索引
df.drop_duplicates(subset=None,keep='first', # 标记第一个出现的重复项inplace=True, # 在原数据修改ignore_index=True # 重置索引
)
print(df)
结果
可以明显看到,第一列也就是行索引变成了从0到9
索引
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
3 缺失值处理
3.1 介绍
缺失值的处理包含两个步骤:①缺失值判断 ②空值处理
常用函数
isna() # 检测空值
dropna() # 删除空值
fillna() # 使用指定的数据填充空值
3.2 isna()
函数
作用
- 函数检测给定系列对象汇总的缺失值。它返回一个bool值相同大小的对象,指示值是否为NaN
- 缺失值被映射到True并且将非缺失值映射到False
用法
df.isna()
:查看缺失值位置df.isna().any()
:判断某一列是否有缺失值df.isna0.sum()
:统计每列缺失值数量df.isna().sum().sum():
统计DataFrame中缺失值合计数
3.3 dropna
函数
函数
DataFrame.dropna(axis=0, # 删除轴向how='any', # 删除条件thresh=None, # 非缺失值阈值subset=None, # 检查范围inplace=False # 是否原地修改
)
参数
常用参数
常用参数 | 说明 |
---|---|
axis | 默认为 0,表示逢空值去除整行,如果设置参数 axis =1 表示逢空值去掉整列 |
how | how='any' 如果一行(或一列)里任何一个数据有出现 NaN 就去掉整行(默认)how='al' 一行(或列)都是 NaN 才去掉这整行 |
thresh | 设置需要多少非空值的数据才可以保留下来的 |
subset | 设置想要检查的列。如果是多个列,可以使用列名的list 作为参数 |
inplace | True,将计算得到的值直接覆盖之前的值并返回 None,修改的是源数据 |
示例代码
删除元数据上全为NaN的列
dropna(axis=1,how='all',inplace=True)
3.3 fillna
函数
函数
DataFrame.fillna(value=None, # 填充值method=None, # 填充方法axis=None, # 填充轴向inplace=False, # 是否原地修改limit=None # 最大填充次数
)
常用参数 | 说明 |
---|---|
value | 固定值填充 |
method | method=None (默认),使用指定的填充值填充method='ffill'或'pad' ,表示用前一个非缺失值去填充该缺失值method ='bflii'或backfill' ,表示用下一个非缺失值填充 |
axis | 若axis=0 ,则对各行数据进行填充若 axis=1 ,则对各列据进行填充 |
inplacelimit | 为可选参数,默认为False,表示不修改原对象,若指定inplace=True,则直接修改原对象。Iimit参数用于指定每列或每行缺失值填充的数量 |
代码示例
以0填充所有缺失值
df.fill(0)
4 特殊字符删除
现将所有数据转换为字符串,然后使用replace()
函数进行替换,一般与高阶函数同时使用
5 数据类型更改
数据类型更改:先使用属性查看数据类型,再根据需求使用astype()
函数更改数据类型
①整数可以转换为浮点数 ②浮点数转整数时直接取整数位 ③字符串转数值型时字符串需全为数字
6 示例
- 删除表格中单元格包含的空格的符号
- 将所有数字列转换为浮点数
- 以每列平均值填充该列缺失值
df=pd.read_excel('门店销售数据(原表).xlsx')
df = df.map(lambda x:str(x).replace('', ''))
print(df)
九 pandas 高阶函数
1 Series.map() 方法
1.1 介绍
-
根据提供的函数对指定序列逐一做映射,可以接受一个函数或含有映射关系的字典型对象。
-
将Series中每个元素逐一传入字典得到的返回值
-
也就是map是对Serise对象使用的,所以对DataFrame用的时候可以直接取出一列或者一行,就是 Series 对象 –> map 不能对DataFrame使用
-
map()
的返回值是一个新的 Series,其索引与原 Series 一致,但值是根据映射关系转换后的结果
函数语法
Series.map(function / dict) #函数或字典
- 参数可以是lambda匿名函数、自定义函数、内置函数,也可以是字典
2.2 示例1
参数接收函数
根据读取的数据,将销售收入达到400000被评为’优秀门店’,否则标记为’非优秀门店’
方法1 –> 使用循环,不易操作,增加代码量
import pandas as pd
import numpy as np
# 读取表格数据
data = pd.read_excel('门店销售数据(原表).xlsx', sheet_name=0)
# 对销售收入进行遍历
for i in data['销售收入'].index:# 添加新列,内容为优秀门店或非优秀门店data.loc[i,'门店级别'] = '优秀门店' if data.loc[i,'销售收入'] >= 400000 else '非优秀门店'
# 打印前5行检查数据是否正确
print(data.head())
''' 打印结果年 月 门店 销售收入 销售成本 销售毛利 门店级别
0 2025 Jan 门店01 478803.28 295586.23 183217.05 优秀门店
1 2025 Jan 门店02 442108.16 330633.55 111474.61 优秀门店
2 2025 Jan 门店03 367875.06 190555.04 177320.02 非优秀门店
3 2025 Jan 门店04 174356.94 138562.72 35794.22 非优秀门店
4 2025 Jan 门店05 182158.68 113867.90 68290.78 非优秀门店
'''
方法2 – 使用 map –> 不用循环,减少代码量
解释
data['销售收入']
: 得到一个Series
对象data['销售收入'].map(函数)
: 对Series
对象
{'优秀门店':'A类门店', '非优秀门店':'其他门店'}
Series 索引 | 输入值(data['销售收入'] ) | 映射规则(lambda x: '优秀门店' if x >= 400000 else '非优秀门店' ) | 输出值(data['门店级别'] ) |
---|---|---|---|
0 | 478803.28 | 478803.28 >= 400000 --> True | 优秀门店 |
1 | 442108.16 | 442108.16 >= 400000 --> True | 优秀门店 |
2 | 367875.06 | 367875.06 >= 400000 --> False | 非优秀门店 |
3 | 174356.94 | 174356.94 >= 400000 --> False | 非优秀门店 |
4 | 182158.68 | 182158.68 >= 400000 --> False | 非优秀门店 |
代码
# 方法2
# 读取表格数据
data = pd.read_excel('门店销售数据(原表).xlsx', sheet_name=0)
# 使用 map 对新列 门店级别 进行判断操作
data['门店级别'] = data['销售收入'].map(lambda x: '优秀门店' if x >= 400000 else '非优秀门店')
# 打印前5行检查数据是否正确
print(data.head())
''' 打印结果年 月 门店 销售收入 销售成本 销售毛利 门店级别
0 2025 Jan 门店01 478803.28 295586.23 183217.05 优秀门店
1 2025 Jan 门店02 442108.16 330633.55 111474.61 优秀门店
2 2025 Jan 门店03 367875.06 190555.04 177320.02 非优秀门店
3 2025 Jan 门店04 174356.94 138562.72 35794.22 非优秀门店
4 2025 Jan 门店05 182158.68 113867.90 68290.78 非优秀门店
'''
2.3 示例2
参数接收字典
将数据中刚插入的**‘门店级别’列中’优秀门店’替换成’A类门店’,其他替换为’其他门店’**
Series 索引 | Series 元素 | Series 索引 | Series 元素 | |
---|---|---|---|---|
0 | ‘优秀门店’ | –> | 0 | ‘A类门店’ |
1 | ‘优秀门店’ | –> | 1 | ‘A类门店’ |
2 | ‘非优秀门店‘ | –> | 2 | ‘其他门店’ |
3 | ‘非优秀门店‘ | –> | 3 | ‘其他门店’ |
4 | ‘非优秀门店‘ | –> | 4 | ‘其他门店’ |
data['门店级别'] = data['门店级别'].map({'优秀门店':'A类门店', '非优秀门店':'其他门店'})
print(data.head())''' 打印结果年 月 门店 销售收入 销售成本 销售毛利 门店级别
0 2025 Jan 门店01 478803.28 295586.23 183217.05 A类门店
1 2025 Jan 门店02 442108.16 330633.55 111474.61 A类门店
2 2025 Jan 门店03 367875.06 190555.04 177320.02 其他门店
3 2025 Jan 门店04 174356.94 138562.72 35794.22 其他门店
4 2025 Jan 门店05 182158.68 113867.90 68290.78 其他门店'''
2 Series/DataFrame.apply(function,args,axis) 方法
2.1 介绍
- 作用原理和map方法类似,功能是遍历整个Series或DataFrame,对每个元素运行指定的函数
- 参数只能是函数,不能是字典
- map只能对Series使用,apply可以对Series使用,也可以对DataFrame使用
参数
参数 | 说明 |
---|---|
argscab | 接收元组,单个元素需加逗号隔开 |
axis | (1)axis=0,代表操作对列column进行 –> 也就是跨行操作 (2)axis=1,代表操作对行row进行 –> 也就是跨列操作 |
axis=0
实现过程
当沿着轴0(axis=0)进行操作时,会将各列(columns)默认以Series的形式作为参数,传入到指定的操作方法中,操作后返回相应结果
2.2 示例1
将销售收入、销售成本、销售毛利三列数据修改为以万元显示,保留四位小数,同时修改列名
沿行操作(跨行操作),需要指定
axis=0
# 将销售收入、销售成本、销售毛利三列数据修改为以万元显示,保留四位小数,同时修改列名
# 将三列改为以万元显示
data[['销售收入', '销售成本', '销售毛利']] = data[['销售收入', '销售成本', '销售毛利']].apply(lambda x: round(x/10000, 4), axis=0)
# 修改列名
data.rename(columns={'销售收入':'销售收入(万元)', '销售成本':'销售成本(万元)', '销售毛利':'销售毛利(万元)'}, inplace=True)
print(data.head())
''' 打印结果年 月 门店 销售收入(万元) 销售成本(万元) 销售毛利(万元) 门店级别
0 2025 Jan 门店01 47.8803 29.5586 18.3217 A类门店
1 2025 Jan 门店02 44.2108 33.0634 11.1475 A类门店
2 2025 Jan 门店03 36.7875 19.0555 17.7320 其他门店
3 2025 Jan 门店04 17.4357 13.8563 3.5794 其他门店
4 2025 Jan 门店05 18.2159 11.3868 6.8291 其他门店
'''
2.3 示例2
计算每笔数据的毛利率,将计算结果后添加“%",且储在【毛利率】列
# 计算每笔数据的毛利率,将计算结果后添加“%",且储在【毛利率】列
# 法1 -- 匿名函数
data['销售毛利率'] = data.apply(lambda row: f"{row['销售毛利(万元)'] / row['销售收入(万元)'] * 100:.2f}%",axis=1
)
print(data.head())# 法2 -- 非匿名函数
def fun(row):ans = (row['销售毛利(万元)'] / row['销售收入(万元)']) * 100return f"{ans:.2f}%"
data['销售毛利率'] = data.apply(fun, axis=1)
print(data.head())# 法3 -- 非匿名函数 --> 用这个方法的时候就不需要 * 100 了
def fun2(row):return format((row['销售毛利(万元)'] / row['销售收入(万元)']),'.2%')
data['销售毛利率'] = data.apply(fun2, axis=1)
print(data.head())
打印结果
# 计算每笔数据的毛利率,将计算结果后添加“%",且储在【毛利率】列
# 法1 -- 匿名函数
data['销售毛利率'] = data.apply(lambda row: f"{row['销售毛利(万元)'] / row['销售收入(万元)'] * 100:.2f}%",axis=1
)
print(data.head())# 法2 -- 非匿名函数
def fun(row):ans = (row['销售毛利(万元)'] / row['销售收入(万元)']) * 100return f"{ans:.2f}%"
data['销售毛利率'] = data.apply(fun, axis=1)
print(data.head())# 法3 -- 非匿名函数 --> 用这个方法的时候就不需要 * 100 了
def fun(row):return format((row['销售毛利(万元)'] / row['销售收入(万元)']),'.2%')
data['销售毛利率'] = data.apply(fun, axis=1)
print(data.head())
3 DataFrame.applymap(function)
3.1 介绍
- 用法比较简单,会对DataFrame中的每个元素执行指定方法的操作
- 只对 DataFrame操作,不对Series操作
但是这个函数在最新的Pandas中,已经被弃用,所以会出警告
这个警告信息表明,在较新版本的 Pandas 中,`DataFrame.applymap()` 方法已被弃用(deprecated),建议改用 `DataFrame.map()` 方法。不过需要注意的是,`map()` 主要用于 Series,而不是整个 DataFrame。
3.2 示例
将原始data中销售收入(万元),销售成本(万元)和销售毛利(万元)三列统一保留一位小数显示
代码
# 将原始data中销售收入(万元),销售成本(万元)和销售毛利(万元)三列统一保留一位小数显示
data[['销售收入(万元)', '销售成本(万元)', '销售毛利(万元)']] = data[['销售收入(万元)', '销售成本(万元)', '销售毛利(万元)']].applymap(lambda x: f'{float(x):.1f}')
print(data.head())''' 打印结果年 月 门店 销售收入(万元) 销售成本(万元) 销售毛利(万元) 门店级别 销售毛利率
0 2025 Jan 门店01 47.9 29.6 18.3 A类门店 38.27%
1 2025 Jan 门店02 44.2 33.1 11.1 A类门店 25.21%
2 2025 Jan 门店03 36.8 19.1 17.7 其他门店 48.20%
3 2025 Jan 门店04 17.4 13.9 3.6 其他门店 20.53%
4 2025 Jan 门店05 18.2 11.4 6.8 其他门店 37.49%
'''
十 pandas数据分组聚合
1 介绍
- 数据分析处理时,当我们收到一份成千上万行数据的源文件,一般需要对数据进行分类汇总,以更清晰的展现我们关注的数据并进行后续计算。
- 在Pandas中也有**“分类汇总”**的功能:分组、聚合。
- 所谓分组聚合,就是按一定的分类标准分组,然后对每一组进行聚合操作
举例说明
原数据表:
门店 | 月份 | 销售收入 | 销售成本 |
---|---|---|---|
门店 01 | 1 | 478803 | 295586 |
门店 02 | 1 | 442108 | 330633 |
门店 01 | 2 | 367875 | 190555 |
门店 02 | 2 | 174356 | 138562 |
门店 03 | 3 | 182158 | 113897 |
按门店分组后:
-
数据表1
门店 月份 销售收入 销售成本 门店 01 1 478803 295586 门店 01 2 367875 190555 -
数据表2
门店 月份 销售收入 销售成本 门店 02 1 442108 330633 门店 02 2 174356 138562 -
数据表3
门店 月份 销售收入 销售成本 门店 03 3 182158 113897
2 分组 – DataFrame.groupby() 函数
2.1 介绍
根据一列或多列将数据分成若干组。
2.2 查看分组结果的方法
DataFrame.groupby()
方法返回DataFrameGroupBy
类型数据,需调用以下方法查看结果:
group.size()
- 查看分组后每组的数量group.groups
- 查看分组情况group.get_group('组名')
- 根据分组名称选择特定分组数据
2.3 函数语法
by指定的是分组键或叫分组依据
DataFrame.groupby(by=None, # 【常用】分组依据的列名或列名列表axis=0, # 【常用】分组轴:0=按行分组(默认),1=按列分组 但新版已经准备删除此参数level=None, # 用于分组的层级索引级别as_index=True, # 【常用】是否将分组键作为结果索引sort=True, # 【常用】是否对分组键进行排序group_keys=True, # 调用apply时是否添加分组键squeeze=False, # 是否尝试减少返回维度observed=False, # 是否仅使用分类分组的观察值dropna=True, # 【新增】是否排除NA分组(新版本重要参数)**kwargs # 其他关键字参数
)
参数 | 是什么 | 示例 |
---|---|---|
by | 分类标准(分组键) | groupby("班级") |
axis (未来会移除) | 方向选择 | axis=0 (竖着分组)/axis=1 (横着分组)默认是行 |
sort | 是否排序 | sort=True (默认排序)/False (不排序) |
as_index | 是否用分组当索引 | as_index=True (默认)/False (当普通列) |
- 注意:axis 在Pandas 1.5 中已经被标记为果实,未来版本将会彻底移除
- 按行分组是唯一支持模式,无需显性指示
- 这个设计冗余,也无需再用
2.4 示例
将表中的数据按照月分组,分析每月总销售额,并且将英文月变为数字月
# 使用map函数将DataFrame中的"月"列从英文缩写转换为数字格式
data['月'] = data['月'].map(month_dict)
# 按照月分组,并且对每组进行求和(每月求和)
# 参数1: 「分组键」为 '月', 参数2: 用 「分组键」 当做索引
data2 = data.groupby('月', as_index=True).sum()
print(data2)
''' 打印结果年 ... 销售毛利
月 ...
01 81000 ... 4282135.08
02 81000 ... 4238531.48
03 81000 ... 4194927.88
04 81000 ... 4345023.08
05 81000 ... 4301419.48
06 81000 ... 4257815.88
07 81000 ... 4407911.08
08 81000 ... 4364307.48
09 81000 ... 4320703.88
10 81000 ... 4470799.08
11 81000 ... 4427195.48
12 81000 ... 4383591.88
'''
3 聚合 – DataFrame.agg() 函数
3.1 介绍
支持根据分组按多种方式进行聚合,可以针对一列或多列选择相同或不同的聚合方式
3.2 函数语法
# DataFrame.agg() 函数参数详解```python
DataFrame.agg(func=None, # 【核心】聚合函数,支持字符串/函数/列表/字典axis=0, # 【常用】聚合轴向:0=按列聚合(默认),1=按行聚合*args, # 传递给聚合函数的位置参数**kwargs # 传递给聚合函数的关键字参数
)
而参数func的内容可以是如下的方法
列和方法都可以写多个
方法 | 描述 |
---|---|
count | 计算分组中的非NA值的数量 |
sum | 计算非NA值的和 |
mean | 计算非NA值的平均值 |
median | 计算非NA值的算数中位数 |
std, var | 计算非NA值的标准差和方差 |
min, max | 获得非NA值的最小和最大值 |
prod | 计算非NA值的积 |
first, last | 获得第一个和最后一个非NA值 |
3.3 怎么用这个函数
- 单独使用
# 使用单个方法
df.groupby('类别').agg('mean') # 计算非NA平均值
- 组合使用
# 同时使用多个方法
df.groupby('类别').agg(['sum', 'median']) # 计算非NA总和和中位数
- 按列定制
# 不同列使用不同方法
df.groupby('类别').agg({'销量': ['count', 'sum'], # 非NA计数+求和'单价': ['mean', 'max'] # 非NA平均+最大值
})
3.4 示例1
将数据根据年、月列聚合后,计算销售收入列的总和、平均值,销售成本列的总和、平均值
import pandas as pd
import numpy as npdata = pd.read_excel('门店销售数据(原表).xlsx', sheet_name=0)
# 创建一个英文月份和数字月份对照的字典,将英文月份转换为数字字符串
month_dict = {'Jan': '01', 'Feb': '02', 'Mar': '03','Apr': '04', 'May': '05', 'Jun': '06','Jul': '07', 'Aug': '08', 'Sep': '09','Oct': '10', 'Nov': '11', 'Dec': '12'
}# 使用map函数将DataFrame中的"月"列从英文缩写转换为数字格式
data['月'] = data['月'].map(month_dict)
data3 = data.groupby(['年', '月']).agg({'销售收入':['sum','mean'], '销售成本':['sum','mean']})
print(data3)''' 打印结果销售收入 销售成本sum mean sum mean
年 月
2025 01 11985859.71 299646.49275 7703724.63 192593.1157502 11861774.91 296544.37275 7623243.43 190581.0857503 11737690.11 293442.25275 7542762.23 188569.0557504 12073592.11 301839.80275 7728569.03 193214.2257505 11949507.31 298737.68275 7648087.83 191202.1957506 11825422.51 295635.56275 7567606.63 189190.1657507 12161324.51 304033.11275 7753413.43 193835.3357508 12037239.71 300930.99275 7672932.23 191823.3057509 11913154.91 297828.87275 7592451.03 189811.2757510 12249056.91 306226.42275 7778257.83 194456.4457511 12124972.11 303124.30275 7697776.63 192444.4157512 12000887.31 300022.18275 7617295.43 190432.38575
'''
十一 pandas 数据透视表与轴向转换
1 基本介绍
1.1 数据透视表
首先,现先简单介绍一下什么叫做数据透视表,主要使用pivot_table()
方法
简单说就是**“自动分类统计工具”**,可以快速:
- 按类别分组(比如按地区、月份)
- 计算统计值(比如求和、平均值)
- 重新排列数据(行变列,列变行)
优点
- 比手工筛选快100倍
- 不用写复杂公式
- Excel和Python都能用
1.2 轴向转换
在 Pandas 中,轴向转换 指的是行变列、列变行来改变数据的展示形式,主要涉及 stack()
和 unstack()
这两个核心方法
本质
就像玩**“俄罗斯方块”**,把数据的行和列方向互相转换:
stack()
(堆叠):将列(columns)压缩成行(index),表格变窄变长
–> 适合将多列数据合并为多行(宽表 –> 长表)unstack()
(解堆叠):将行(index)展开成列(columns),表格变宽变短
–> 适合将多行数据展开为多列(长表 –> 宽表)
2 DataFrame.pivot_table()
2.1 介绍
- 似于Excel里的数据透视表功能,只要指定行、列、值和值的计算类型(计数、合计、平均等),就可以做出一个数据透视表来
- 当分析庞大的数据时,为了更好的发掘数据特征之间的关系,且不破坏原数据,就可以利用透视表进行操作
2.2 函数语法
DataFrame.pivot_table(values=None, # 【核心】要聚合的列(可选,不指定时默认聚合所有数值列) --> 就是要显示数值的列index=None, # 【常用】行分组键(单个列名或列名列表)columns=None, # 【常用】列分组键(单个列名或列名列表)aggfunc='mean', # 【核心】聚合函数,支持字符串/函数/列表/字典(默认求平均值)fill_value=None, # 替换结果中的缺失值(如:0 或 'N/A')margins=False, # 是否添加总计行/列(默认False)margins_name='All', # 总计行列的标签名(当margins=True时生效)dropna=True, # 是否排除包含NaN的分组(默认True)observed=False, # 是否只显示分类分组中存在的组合(针对分类数据)sort=True # 是否对结果排序(默认True)
)
参数 | 解释 | 实例 |
---|---|---|
index | 数据透视表的行 | index=['列1'] |
columns | 数据透视表的列 | columns=['列2'] |
values | 数据透视表的值,默认是显示所有的值 | values=['列3','列4'] |
aggfunc | 值计算方式,默认是’mean’平均值 | 还可以是'sum','len' |
fill_value | 替换表中的NaN | 比如用0填充NaN,fill_value=0 |
margins | 是否添加行列的总计 | margins=True是汇总 |
margins_name | 汇总栏命名,默认为’All’ | margins_name=‘名字’ |
注意
pivot_table()
本质是groupby()+agg()
的快捷方式- 支持同时指定行/列分组(二维聚合)
2.3 示例
- 将月列中的英文列换成数字列
- 以月为行,年为列,统计销售收入的合计,和销售成本的平均数
import numpy as np
import pandas as pd# - 将月列中的英文列换成数字列
# - 以月为行,年为列,统计销售收入的合计,和销售成本的平均数data = pd.read_excel('门店销售数据(原表).xlsx', sheet_name=0)
# 将月变成数字月
month_dict = {'Jan': '01', 'Feb': '02', 'Mar': '03','Apr': '04', 'May': '05', 'Jun': '06','Jul': '07', 'Aug': '08', 'Sep': '09','Oct': '10', 'Nov': '11', 'Dec': '12'
}
# 使用map函数将DataFrame中的"月"列从英文缩写转换为数字格式
data['月'] = data['月'].map(month_dict)
# 以月为行,年为列,统计销售收入的合计,和销售毛利的平均数
data2 = data.pivot_table(values=['销售收入', '销售成本'], # 显示的列index=['月'], # 数据透视表的行columns=['年'], # 数据透视表的列aggfunc={'销售收入':'sum','销售成本':'mean'},fill_value=0, # 将 NaN 替换为数值 0margins=True, # 添加总计行/列margins_name='统计'
)
print(data2)''' 打印结果销售成本 销售收入
年 2025 统计 2025 统计
月
01 192593.11575 192593.11575 1.198586e+07 1.198586e+07
02 190581.08575 190581.08575 1.186177e+07 1.186177e+07
03 188569.05575 188569.05575 1.173769e+07 1.173769e+07
04 193214.22575 193214.22575 1.207359e+07 1.207359e+07
05 191202.19575 191202.19575 1.194951e+07 1.194951e+07
06 189190.16575 189190.16575 1.182542e+07 1.182542e+07
07 193835.33575 193835.33575 1.216132e+07 1.216132e+07
08 191823.30575 191823.30575 1.203724e+07 1.203724e+07
09 189811.27575 189811.27575 1.191315e+07 1.191315e+07
10 194456.44575 194456.44575 1.224906e+07 1.224906e+07
11 192444.41575 192444.41575 1.212497e+07 1.212497e+07
12 190432.38575 190432.38575 1.200089e+07 1.200089e+07
统计 191512.75075 191512.75075 1.439205e+08 1.439205e+08
'''
3 DataFrame.stack 与 DataFrame.unstack
3.1 介绍
数据行列索引相互转换(类似于Excel中的行列转置),包括**stack()
方法和unstack()
方法**,灵活运用轴向转换功能可轻松实现数据重塑
3.2 函数语法
DataFrame.stack(level=-1, # 【可选】要堆叠的层级(默认最后一层),支持int/str/列表dropna=True # 【可选】丢弃堆叠后含 NaN 的行(默认True)
)DataFrame.unstack(level=-1, # 【可选】要展开的层级(默认最后一层),支持int/str/列表fill_value=None # 【可选】替换NaN的内容
)
DataFrame.stack | 解释 | 实例 |
---|---|---|
level | 要堆叠的层级(默认最后一层) | 默认level=1 ,即最内测索引level=0 (堆叠最外层)level=['日期', '城市'] (堆叠指定层级) |
dropna | 是否丢弃堆叠后含 NaN 的行 | True (默认):丢弃False :保留 |
ame.unstack | 解释 | 实例 |
---|---|---|
level | 要展开的层级(默认最后一层),支持: - 整数(如 1 , -1 )- 字符串(列名) - 列表(多层级) | level='城市' (展开命名层级)level=[0, 1] (展开前两个层级) |
fill_value | 替换展开后生成的 NaN 的值:- None (默认):不替换- 标量值(如 0 , '缺失' ) | fill_value=0 (用 0 填充 NaN )fill_value='无' |
3.3 两者区别
两种方法 | 作用方向 | 典型场景示例 |
---|---|---|
stack() | 列 –> 行 | 将多列(如月度数据)合并为一列,生成多级索引:df.stack().reset_index() |
unstack() | 行 –> 列 | 将多级索引的某一层展开为列:df.unstack(level='城市') |
unstack()
函数是stack()
函数的逆操作
3.4 示例
- 以月列为行分组,统计每月销售收入、销售成本的总和
# 以月列为行分组,统计每月销售收入、销售成本的总和
data = pd.read_excel('门店销售数据(原表).xlsx', sheet_name=0)
data2 = data.groupby(by='月', as_index=True).sum()[['销售收入', '销售成本']]
print(data2)''' 打印结果销售收入 销售成本
月
Apr 12073592.11 7728569.03
Aug 12037239.71 7672932.23
Dec 12000887.31 7617295.43
Feb 11861774.91 7623243.43
Jan 11985859.71 7703724.63
Jul 12161324.51 7753413.43
Jun 11825422.51 7567606.63
Mar 11737690.11 7542762.23
May 11949507.31 7648087.83
Nov 12124972.11 7697776.63
Oct 12249056.91 7778257.83
Sep 11913154.91 7592451.03
'''
- 将销售收入、销售成本索引转换到行索引 –> 即列转行
# 将销售收入、销售成本索引转换到行索引 –> 即列转行
data2 = data2.stack()
print(data2)''' 打印结果
月
Apr 销售收入 12073592.11销售成本 7728569.03
Aug 销售收入 12037239.71销售成本 7672932.23
Dec 销售收入 12000887.31销售成本 7617295.43
Feb 销售收入 11861774.91销售成本 7623243.43
Jan 销售收入 11985859.71销售成本 7703724.63
Jul 销售收入 12161324.51销售成本 7753413.43
Jun 销售收入 11825422.51销售成本 7567606.63
Mar 销售收入 11737690.11销售成本 7542762.23
May 销售收入 11949507.31销售成本 7648087.83
Nov 销售收入 12124972.11销售成本 7697776.63
Oct 销售收入 12249056.91销售成本 7778257.83
Sep 销售收入 11913154.91销售成本 7592451.03
dtype: float64
'''
- 然后将刚才的表中的月行索引,转换到列索引中去 –> 即行转列
# 然后将刚才的表中的月行索引,转换到列索引中去 –> 即行转列
data2 = data2.unstack(level='月')
print(data2)''' 打印结果
月 Apr Aug ... Oct Sep
销售收入 12073592.11 12037239.71 ... 12249056.91 11913154.91
销售成本 7728569.03 7672932.23 ... 7778257.83 7592451.03[2 rows x 12 columns]
'''