Python数据分析(二)--pandas
Pandas 是 Python 数据分析工具链中最核心的库,充当数据读取、清洗、分析、统计、输出的高效工具。Pandas 是一个开源的数据分析和数据处理库,它是基于 Python 编程语言的。Pandas 提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据(类似于Excel表格)。Pandas 是数据科学和分析领域中常用的工具之一,它使得用户能够轻松地从各种数据源中导入
文章目录
Pandas 数据分析
视频来源B站:Python数据分析教程,零基础学会numpy+pandas+matplotlib数据分析,数据可视化
基本介绍
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)灵活的数据处理功能。它提供了复杂精细的索引功能,能更加便捷地完成重塑、切片和切块、聚合以及选取数据子集等操作。
功能
有标签轴的数据结构。在数据结构中,每个轴都被赋予了特定的标签,这些标签用于标识和引用轴上的数据元素,使得数据的组织、访问和操作更加直观和方便
应用
| 工具 | 功能特色 | 适用场景 |
|---|---|---|
| Excel | 图形界面,简单上手 | 人工分析、小规模数据 |
| SQL | 高效读写,最终数据源 | 数据库查询和联表 |
| Python + Pandas | 算法和分析部署核心 | 数据清洗,统计分析,可视化等 |
与excel对比
- 优势:
- 处理百万级数据不卡顿(Excel约100万行限制)
- 可复用的分析流程(脚本 vs 手工操作)
- 支持复杂数据转换(如:分组聚合、时间重采样)
- 局限:
- 可视化交互性较弱
- 学习曲线较陡峭
与数据库对比
- 优势:
- 无需SQL知识即可分析
- 适合探索性分析(即时反馈)
- 丰富的数据清洗函数
- 局限:
- 数据量受内存限制
- 不适合高并发访问
处理流程图

核心数据结构
Series
一维带有标签的数组
什么是 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):
import numpy as np
# series 的创建
import pandas as pd
s = pd.Series([1,2,3,4,5])
print(s)
# 结果
0 1
1 2
2 3
3 4
4 5
dtype: int64
创建 Series
直接通过列表创建Series
import numpy as np
# series 的创建
import pandas as pd
s = pd.Series([1,2,3,4,5])
print(s)
# 结果
0 1
1 2
2 3
3 4
4 5
dtype: int64
Series的字符串表现形式为:索引在左边,值在右边。由于我们没有为数据指定索引,于是会自动创建一个 0 到 N-1(N为数据的长度)的整数型索引。
创建方式:
- 通过列表创建 Series 时指定索引
# 自定义索引
s = pd.Series([1,2,3,4,5], index=['a','b','c','d','e'])
print(s)
#结果
a 1
b 2
c 3
d 4
e 5
dtype: int64
- 通过列表创建 Series 时指定索引和名称
# 定义name
s = pd.Series([1,2,3,4,5], index=['a','b','c','d', 'e'], name='shuzi')
print(s)
# 结果
a 1
b 2
c 3
d 4
e 5
Name: shuzi, dtype: int64
- 通过字段方式创建
#通过字段方式创建
s = pd.Series({'a':1,'b':2,'c':3,'d':4,'e':5})
print(s)
print('--------------------')
s = pd.Series(s, index=['a','c'])
print(s)
#结果
a 1
b 2
c 3
d 4
e 5
dtype: int64
--------------------
a 1
c 3
dtype: int64
名称的作用,与变量名的区别
在 Pandas 的 Series 中,name 参数用于给整个 Series 对象赋予一个名称。这个名称有以下几个用途:
- 标识作用:name 可以作为 Series 的标识,类似于给数据列取一个名字。当你打印 Series 时,name 会显示在输出的最下方。
- DataFrame 列名:如果你将一个 Series 转换成 DataFrame 或与其他 DataFrame 合并,name 会自动成为列名。
- 例如:
import pandas as pd
s = pd.Series([4, 7, -5, 3], index=["a", "b", "c", "d"],name="hello_python")
df = s.to_frame() # 转换为 DataFrame,列名就是 "hello_python"
print(df)
# hello_python
# a 4
# b 7
# c -5
# d 3
- 对齐操作:在 Pandas 运算(如 concat、merge 等)时,name 可以帮助对齐数据。
- 导出数据:将 Series 导出为 CSV 或其他格式时,name 会成为列名。
name 的主要作用是 给 Series 一个标识,方便后续数据处理、合并或导出。如果只是单独使用 Series,name 可能看起来作用不大,但在更复杂的数据操作中(如 DataFrame 整合),它会很有用。
访问数据
以下是 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]。
import pandas as pd
s = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
# 位置索引
print(s.iloc[0]) # 10
# 标签访问
print(s["a"])
# 标签索引
print(s.loc['b']) # 20
# 布尔索引
print(s[s > 25]) # c:30, d:40
# 花式索引
print(s[['a', 'c']]) # a:10, c:30
#使用布尔索引从Series中筛选满足某些条件的值
print("mean:" + str(s.mean())) # 25.0
bools = s > s.mean()
# 将大于平均值的元素标记为 True
print(bools)
# a False
# b False
# c True
# d True
# dtype: bool
print(s[bools])# b 3.5
# c 6.8
# dtype: float64# 使用where过滤
print(s.where(s > 20, -1)) # 小于等于20的值替换为-1
常用属性
| 属性 | 说明 |
|---|---|
| index | Series的索引对象 |
| values | Series的值 |
| dtype或dtypes | Series的元素类型 |
| shape | Series的形状 |
| ndim | Series的维度 |
| size | Series的元素个数 |
| name | Series的名称 |
| loc[] | 显式索引,按标签索引或切片 |
| iloc[] | 隐式索引,按位置索引或切片 |
| at[] | 使用标签访问单个元素 |
| iat[] | 使用位置访问单个元素 |
# series 的属性
"""
index
values
shape
ndim
size
name
loc[] 支持切片
iloc[]
at[] 不支持切片
iat[]
"""
s = pd.Series({'a':1,'b':2,'c':3,'d':4,'e':5})
print(s)
print(s.index)
print(s.values)
print(s.shape)
print(s.ndim)
print(s.dtype)
print(s.size)
print('--------------------')
print(s.loc['a'])
print(s.iloc[1])
print('-----------------')
print(s.loc['a':'c'])
print(s.iloc[1:3])
print('-------------------')
print(s.at['a'])
print(s.iat[3])
运算
import pandas as pd
s1 = pd.Series([1, 2, 3, 4])
s2 = pd.Series([10, 20, 30, 40])
# 基本运算
print(s1 + s2) # 对应位置相加
print(s1 * 2) # 标量乘法
常用方法与统计
| 用途分类 | 方法 | 说明 | 示例代码 |
|---|---|---|---|
| 数据预览 | 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) |
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))
DataFrame
二维表格结构,可看作多个 Series 的组合
创建
DataFrame 是 Pandas 中的核心数据结构之一,多行多列表格数据,类似于 Excel 表格 或 SQL 查询结果。
它是一个 二维表格结构,具有行索引(index)和列标签(columns)。
df = pd.DataFrame({
"name": ["Alice", "Bob"],
"score": [90, 80]
})

DataFrame中的数据是以一个或多个二维块存放的(而不是列表、字典或别的一维数据结构)。它可以被看做由Series组成的字典(共同用一个索引)。提供了各种功能来进行数据访问、筛选、分割、合并、重塑、聚合以及转换等操作,广泛用于数据分析、清洗、转换、可视化等任务。
- 通过series来创建
# dataframe学习
import pandas as pd
import numpy as np
#通过Series创建
s1 = pd.Series([1,2,3,4,5])
s2 = pd.Series([6,7,8,9,10])
df = pd.DataFrame({"第一列": s1, "第二列": s2})
df
type(df)
type(df["第一列"])
- 直接通过字典创建DataFrame:
#通过字典创建
df = pd.DataFrame({
"name": ["A","B","C","D","E"],
"age": [12,23,34,45,56]
}, index=[1,2,3,4,5], columns=["age","name"])
df
- 通过字典创建时指定列的顺序和行索引
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
访问方式
| 方法分类 | 语法示例 | 描述 | 返回值类型 | 是否支持切片/条件索引 |
|---|---|---|---|---|
| 列选择 | 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)。
- 布尔条件筛选
- 支持组合条件(需用&、|,并用括号分隔条件):
df[(df['col1'] > 3) & (df['col2'] == 'A')]
- at/iat vs loc/iloc
- at/iat:仅用于访问单个值,速度更快。
- loc/iloc:支持多行/列选择,功能更灵活
代码演示:
- 获取一列数据
# 访问数据
print(df['name']) #访问某列数据
print(df.score)
# df["col"] / df.col
df["name"] # 返回 Series
df.name
df[["name"]] # 返回 DataFrame
- 获取多列数据
df[["date", "temp_max", "temp_min"]] # 获取多列数据print(df[['name','score']]) # 访问多列数据
- 获取行数据
- loc:通过行标签获取数据
df.loc[1] # 获取行标签为1的数据
df.loc[[1, 10, 100]] # 获取行标签分别为1、10、100的数据
- iloc:通过行位置获取数据
df.iloc[0] # 获取行位置为0的数据
df.iloc[-1] # 获取行位置为最后一位的数据
- 获取指定单元格
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"]] # 通过行列标签获取数据
- 查看部分数据
print(df.head())print(df.tail(10))
- 使用布尔索引筛选数据
# 条件筛选
df['score']>70
print(df[df.score>70])
print(df[(df['score']>70) & (df['age']<20)])# 随机抽样
df.sample(2)
常用属性
| 属性 | 说明 |
|---|---|
| index | DataFrame的行索引 |
| columns | DataFrame的列标签 |
| values | DataFrame的值 |
| ndim | DataFrame的维度 |
| shape | DataFrame的形状 |
| size | DataFrame的元素个数 |
| dtypes | DataFrame的元素类型 |
| T | 行列转置 |
| loc[] | 显式索引,按行列标签索引或切片 |
| iloc[] | 隐式索引,按行列位置索引或切片 |
| at[] | 使用行列标签访问单个元素 |
| iat[] | 使用行列位置访问单个元素 |
代码演示:
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])
常用方法与统计
| 方法 | 说明 |
|---|---|
| head() | 查看前n行数据,默认5行 |
| tail() | 查看后n行数据,默认5行 |
| isin() | 元素是否包含在参数集合中 |
| isna() | 元素是否为缺失值 |
| sum() | 求和 |
| mean() | 平均值 |
| min() | 最小值 |
| max() | 最大值 |
| var() | 方差 |
| std() | 标准差 |
| median() | 中位数 |
| mode() | 众数 |
| quantile() | 指定位置的分位数,如quantile(0.5) |
| describe() | 常见统计信息 |
| info() | 基本信息 |
| value_counts() | 每个元素的个数 |
| count() | 非空元素的个数 |
| drop_duplicates() | 去重 |
| sample() | 随机采样 |
| replace() | 用指定值代替原有值 |
| equals() | 判断两个DataFrame是否相同 |
| cummax() | 累计最大值 |
| cummin() | 累计最小值 |
| cumsum() | 累计和 |
| cumprod() | 累计积 |
| diff() | 一阶差分,对序列中的元素进行差分运算,也就是用当前元素减去前一个元素得到差值,默认情况下,它会计算一阶差分,即相邻元素之间的差值。参数:periods:整数,默认为 1。表示要向前或向后移动的周期数,用于计算差值。正数表示向前移动,负数表示向后移动。axis:指定计算的轴方向。0 或 ‘index’ 表示按列计算,1 或 ‘columns’ 表示按行计算,默认值为 0。 |
| sort_index() | 按行索引排序 |
| sort_values() | 按某列的值排序,可传入列表来按多列排序,并通过ascending参数设置升序或降序 |
| nlargest() | 返回某列最大的n条数据 |
| nsmallest() | 返回某列最小的n条数据 |
import pandas as pd
from typing import Any, Optional
def create_dataframe() -> pd.DataFrame:
"""
创建一个包含 id、name 和 age 列的 DataFrame。
Returns:
pd.DataFrame: 包含示例数据的 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"]
return pd.DataFrame(data, index=index)
def print_statistics(df: pd.DataFrame) -> None:
"""
打印 DataFrame 的基本统计信息。
Args:
df (pd.DataFrame): 输入的 DataFrame。
"""
print("=== 前几行数据 ===")
print(df.head())
print("\n=== 后几行数据 ===")
print(df.tail())
print("\n=== 检查元素是否存在于指定集合中 ===")
print(df.isin([103, 106]))
print("\n=== 检查缺失值 ===")
print(df.isna())
print("\n=== 年龄列的总和 ===")
print(df["age"].sum())
print("\n=== 年龄列的平均值 ===")
print(df["age"].mean())
print("\n=== 年龄列的最小值 ===")
print(df["age"].min())
print("\n=== 年龄列的最大值 ===")
print(df["age"].max())
print("\n=== 年龄列的方差 ===")
print(df["age"].var())
print("\n=== 年龄列的标准差 ===")
print(df["age"].std())
print("\n=== 年龄列的中位数 ===")
print(df["age"].median())
print("\n=== 年龄列的众数 ===")
print(df["age"].mode())
print("\n=== 年龄列的中位数(分位数 0.5) ===")
print(df["age"].quantile(0.5))
print("\n=== 数据的基本统计信息 ===")
print(df.describe())
print("\n=== 数据基本信息 ===")
print(df.info())
print("\n=== 每个元素的出现次数 ===")
print(df.value_counts())
print("\n=== 非空元素的个数 ===")
print(df.count())
print("\n=== 是否有重复行(按 age 列)===\n")
print(df.duplicated(subset="age"))
print("\n=== 随机采样一行 ===")
print(df.sample())
print("\n=== 替换特定值 ===")
print(df.replace(20, "haha"))
print("\n=== 累计最大值(按行)===\n")
df3 = pd.DataFrame({'A': [2, 5, 3, 7, 4], 'B': [1, 6, 2, 8, 3]})
print(df3.cummax(axis="columns"))
print("\n=== 累计最小值 ===")
print(df3.cummin())
print("\n=== 累计和 ===")
print(df3.cumsum())
print("\n=== 累计积 ===")
print(df3.cumprod())
print("\n=== 一阶差分 ===")
print(df3.diff())
print("\n=== 按行索引排序 ===")
print(df.sort_index())
print("\n=== 按年龄列排序 ===")
print(df.sort_values(by="age"))
print("\n=== 年龄最大的两行 ===")
print(df.nlargest(n=2, columns="age"))
print("\n=== 年龄最小的一行 ===")
print(df.nsmallest(n=1, columns="age"))
if __name__ == "__main__":
dataframe = create_dataframe()
print_statistics(dataframe)
在Pandas的 DataFrame 方法里,axis 是一个非常重要的参数,它用于指定操作的方向。
axis 参数可以取两个主要的值,即 0 或 ‘index’,以及 1 或 ‘columns’ ,其含义如下:
- axis=0 或 axis=‘index’:表示操作沿着行的方向进行,也就是对每一列的数据进行处理。例如,当计算每列的均值时,就是对每列中的所有行数据进行计算。
- axis=1 或 axis=‘columns’:表示操作沿着列的方向进行,也就是对每行的数据进行处理。例如,当计算每行的总和时,就是对每行中的所有列数据进行计算。
标量运算
标量与每个元素进行计算。
import pandas as pd
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
数据的导入与导出
导出数据
| 方法 | 说明 |
|---|---|
| to_csv() | 将数据保存为csv格式文件,数据之间以逗号分隔,可通过sep参数设置使用其他分隔符,可通过index参数设置是否保存行标签,可通过header参数设置是否保存列标签。 |
| to_pickle() | 如要保存的对象是计算的中间结果,或者保存的对象以后会在Python中复用,可把对象保存为.pickle文件。如果保存成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是一种文件格式,用于存储二进制对象。feather对象也可以加载到R语言中使用。feather格式的主要优点是在Python和R语言之间的读写速度要比csv文件快。feather数据格式通常只用中间数据格式,用于Python和R之间传递数据,一般不用做保存最终数据。需安装pyarrow包。 |
| to_sql() | 保存到数据库。 |
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() | 加载数据库中的数据。 |
import os
import pandas as pd
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)
数据清洗与预处理
缺失值处理
| 方法/操作 | 语法示例 | 描述 |
|---|---|---|
| 检测缺失值 | 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)表示缺失数据,使用 NA(Not Available)表示缺失值。可以通过isnull()、isna()或notnull()、notna()方法判断某个值是否为缺失值。
Nan 通常表示一个无效的或未定义的数字值,是浮点数的一种特殊取值,用于表示那些不能表示为正常数字的情况,如 0/0、∞-∞ 等数学运算的结果。nan 与任何值(包括它自身)进行比较的结果都为False。例如在 Python 中,nan == nan返回 False。
NA 一般用于表示数据不可用或缺失的情况,它的含义更侧重于数据在某种上下文中是缺失或不存在的,不一定特指数字类型的缺失。
na 和 nan 都用于表示缺失值,但 nan 更强调是数值计算中的特殊值,而na更强调数据的可用性或存在性。
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
加载数据中包含缺失值。
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参数设置是否将空白值设置为缺失值。
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参数将指定值设置为缺失值。
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()查看缺失值数量
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 剔除缺失值:
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 剔除缺失值:
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
无法从DataFrame中单独剔除一个值,只能剔除缺失值所在的整行或整列。默认情况下,dropna()会剔除任何包含缺失值的整行数据。
可以设置按不同的坐标轴剔除缺失值,比如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 参数指定,常见的方法有:
import pandas as pd
import numpy as np
# 创建包含缺失值的 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
- ‘linear’:线性插值,基于两点之间的直线来估算缺失值,适用于数据呈线性变化的情况。
- ‘time’:适用于时间序列数据,会考虑时间间隔进行插值。
- ‘polynomial’:多项式插值,通过拟合多项式曲线来估算缺失值,可通过 order 参数指定多项式的阶数
# 缺失值
import numpy as np
import pandas as pd
# 缺失值的类型 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.duplicated() | 返回布尔序列标记重复行(首次出现的行标记为False) |
| 删除重复行 | df.drop_duplicates() | 保留首次出现的行(默认检查所有列) |
| df.drop_duplicates(subset=[‘col1’]) | 仅根据指定列去重 | |
| df.drop_duplicates(keep=‘last’) | 保留最后一次出现的行 |
检测重复行
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())
# 0 False
# 1 False
# 2 True
# 3 False
# 4 True
# dtype: bool
删除重复行
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)
# 默认保留首次出现的行
df_unique = df.drop_duplicates()
print("去重后的DataFrame:")
print(df_unique)
按指定列去重
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)
# 仅根据'Name'列去重(保留首次出现)
df_name_unique = df.drop_duplicates(subset=['Name'])
print("按Name列去重:")
print(df_name_unique)
# Name Age City
# 0 Alice 25 NY
# 1 Bob 30 LA
# 3 Charlie 35 SF
保留最后一次出现的重复行
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_last = df.drop_duplicates(keep='last')
print("保留最后一次出现的行:")
print(df_last)
# Name Age City
# 2 Alice 25 NY
# 4 Bob 30 LA
# 3 Charlie 35 SF
数据类型转换
| 方法/操作 | 语法示例 | 描述 |
|---|---|---|
| 查看数据类型 | 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位)。 |
- 查看数据类型
import pandas as pd
# 加载数据(以sleep.csv为例)
df = pd.read_csv("sleep.csv")
print(df.dtypes)
# person_id int64
# gender object
# age int64
# occupation object
# sleep_duration float64
# sleep_quality float64
# ... ...
说明:object通常为字符串或混合类型,需检查是否需要转换
- 强制类型转换:将数值列转换为整数或字符串
# 将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())
# sleep_duration sleep_duration_int gender_str
# 0 7.4 7 Male
# 1 4.2 4 Female
# 2 6.1 6 Male
- 转换为日期时间:处理时间数据(假设employees.csv有日期列):
# 示例:创建临时日期列(实际数据可能为hire_date)
df_employees = pd.read_csv("employees.csv")
df_employees['fake_date'] = '2023-01-' + df_employees['employee_id'].astype(str).str[:2]
# 转换为datetime
df_employees['fake_date'] = pd.to_datetime(df_employees['fake_date'])
print(df_employees[['employee_id', 'fake_date']].head())
# employee_id fake_date
# 0 100 2023-01-10
# 1 101 2023-01-10
# 2 102 2023-01-10
注意:若原始格式非标准,需指定格式参数,如:pd.to_datetime(df[‘date’], format=‘%Y/%m/%d’)
- 转换为分类数据:优化内存和性能(适用于低基数列)
# 将gender列转为分类类型
df['gender'] = df['gender'].astype('category')
print(df['gender'].dtypes)
# category
- 优势:
- 减少内存占用(尤其对重复值多的列)。
- 加速groupby、sort等操作
- 数值格式化:控制小数位数:
# 保留sleep_quality的2位小数
df['sleep_quality_rounded'] = df['sleep_quality'].round(2)
print(df[['sleep_quality', 'sleep_quality_rounded']].head())
# sleep_quality sleep_quality_rounded
# 0 7.0 7.00
# 1 4.9 4.90
# 2 6.0 6.00
常见问题与技巧
- 处理转换错误:使用errors='coerce’将无效值转为NaN,避免报错
df['age'] = pd.to_numeric(df['age'], errors='coerce')
- 内存优化:将数值列从int64转为int32或float32:
df['age'] = df['age'].astype('int32')
- 布尔类型转换:将字符串(如"Yes"/“No”)转为布尔值:
df['is_active'] = df['active_flag'].map({'Yes': True, 'No': False})
- 自定义格式化:使用apply实现复杂转换(如百分比):
df['score_percent'] = df['score'].apply(lambda x: f"{x*100:.1f}%")
数据重塑与变形
| 方法/操作 | 语法示例 | 描述 |
|---|---|---|
| 行列转置 | 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) | 按分隔符拆分字符串为多列 |
注意事项
- pivot 与 pivot_table 的区别:
- pivot 要求索引和列的组合唯一,否则报错。
- pivot_table 支持聚合(如均值、求和),适合非唯一组合。
- 分列操作:
- 使用 expand=True 将拆分结果转为多列。
- 若分隔符数量不一致,需预处理数据(如填充缺失值)。
- 内存管理:
- 宽表转长表可能增加行数,需注意内存占用
#数据变形
import pandas as pd
data = {
'ID': [1, 2],
'name':['alice','bob'],
'Math': [90, 85],
'English': [88, 92],
'Science': [95, 89]
}
df = pd.DataFrame(data)
df
df.T
#宽表转长表
df2= pd.melt(df, id_vars=['ID','name'], var_name='科目', value_name='分数')
df2.sort_values(by=['name','科目'])
#长表转宽表
df3=pd.pivot(df2,index=['ID','name'],columns=['科目'],values='分数')
#分列
data = {
'ID': [1, 2],
'name':['alice smith','bob jack'],
'Math': [90, 85],
'English': [88, 92],
'Science': [95, 89]
}
df = pd.DataFrame(data)
df[['first name','last name']] = df['name'].str.split(' ',expand=True)
# 加载数据
df = pd.read_csv("data/sleep.csv")
df=df[['person_id','blood_pressure']]
df[['high','low']]=df['blood_pressure'].str.split('/',expand=True)
df
文本数据处理
| 方法/操作 | 语法示例 | 描述 |
|---|---|---|
| 字符串大小写转换 | df[‘col’].str.lower() | 转为小写 |
| 去除空格 | df[‘col’].str.strip() | 去除两端空格 |
| 字符串替换 | df[‘col’].str.replace(‘old’, ‘new’) | 替换文本 |
| 正则表达式提取 | df[‘col’].str.extract(r’(\d+)') | 提取匹配正则的文本(如数字) |
| 字符串包含检测 | df[‘col’].str.contains(‘abc’) | 返回布尔序列,判断是否包含子串 |
时间数据处理
Timestamp 是 pandas 对 datetime64 数据类型的一个封装。datetime64 是 NumPy 中的一种数据类型,用于表示日期和时间,而 pandas 基于 datetime64 构建了 Timestamp 类,以便更方便地在 pandas 的数据结构(如 DataFrame 和 Series)中处理日期时间数据。
当 pd.to_datetime 接收单个日期时间值时,会返回 Timestamp 对象。
时间戳 timestamp
to_period()获取统计周期
import pandas as pd
d = pd.Timestamp( "2015-05-01 09:08:07.123456" )
# 属性
print('年:',d.year)
print('月:',d.month)
print('日:',d.day)
print('小时:',d.hour)
print('分钟:',d.minute)
print('秒:',d.second)
print('微秒:',d.microsecond)
print('季度:',d.quarter)
print('是否是月底:',d.is_month_end)
print('是否是月初:',d.is_month_start)
print('是否是年底:',d.is_year_end)
print('是否是年初:',d.is_year_start)
# 方法
print('星期几:',d.day_name())
print('转换为年度:',d.to_period("Y"))
print('转换为季度:',d.to_period("Q"))
print('转换为月度:',d.to_period("M"))
print('转换为季度:',d.to_period("Q"))
print('转换为周维度:',d.to_period("W"))

freq:这是 to_period() 方法最重要的参数,用于指定要转换的时间周期频率。
常见的取值如下:
- “D”:按天周期,例如 2024-01-01 会转换为 2024-01-01 这个天的周期。
- “W”:按周周期,通常以周日作为一周的结束,比如日期落在某一周内,就会转换为该周的周期表示。
- “M”:按月周期,像 2024-05-15 会转换为 2024-05。
- “Q”:按季度周期,一年分为四个季度,日期会转换到对应的季度周期,例如 2024Q2 。
- “A” 或 “Y”:按年周期,如 2024-07-20 会转换为 2024 。
日期数据转换
import pandas as pd
# 字符串字段转换为日期类型
a = pd.to_datetime('2025-07-01')
a = pd.to_datetime('20250409')
a = pd.to_datetime('2025/04/13')
a = pd.to_datetime('2025-07')
print(a)
print(type(a))
# dateFrame中的日期转换
df = pd.DataFrame({
'sales':[100,50,40],
'date':['2025-01-01','2023-03-02','2025-03-09']
})
df['datetime'] = pd.to_datetime(df['date'])
print(type(df['datetime'].dt))
df['datetime'].dt.day_name()
将日期数据作为索引
将datetime64类型的数据设置为索引,得到的就是DatetimeIndex。
df = pd.read_csv("data/weather.csv")df["date"] = pd.to_datetime(df["date"]) # 将date列转换为datetime64类型
df.set_index("date", inplace=True) # 将date列设置为索引,inplace=true直接修改原对象
df.info()# <class 'pandas.core.frame.DataFrame'>
# DatetimeIndex: 1461 entries, 2012-01-01 to 2015-12-31
将时间作为索引后可以直接使用时间进行切片取值。
print(df.loc["2013-01":"2013-06"]) # 获取2013年1~6月的数据
# precipitation temp_max temp_min wind weather
# date
# 2013-01-01 0.0 5.0 -2.8 2.7 sun
# 2013-01-02 0.0 6.1 -1.1 3.2 sun
# ... ... ... ... ... ...
# 2013-06-29 0.0 30.0 18.3 1.7 sun
# 2013-06-30 0.0 33.9 17.2 2.5 sun
print ( df . loc [ "2015" ]) # 获取2015年所有数据
# precipitation temp_max temp_min wind weather
# date
# 2015-01-01 0.0 5.6 -3.2 1.2 sun
# 2015-01-02 1.5 5.6 0.0 2.3 rain
# ... ... ... ... ... ...
# 2015-12-30 0.0 5.6 -1.0 3.4 sun
# 2015-12-31 0.0 5.6 -2.1 3.5 sun
也可以通过between_time()和at_time()获取某些时刻的数据。
df.between_time("9:00", "11:00") # 获取9:00到11:00之间的数据
df.at_time("3:33") # 获取3:33的数据
时间间隔 timedelta
当用一个日期减去另一个日期,返回的结果是timedelta64类型
d1 = pd.Timestamp( "2015-05-01 09:08:07.123456" )
d2 = pd.Timestamp( "2015-05-31 09:23:07.123456" )
print(d2-d1)
print(type(d1))
print(type(d2-d1))
将timedelta64类型的数据设置为索引,得到的就是TimedeltaIndex。
df = pd.read_csv("data/weather.csv", parse_dates=[0])
df_date = pd . to_datetime ( df [ "date" ])
df [ "timedelta" ] = df_date - df_date [ 0 ] # 得到timedelta64类型的数据
df . set_index ( "timedelta" , inplace = True ) # 将timedelta列设置为索引
df . info ()# <class 'pandas.core.frame.DataFrame'>
# TimedeltaIndex: 1461 entries, 0 days to 1460 days
将时间作为索引后可以直接使用时间进行切片取值
print(df.loc["0 days":"5 days"])
# date precipitation temp_max temp_min wind weather
# timedelta
# 0 days 2012-01-01 0.0 12.8 5.0 4.7 drizzle
# 1 days 2012-01-02 10.9 10.6 2.8 4.5 rain
# 2 days 2012-01-03 0.8 11.7 7.2 2.3 rain
# 3 days 2012-01-04 20.3 12.2 5.6 4.7 rain
# 4 days 2012-01-05 1.3 8.9 2.8 6.1 rain
# 5 days 2012-01-06 2.5 4.4 2.2 2.2 rain
时间序列
- 生成时间序列
为了能更简便地创建有规律的时间序列,pandas提供了date_range()方法。
date_range()通过开始日期、结束日期和频率代码(可选)创建一个有规律的日期序列,默认的频率是天。
print(pd.date_range("2015-07-03", "2015-07-10"))
# DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-05', '2015-07-06',
# '2015-07-07', '2015-07-08', '2015-07-09', '2015-07-10'],
# dtype='datetime64[ns]', freq='D')
此外,日期范围不一定非是开始时间与结束时间,也可以是开始时间与周期数periods。
print(pd.date_range("2015-07-03", periods=5))
# DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-05', '2015-07-06',
# '2015-07-07'],
# dtype='datetime64[ns]', freq='D')
可以通过freq参数设置时间频率,默认值是D。此处改为h,按小时变化的时间戳。
print(pd.date_range("2015-07-03", periods=5, freq="h"))
# DatetimeIndex(['2015-07-03 00:00:00', '2015-07-03 01:00:00',
# '2015-07-03 02:00:00', '2015-07-03 03:00:00',
# '2015-07-03 04:00:00'],
# dtype='datetime64[ns]', freq='h')
下表为常见时间频率代码与说明:
| 代码 | 说明 |
|---|---|
| D | 天(calendar day,按日历算,含双休日) |
| B | 天(business day,仅含工作日) |
| W | 周(weekly) |
| ME / M | 月末(month end) |
| BME | 月末(business month end,仅含工作日) |
| MS | 月初(month start) |
| BMS | 月初(business month start,仅含工作日) |
| QE / Q | 季末(quarter end) |
| BQE | 季末(business quarter end,仅含工作日) |
| QS | 季初(quarter start) |
| BQS | 季初(business quarter start,仅含工作日) |
| YE / Y | 年末(year end) |
| BYE | 年末(business year end,仅含工作日) |
| YS | 年初(year start) |
| BYS | 年初(business year start,仅含工作日) |
| h | 小时(hours) |
| bh | 小时(business hours,工作时间) |
| min | 分钟(minutes) |
| s | 秒(seconds) |
| ms | 毫秒(milliseonds) |
| us | 微秒(microseconds) |
| ns | 纳秒(nanoseconds) |
- 偏移量
可以在频率代码后面加三位月份缩写字母来改变季、年频率的开始时间。
比如 QE-JAN、BQE-FEB、QS-MAR、BQS-APR等,YE-JAN、BYE-FEB、YS-MAR、BYS-APR 等。
print ( pd . date_range ( "2015-07-03" , periods = 10 , freq = "QE-JAN" ))
# 设置1月为季度末
# DatetimeIndex(['2015-07-31', '2015-10-31', '2016-01-31', '2016-04-30',
# '2016-07-31', '2016-10-31', '2017-01-31', '2017-04-30',
# '2017-07-31', '2017-10-31'],
# dtype='datetime64[ns]', freq='QE-JAN')
同理,也可以在后面加三位星期缩写字母来改变一周的开始时间。
比如 W-SUN、W-MON、W-TUE、W-WED等。
print ( pd . date_range ( "2015-07-03" , periods = 10 , freq = "W-WED" ))
# 设置周三为一周的第一天
# DatetimeIndex(['2015-07-08', '2015-07-15', '2015-07-22', '2015-07-29',
# '2015-08-05', '2015-08-12', '2015-08-19', '2015-08-26',
# '2015-09-02', '2015-09-09'],
# dtype='datetime64[ns]', freq='W-WED')
在这些代码的基础上,还可以将频率组合起来创建的新的周期。例如,可以用小时(h)和分钟(min)的组合来实现2小时30分钟。
print ( pd . date_range ( "2015-07-03" , periods = 10 , freq = "2h30min" ))
# DatetimeIndex(['2015-07-03 00:00:00', '2015-07-03 02:30:00',
# '2015-07-03 05:00:00', '2015-07-03 07:30:00',
# '2015-07-03 10:00:00', '2015-07-03 12:30:00',
# '2015-07-03 15:00:00', '2015-07-03 17:30:00',
# '2015-07-03 20:00:00', '2015-07-03 22:30:00'],
# dtype='datetime64[ns]', freq='150min')
数据分析与统计
| 分类 | 依赖关系 | 协同应用场景 | 示例 |
|---|---|---|---|
| 描述性统计 | 所有分析的基础 | 初步了解数据分布,指导后续分组策略 | df.describe() 发现某列标准差大 → 触发分组过滤 |
| 分组聚合 | 基于描述性统计或分组过滤结果 | 按维度拆分后计算指标(如各品类销售额总和) | df.groupby(‘category’)[‘sales’].sum() |
| 分组转换 | 依赖分组聚合结构 | 在保留原始行数的前提下,添加组内计算列(如标准化、排名) | df.groupby(‘group’)[‘value’].transform(lambda x: x/x.max()) |
| 分组过滤 | 依赖描述性统计或分组聚合结果 | 根据组级条件筛选数据(如剔除样本量不足的组) | df.groupby(‘group’).filter(lambda x: len(x) > 5) |
| 相关性分析 | 可结合分组聚合结果 | 分析不同分组下变量的关联性(如各地区的价格-销量相关性) | df.groupby(‘region’)[[‘price’,‘sales’]].corr() |
关键交互逻辑
- 从宏观到微观
- 描述性统计(宏观) → 分组聚合(细分维度) → 分组转换/过滤(微观调整)
- 数据流闭环
# 示例:分析高波动品类(闭环流程)
grouped = df.groupby('category')
# 1. 描述性统计 → 2. 分组过滤 → 3. 分组转换
result = (grouped.filter(lambda x: x['price'].std() > 2)
.groupby('category')['price']
.transform(lambda x: (x - x.mean())/x.std()))
- 功能互补性
- 聚合 vs 转换:聚合减少行数,转换保持行数。
- 过滤 vs 转*:过滤删除整组,转换修改组内值。
可视化应用场景
通过以上关系图和表格,可清晰理解如何组合这些方法解决实际问题,例如:
- 数据清洗:描述统计 → 发现异常 → 分组过滤
- 特征工程:分组聚合 → 分组转换(如生成占比特征)
- 业务分析:分组聚合 → 相关性分析(如用户分群行为关联)
聚合函数
| 方法 | 说明 |
|---|---|
| sum() | 求和 |
| mean() | 平均值 |
| min() | 最小值 |
| max() | 最大值 |
| var() | 方差 |
| std() | 标准差 |
| median() | 中位数 |
| quantile() | 指定位置的分位数,如quantile(0.5) |
| describe() | 常见统计信息 |
| size() | 所有元素的个数 |
| count() | 非空元素的个数 |
| first | 第一行 |
| last | 最后一行 |
| nth | 第n行 |
分组聚合
聚合语法:
- df.groupby(“分组字段”)[“要聚合的字段”].聚合函数()
- df.groupby([“分组字段”, “分组字段2”, …])[[“要聚合的字段”, “要聚合的字段2”, …]].聚合函数()
DataFrameGroupBy 对象
对 DataFrame 对象调用groupby()方法后,会返回DataFrameGroupBy对象。如:<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000024FCBAFD700>
这个对象可以看成是一种特殊形式的 DataFrame,里面隐藏着若干组数据,但是在没有应用累计函数之前不会计算。GroupBy对象是一种非常灵活的抽象类型。在大多数场景中,可以将它看成是DataFrame的集合。
查看分组:通过groups属性查看分组结果,返回一个字典,字典的键是分组的标签,值是属于该组的所有索引的列表。
print(df.groupby("department_id").groups) # 查看分组结果
# {10.0: [100], 20.0: [101, 102], 30.0: [14, 15, 16, 17, 18, 19]...
通过get_group()方法获取分组:
print(df.groupby("department_id").get_group(50))
# 获取分组为50的数据
# employee_id first_name last_name email...
# 20 120 Matthew Weiss MWEISS...
# 21 121 Adam Fripp AFRIPP...
# 22 122 Payam Kaufling PKAUFLIN...
按列取值:
print(df.groupby("department_id")["salary"])
# 按department_id分组,取salary列
# <pandas.core.groupby.generic.SeriesGroupBy object at 0x0000022456D6F2F0>
这里从原来的DataFrame中取某个列名作为一个Series组。与GroupBy对象一样,直到运行累计函数,才会开始计算。
print(df.groupby("department_id")["salary"].mean())
# 计算每个部门平均薪资
# department_id
# 10.0 4400.000000
# 20.0 9500.000000
# 30.0 4150.000000
按组迭代:GroupBy对象支持直接按组进行迭代,返回的每一组都是Series或DataFrame。
for dept_id,group in df.groupby("department_id"):
print(f"当前组为{dept_id},组里的数据情况{group.shape}:")
print(group.iloc[:,0:3])
print("-------------------")
# 当前组为10.0,组里的数据情况(1, 10):
# employee_id first_name last_name
# 100 200 Jennifer Whalen
# -------------------
# 当前组为20.0,组里的数据情况(2, 10):
# employee_id first_name last_name
# 101 201 Michael Hartstein
# 102 202 Pat Fay
按多字段分组:
salary_mean = df.groupby(["department_id", "job_id"])[
["salary", "commission_pct"]
].mean() # 按department_id和job_id分组
print(salary_mean.index) # 查看分组后的索引
# MultiIndex([( 10.0, 'AD_ASST'),
# ( 20.0, 'MK_MAN'),
# ( 20.0, 'MK_REP'),
# ( 30.0, 'PU_CLERK'),
# ( 30.0, 'PU_MAN'),
# ...
print(salary_mean.columns) # 查看分组后的列
# Index(['salary', 'commission_pct'], dtype='object')
按多个字段分组后得到的索引为复合索引。
可通过reset_index()方法重置索引。
print(salary_mean.reset_index())
# department_id job_id salary commission_pct
# 0 10.0 AD_ASST 4400.000000 NaN
# 1 20.0 MK_MAN 13000.000000 NaN
# 2 20.0 MK_REP 6000.000000 NaN
# 3 30.0 PU_CLERK 2780.000000 NaN
# 4 30.0 PU_MAN 11000.000000 NaN
也可以在分组的时候通过as_index = False参数(默认是True)重置索引。
salary_mean = df.groupby(["department_id", "job_id"], as_index=False)[
["salary", "commission_pct"]
].mean() # 按department_id和job_id分组
print(salary_mean)# department_id job_id salary commission_pct
# 0 10.0 AD_ASST 4400.000000 NaN
# 1 20.0 MK_MAN 13000.000000 NaN
# 2 20.0 MK_REP 6000.000000 NaN
# 3 30.0 PU_CLERK 2780.000000 NaN
# 4 30.0 PU_MAN 11000.000000 NaN
分组频数计算:统计每个月不同天气状况的数量
df.groupby("month")["weather"].nunique()
# date
# 2012-01 4
# 2012-02 4
# 2012-03 4
# 2012-04 4
# 2012-05 3
一次计算多个统计值
可以通过agg()或aggregate()进行更复杂的操作,如一次计算多个统计值
df = pd.read_csv("data/employees.csv") # 读取员工数据
# 按department_id分组,计算salary的最小值,中位数,最大值
print(df.groupby("department_id")["salary"].agg(["min", "median", "max"]))# min median max
# department_id
# 10.0 4400.0 4400.0 4400.0
# 20.0 6000.0 9500.0 13000.0
# 30.0 2500.0 2850.0 11000.0
# 40.0 6500.0 6500.0 6500.0
# 50.0 2100.0 3100.0 8200.0
多个列计算不同的统计值:可以在agg()中传入字典,对多个列计算不同的统计值。
df = pd.read_csv("data/employees.csv") # 读取员工数据
# 按department_id分组,统计job_id的种类数,commission_pct的平均值
print(df.groupby("department_id").agg({"job_id": "nunique", "commission_pct": "mean"}))
# job_id commission_pct
# 10.0 1 NaN
# 20.0 2 NaN
# 30.0 2 NaN
# 40.0 1 NaN
# 50.0 3 NaN
重命名统计值:可以在agg()后通过rename()对统计后的列重命名
df = pd.read_csv("data/employees.csv") # 读取员工数据
# 按department_id分组,统计job_id的种类数,commission_pct的平均值
print(
df.groupby("department_id")
.agg(
{"job_id": "nunique", "commission_pct": "mean"},
)
.rename(
columns={"job_id": "工种数", "commission_pct": "佣金比例平均值"},
)
)
# 工种数 佣金比例平均值
# department_id
# 10.0 1 NaN
# 20.0 2 NaN
# 30.0 2 NaN
# 40.0 1 NaN
# 50.0 3 NaN
自定义函数:可以向agg()中传入自定义函数进行计算。
df = pd.read_csv("data/employees.csv") # 读取员工数据
def f(x):
"""统计每个部门员工last_name的首字母""" result = set()
for i in x:
result.add(i[0])
return result
print(df.groupby("department_id")["last_name"].agg(f))
# department_id
# 10.0 {W}
# 20.0 {F, H}
# 30.0 {B, T, R, C, K, H}
# 40.0 {M}
# 50.0 {O, E, K, S, W, L, P, D, C, V, B, T, M, J, F, ...
分组转换
聚合操作返回的是对组内全量数据缩减过的结果,而转换操作会返回一个新的全量数据。数据经过转换之后,其形状与原来的输入数据是一样的。
通过transform()将每一组的样本数据减去各组的均值,实现数据标准化
df = pd.read_csv("data/employees.csv") # 读取员工数据
print(df.groupby("department_id")["salary"].transform(lambda x: x - x.mean()))
通过transform()按分组使用平均值填充缺失值
df = pd.read_csv("data/employees.csv") # 读取员工数据
na_index = pd.Series(df.index.tolist()).sample(30) # 随机挑选30条数据
df.loc[na_index, "salary"] = pd.NA # 将这30条数据的salary设置为缺失值
print(df.groupby("department_id")["salary"].agg(["size", "count"])) # 查看每组数据总数与非空数据数
def fill_missing(x):
# 使用平均值填充,如果平均值也为NaN,用0填充
if np.isnan(x.mean()):
return 0
return x.fillna(x.mean())
df["salary"] = df.groupby("department_id")["salary"].transform(fill_missing)
print(df.groupby("department_id")["salary"].agg(["size", "count"])) # 查看每组数据总数与非空数据数
分组过滤
过滤操作可以让我们按照分组的属性丢弃若干数据。
例如,我们可能只需要保留commission_pct不包含空值的分组的数据。
commission_pct_filter = df.groupby("department_id").filter(lambda x: x["commission_pct"].notnull().all()
) # 按department_id分组,过滤掉commission_pct包含空值的分组
print(commission_pct_filter)
用得最多的pandas对象是 Series,一个一维的标签化数组对象,另一个是 DataFrame,它是一个面向列的二维表结构。
| 特性 | Series | DataFrame |
|---|---|---|
| 维度 | 一维 | 二维 |
| 索引 | 单索引 | 行索引+列名 |
| 数据存储 | 同质化数据类型 | 各列可不同数据类型 |
| 类比 | Excel单列 | 整张Excel工作表 |
| 创建方式 | pd.Series([1,2,3]) | pd.DataFrame({‘col’:[1,2,3]}) |
Pandas 与 Numpy 的关系与区别:就像学习数学要先掌握算术才能学代数一样,NumPy就是数据分析的"算术基础"。虽然可以直接用计算器(Pandas),但理解底层原理才能走得更远。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)