Pandas学习笔记

Pandas数据类型

pandas的数据类型几乎和python的数据类型一致,不一致的有:

  1. python的string 相当于 pandas的object,并且存放任意 Python 对象:str、list、dict、自定义类、混合类型。
  2. python的None 相当于pandas的nan、NAN、NaN
    使用空值:np.nan
  3. 分类类型 category(Pandas 独有),Python 无对应类型。
    适用:一列重复值非常多
  4. 时间类型:datetime64ns
    可以直接 .dt.year / .dt.month / .dt.day 批量提取时间,原生 Python 做不到批量列操作。
  5. 时间差类型:timedelta64ns
    表示两个日期相减后的间隔(几天、几小时)

Pandas数据结构

Pandas 所有操作都基于两个结构:

Series(一维带索引数组)

DataFrame(二维带索引表格,最常用)

Series 一维序列

结构:索引(index) → 值(values)

Series 创建方式

py 复制代码
# 1.列表生成,默认0,1,2...自动索引
s1 = pd.Series([1,2,3,])
print(s1)
#0    1
#1    2
#2    3

# 2.手动指定索引
s2 = pd.Series([10,20,30], index=["a","b","c"])
print(s2)
#a    10
#b    20
#c    30

# 3.字典生成:key=索引,value=数据
dic = {"语文":90, "数学":95, "英语":88}
s3 = pd.Series(dic)
print(s3)
#语文    90
#数学    95
#英语    88

# 4.元组创建
tup = (11,22,33)
s4 = pd.Series(tup)
print(s4)
#0    11
#1    22
#2    33

# 5.标量创建(单个值重复填充)
s5 = pd.Series(5, index=range(4))
print(s5)
#0    5
#1    5
#2    5
#3    5

Series 常用基础属性

py 复制代码
s.index    # 获取索引
s.values   # 获取numpy数组格式的数据
s.dtype    # 数据类型
s.name     # Series名称(对应DataFrame列名)

取值方式

py 复制代码
# 按索引名取
print(s2["a"]) # 10

# 按位置下标取
print(s2.iloc[1]) # 20

# 切片
print(s2[1:])
#b    20
#c    30

# 条件筛选
print(s2[s2 < 30])
#a    10
#b    20

DataFrame 二维表格

多行 + 多列,像 Excel 表格:

行索引:index(行标签)

列索引:columns(列名)

每一列本质就是一个 Series

DataFrame 创建方式

1.通过字典+列表构造

每个字典相当于一列数据

py 复制代码
data = {
    "姓名": ["张三", "李四", "王五"],
    "年龄": [18, 19, 20],
    "分数": [85, 92, 78]
}
df = pd.DataFrame(data)
print(df)
#   姓名  年龄  分数
#0  张三  18  85
#1  李四  19  92
#2  王五  20  78

2.通过列表+元组+指定列名(可以不指定,从0开始)

每个元组相当于一行数据

py 复制代码
data = [("孙悟空",20,80),
        ("唐僧",22,100),
        ("八戒",19,70)]
df = pd.DataFrame(data,columns=["name","age","score"])
print(df)
#  name  age  score
#0  孙悟空   20     80
#1   唐僧   22    100
#2   八戒   19     70

3.嵌套列表 + 指定列名

py 复制代码
arr = [
    [1, "A"],
    [2, "B"],
    [3, "C"]
]
df = pd.DataFrame(arr, columns=["id", "name"])
print(df)
#   id name
#0   1    A
#1   2    B
#2   3    C

4.由多个 Series 合并

py 复制代码
s_name = pd.Series(["小明","小红"])
s_age = pd.Series([17,18])
df = pd.DataFrame({
    "name": s_name,
    "age": s_age
})
print(df)
#    name  age
#0   小明   17
#1   小红   18

5.通过numpy的ndarray对象封装成pands的dataframe对象

py 复制代码
data = np.arange(6).reshape(2,3)
df = pd.DataFrame(data)
print(df)
#   0  1  2
#0  0  1  2
#1  3  4  5

DataFrame 属性

py 复制代码
df.index      # 行索引
df.columns    # 列名列表
df.values     # 转为二维numpy数组
df.shape      # (行数, 列数)
df.dtypes     # 每一列数据类型
df.ndim       # 维度:DataFrame是2,Series是1
df.T          # 行列转置

基本函数

py 复制代码
df.head(3)    # 前3行,默认前5行
df.tail(2)    # 最后2行
df.sample(n)  # 随机抽取 n 行
df.info()     # 数据概览、缺失值、类型
df.describe() # 数值列统计:均值、最值、分位数
# 修改列名
df.rename(columns={"姓名":"name", "语文":"chinese"}, inplace=True)
# 修改行索引
df.rename(index={0:"行0"})
# drop=True删掉旧索引
df.reset_index(drop=True)  
# 创建新索引
df.set_index("姓名", inplace=True)

Series 和 DataFrame 互相转换

Series 转换 DataFrame

py 复制代码
score = pd.Series([77,55],name="成绩")
data = score.to_frame()
print(data)
#   成绩
#0  77
#1  55

DataFrame 某一列 转换 Series

py 复制代码
student = pd.DataFrame({"姓名":["张三","李四"],
              "年龄":[18,19]})
print(student)

ser = student["姓名"]
print(type(ser)) #<class 'pandas.core.series.Series'>

一行转为 Series

py 复制代码
row = student.iloc[0]
print(row)
#姓名    张三
#年龄    18

Pandas 索引操作

基本概念:

行索引 index:每一行的标识,默认 0,1,2,3...

列索引 columns:就是列名 "姓名","年龄","分数"

链式索引

分两步索引:先取列,再取行。

py 复制代码
df = pd.DataFrame({
    "姓名":["张三","李四","王五","赵六"],
    "年龄":[18,19,18,20],
    "分数":[85,92,78,96]
})
print( df['姓名'][0] )
#张三

loc /iloc 索引取值

核心两类索引器:

loc:按索引标签名操作

iloc:按数字位置下标操作

loc 标签索引(匹配 index 名字、columns 名字)

语法:df.loc行条件, 列名

py 复制代码
df = pd.DataFrame({
    "姓名":["张三","李四","王五","赵六"],
    "年龄":[18,19,18,20],
    "分数":[85,92,78,96]
})
print(df.loc[:,'姓名']) #:代表全部
#0    张三
#1    李四
#2    王五
#3    赵六

iloc 位置索引(只认 0 开始数字下标)

语法:df.iloc行号, 列号

py 复制代码
df = pd.DataFrame({
    "姓名":["张三","李四","王五","赵六"],
    "年龄":[18,19,18,20],
    "分数":[85,92,78,96]
})
print( df.iloc[0] ) #获取行
#姓名    张三
#年龄    18
#分数    85

df'列名' 和 df\['列名'] 的区别

df'列名':传入字符串 → 返回 Series(一维序列)

df\['列名']:传入包含字符串的列表 → 返回 DataFrame(二维表格)

py 复制代码
df = pd.DataFrame({'姓名':['张三','李四','王五'], '年龄':[18,22,19]})
print(df['姓名'])
#返回 Series
#0    张三
#1    李四
#2    王五
print(df[['姓名']])
#返回 DataFrame
#   姓名
#0  张三
#1  李四
#2  王五

Pandas 赋值操作

以下例子操作数据:

py 复制代码
import pandas as pd
df = pd.DataFrame({
    "姓名":["张三","李四","王五"],
    "年龄":[18,19,20],
    "分数":[80,85,90]
})

inplace参数 原地修改说明

带 inplace=True 会直接修改原 DataFrame,不返回新数据;

不带则必须用变量接收

整列赋值

1.新增一列

py 复制代码
# 整列统一值
df["班级"] = "一班"

# 由已有列计算生成
df["总分"] = df["分数"] + df["年龄"]

# 列表一一对应赋值(长度必须和行数一致)
df["等级"] = ["及格","良好","优秀"]
  1. 直接覆盖原有整列
py 复制代码
df["分数"] = df["分数"] + 5
df["年龄"] = 22

单个单元格精准赋值

1.loc 按行索引 + 列名

py 复制代码
# 行索引0,姓名列改为"小张"
df.loc[0, "姓名"] = "小张"

# 行索引1,分数改成100
df.loc[1, "分数"] = 100

2.iloc 按行列数字位置

py 复制代码
# 第2行第3列(下标从0开始)
df.iloc[2, 2] = 99

批量行赋值

py 复制代码
# 选中索引0、2两行,年龄全部设为21
df.loc[[0,2], "年龄"] = 21

# 切片连续行,从0到1
df.loc[0:1, "年龄"] = 88

Pandas 排序操作

两类排序:

按值排序:sort_values() 按某一列 / 多列的数据大小排序(最常用)

按索引排序:sort_index() 按照行 / 列索引标签排序

一下例子的操作数据:

py 复制代码
# 排序操作
df = pd.DataFrame({
    "姓名":["张三","李四","王五","赵六"],
    "年龄":[18,19,18,20],
    "分数":[85,92,86,96]
})
print(df)
#   姓名  年龄  分数
#0  张三  18  85
#1  李四  19  92
#2  王五  18  86
#3  赵六  20  96

sort_values () 按字段值排序

基础语法:

py 复制代码
df.sort_values(
    by="排序列名",
    ascending=True,   # True升序(默认从小到大);False降序
    inplace=False,    # 是否原地修改原df
    ignore_index=False # 排序后是否重置行索引为0,1,2,3
)

升序操作

py 复制代码
data = df.sort_values(by = '年龄') # 默认升序操作
print(data)
#   姓名  年龄  分数
#0  张三  18  85
#2  王五  18  86
#1  李四  19  92
#3  赵六  20  96

降序操作

py 复制代码
data = df.sort_values(by = '年龄', ascending = False) # 降序操作
print(data)
#   姓名  年龄  分数
#3  赵六  20  96
#1  李四  19  92
#0  张三  18  85
#2  王五  18  86

多列组合排序

先按第一列排,第一列相同再按第二列:

py 复制代码
data = df.sort_values(by = ['年龄','分数'], ascending = [False,False]) # 降序操作
print(data)
#   姓名  年龄  分数
#3  赵六  20  96
#1  李四  19  92
#2  王五  18  86
#0  张三  18  85

排序后重置索引

排序后原来行索引会乱,加上 ignore_index=True 直接生成新序号索引:

py 复制代码
# 排序后重置索引
data = df.sort_values("分数", ascending=False, ignore_index=True)
print(data)
#   姓名  年龄  分数
#0  赵六  20  96
#1  李四  19  92
#2  王五  18  86
#3  张三  18  85

sort_index () 按索引排序

按行索引排序

py 复制代码
# 行索引从小到大
data = df.sort_index()
print(data)
#   姓名  年龄  分数
#0  张三  18  85
#1  李四  19  92
#2  王五  18  86
#3  赵六  20  96

# 行索引倒序
data = df.sort_index(ascending=False)
print(data)
#   姓名  年龄  分数
#3  赵六  20  96
#2  王五  18  86
#1  李四  19  92
#0  张三  18  85

按列索引(列名)左右排序

axis=1 代表操作列

py 复制代码
#按列索引(列名)左右排序
data = df.sort_index(axis=1)
print(data)
#   分数  姓名  年龄
#0  85  张三  18
#1  92  李四  19
#2  86  王五  18
#3  96  赵六  20

Series 排序

py 复制代码
data = df['年龄']
data.sort_values()
print(data)
#0    18
#1    19
#2    18
#3    20

Pandas 基本运算

基础算术运算

py 复制代码
s1 + 5    
s1 - 2
s1 * 10
s1 / 2
s1 ** 2   # 平方
s1 // 2   # 整除
s1 % 2    # 取余

条件逻辑运算(& 与 | 或~非)

单条件

py 复制代码
df = pd.DataFrame({
    "语文": [80, 90, 70],
    "数学": [85, 95, 75]
}, index=[0, 1, 2])

# 筛选语文大于80的数据
df[df.语文>70]
df[df["语文"]>70] #效果同上
df.query('语文>70') #使用query语法,效果同上

多条件

py 复制代码
# 筛选语文大于70且数学大于85
df[(df.语文>70) & (df.数学 > 85)]
df.query('语文 > 70 & 数学 > 85') #效果同上

自定义函数(apply)

apply:按列 / 按行传入一组数据 → 跨行多列计算、整列聚合

底层就是各列或者各行分别传入函数,计算结果

py 复制代码
# 找到最大年龄和最小年龄差
#写法1:func写法
def my_func(column):
    return column.max()-column.min()
df[['年龄']].apply(my_func)

# 写法2:lambda写法,效果同上
df[['年龄']].apply(lambda c : c.max()-c.min())

Pandas 常用统计函数(Series & DataFrame)

函数 作用
.sum() 求和,自动跳过 NaN
.mean() 算术平均值
.median() 中位数
.max() 最大值
.min() 最小值
.count() 非空元素个数(不计 NaN)
.size 总行数(包含 NaN)
.std() 标准差
.var() 方差
.quantile(q) 分位数,q=0.5就是中位数
.mode() 众数(出现次数最多的值)

方差和标准差计算过程如下:

Pandas 文件读写

csv文件

写csv文件

py 复制代码
# 写csv文件
# index=False 不把pandas自带行索引导出成一列(必加)
students = pd.DataFrame({"姓名":["张三","李四"],
              "年龄":[18,19]})
students.to_csv("students.csv", index=False, encoding="utf-8-sig")

当前文件夹下生成students.csv文件:

写入参数

py 复制代码
df.to_csv(
    # 保存路径
    path_or_buf="out.csv",

    # 是否导出pandas自带行索引(99%场景必须False)
    index=False,

    # 列名是否写入第一行
    header=True,

    # 分隔符
    sep=",",

    # 编码(Excel打开不乱码首选 utf-8-sig)
    encoding="utf-8-sig",

    # 缺失值填充为指定字符串,默认空
    na_rep="",

    # 小数保留位数
    float_format=None,

    # 指定只导出哪些列
    columns=None,

    # 追加写入文件,而不是覆盖
    mode="w",
    # mode="a" 追加,注意第一次写入要写header,后续追加建议header=False

    # 换行符兼容Windows
    lineterminator=None,

    # 是否压缩
    compression="infer"
)

读取csv文件

py 复制代码
# 读取csv文件
df = pd.read_csv("students.csv")
print(df)
#   姓名  年龄
#0  张三  18
#1  李四  19

读取参数

py 复制代码
pd.read_csv(
    # 1. 文件路径:相对路径/绝对路径/URL
    filepath_or_buffer="data.csv",

    # 分隔符
    sep=",",
    # 自动识别分隔符(适合杂乱txt/csv)
    delimiter=None,

    # 表头设置
    header=0,        # 第0行作为列名;header=None 无表头,列自动0,1,2
    names=None,      # 手动指定列名列表,搭配header=None使用

    # 指定行索引列
    index_col=None,  # 填数字/列名,把某一列设为行索引

    # 跳过行
    skiprows=None,   # 跳过前N行,或传入列表跳过指定行号
    skipfooter=0,    # 从文件末尾跳过几行

    # 只读取指定列
    usecols=None,    # 列表:["姓名","分数"] 或 [0,2]

    # 读取行数限制
    nrows=None,      # 只读取前n行,调试大文件必备

    # 编码(中文乱码核心)
    encoding="utf-8",
    # 常用备选:gbk / gb2312 / utf-8-sig

    # 缺失值标记(把指定文本识别为NaN)
    na_values=["NA", "null", "空"],

    # 布尔值识别
    true_values=["是"],
    false_values=["否"],

    # 跳过空行
    skip_blank_lines=True,

    # 大文件分块读取(超大csv防止内存溢出)
    chunksize=None,

    # 解析日期列
    parse_dates=False,
    # parse_dates=["日期列"] 直接将该列转为datetime64

    # 列数据类型强制指定
    dtype=None,
    # 示例 dtype={"手机号":str} 避免长数字科学计数法

    # 不自动推断类型
    converters=None, # 字典{列名:函数},自定义列读取处理
)

csv文件和tsv文件区别

本质分隔符区别:

CSV = Comma-Separated Values

逗号 , 作为字段分隔符

TSV = Tab-Separated Values

制表符 \t(键盘 Tab 键) 作为字段分隔符

MySQL 数据库

测试sql数据

sql 复制代码
create database py_db;
use py_db;

create table users(
name char(20),
gender char(5),
age int
)

insert into users values('张三','男',18),('李四','女',22),('王五','男',20);

创建mysql连接

方案一:pymysql + pd.read_sql
py 复制代码
# 1. 建立数据库连接
conn = ps.connect(
    host="127.0.0.1",    # 数据库地址
    port=3306,           # mysql端口默认3306
    user="root",         # 用户名
    password="123",  # 密码
    database="py_db",  # 数据库名
    charset="utf8mb4"    # 支持emoji中文
)

# 2. 直接写SQL语句读取整张表/查询结果
sql = "SELECT * FROM users;"
df = pd.read_sql(sql, con=conn)
print(df.head()) #输出
#  name gender  age
#0   张三      男   18
#1   李四      女   22
#2   王五      男   20

# 3. 用完关闭连接
conn.close()
方案二:SQLAlchemy 连接
py 复制代码
from sqlalchemy import create_engine
import pandas as pd

# 连接格式:mysql+pymysql://用户名:密码@host:端口/数据库
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/py_db")

sql = "select * from users"
df = pd.read_sql(sql, engine)
print(df)
#  name gender  age
#0   张三      男   18
#1   李四      女   22
#2   王五      男   20

engine.dispose() #销毁连接池

写数据进MySQL

to_sql函数参数介绍
py 复制代码
df.to_sql(
    # 目标数据库中的表名,字符串;表不存在会自动创建,已存在行为由if_exists控制
    name,
    # 数据库连接对象,推荐传入SQLAlchemy的engine引擎,原生pymysql连接会提示警告
    con,
    # 当目标表已经存在时的处理策略
    # fail:默认,直接抛出报错终止程序
    # replace:删除原有整张表,新建表后插入当前DataFrame数据
    # append:在原有表末尾追加插入数据,不会删除原有数据
    if_exists="fail",
    # 是否将DataFrame自带的行索引作为一列写入数据库表
    # True:写入索引列;False:不写入索引,日常业务几乎都设为False
    index=True,
    # 仅index=True时生效,给索引那一列指定数据库字段名称;不指定则默认列名为index
    index_label=None,
    # 分批插入行数,一次性写入数据量过大时拆分批次提交,避免数据库锁表、内存溢出
    # 例:chunksize=1000 代表每1000行执行一次插入SQL
    chunksize=None,
    # 手动指定每一列在数据库中对应的字段类型,字典格式 {列名: SQLAlchemy类型对象}
    # 用来约束字符串长度、整型、文本大字段等,防止自动建表字段类型不符合需求
    dtype=None,
    # 插入SQL语句执行方式,优化大批量数据写入效率
    # None:默认逐行单条insert插入,数据量大速度慢
    # "multi":多条数据合并为单条insert批量插入,大幅提升写入速度
    # 也可传入自定义函数,自定义插入逻辑(如主键重复更新ON DUPLICATE KEY)
    method=None
)
写入数据库例子
py 复制代码
# 将数据写入mysql数据库

#DataFrame数据
df = pd.DataFrame({"name":["孙悟空","唐僧","八戒"],"gender":["男","男","男"],"age":[18,22,19]})
df

# 1.创建数据库引擎
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/py_db")

# 2.写入MySQL
df.to_sql(
    name="users", #要写入表名
    con=engine,   #数据库引擎
    if_exists="append", #追加操作
    index=False #
)

# 3.销毁连接
engine.dispose()

JSON 文件

read_json的参数

py 复制代码
pd.read_json(
    # 文件路径 / 网络url / json字符串
    path_or_buf,
    # 编码格式,中文一般 utf-8
    encoding="utf-8",
    # 是否按行解析JSON Lines格式,单行一条{}就设为True
    lines=False,
    # 数据组织格式,控制json结构映射成DataFrame方式
    orient=None,
    # 解析日期字段,自动转datetime64
    convert_dates=True,
    # 指定哪些列强制解析为日期
    convert_axes=None,
    # 对列名/索引做类型推断
    dtype=True,
    # 字典手动指定每列数据类型 {列名:类型}
    dtype_backend=None,
    # 只读取前n行,大文件调试用
    nrows=None,
    # 压缩格式:zip/gz/bz2等,自动识别压缩包
    compression="infer",
    # 读取后设置行索引列
    index_col=None,
    # 只加载指定列
    usecols=None
)

to_json的参数

py 复制代码
df.to_json(
    # 输出文件路径;传None则返回JSON字符串,不写入文件
    path_or_buf=None,
    # 控制JSON整体组织结构,最核心参数
    orient=None,
    # 是否输出为JSON Lines格式(每行一条独立JSON对象,无外层[])
    lines=False,
    # 浮点数保留小数位数,None不限制
    double_precision=10,
    # 日期格式化字符串,默认ISO标准格式
    date_format="epoch",
    # 时间单位,epoch时间戳基准单位
    date_unit="ms",
    # 中文/特殊字符是否强制转为ASCII unicode编码(\uXXXX)
    # False:中文正常显示;True:中文转unicode
    force_ascii=True,
    # 缩进空格数,None输出紧凑无换行;设为2格式化美观
    indent=None,
    # 最小浮点数精度
    default_handler=None,
    # 压缩方式:infer自动识别、gzip/bz2/zip等
    compression="infer",
    # 换行符
    lineterminator="\n",
    # 模式,w覆盖;a追加写入文件
    mode="w"
)

重点参数详细说明

orient 组织结构:

取值 JSON 结构说明 适用场景
records {"列1":值,"列2":值}, {...} 数组包裹每行数据 最常用,方便后续用read_json读取
columns 默认值 {"列名":{"行索引":数值}} 以列为顶层 key
index {"行索引":{"列名":数值}} 以行索引为顶层 key
values 纯二维数组 \[行1列1,行1列2,行2列1,行2列2] 只导出纯数据矩阵
table 带表结构元信息,适合 Pandas 标准交互 跨程序数据交换

lines:

False(默认):整体一个 JSON 数组,外层 \[\]

True:必须搭配 orient="records",生成JSON Lines,每行一条{},无外层数组

force_ascii:

True:中文变成 "\u5f20\u4e09",通用性强但可读性差

False:直接输出原生中文,日常导出必开

读写例子:

py 复制代码
# 读json
df = pd.read_json("data.json")
print(df)

#写json
df.to_json('data2.json',force_ascii=False, orient="records", lines=True)

DataFrame数据的增删改查操作

查操作(查询数据)

py 复制代码
df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五"],
    "年龄": [18, 22, 19],
    "分数": [80, 95, 72]
})

df["姓名"] #获取姓名列 series对象
df.姓名    #效果同上

df[["姓名"]] #获取姓名列 dataframe对象
pd.DataFrame(df.姓名) #效果同上,将series对象转换为dataframe对象

df[['姓名','年龄']] #获取多列 dataframe对象
py 复制代码
# 根据索引值来获取数据
df[:2] #获取前
df[1:3:2]  #从1到3,步长为2获取数据(包左不包右)
df[1::1] #从1开始,到末尾结束,步长为1

rank函数

rank参数:

py 复制代码
ser.rank(
    method="average",   # 并列排名规则(核心)
    ascending=True,     # True升序(小在前);False降序(大在前)
    pct=False,          # 是否输出百分比排名
    na_option="keep",   # 缺失值处理方式
)

method参数:

average:同分取平均名次

min:同分取最前名(班级排名首选)

max:同分取最后名

first:按出场顺序强行分先后

dense:排名连续不空位

ascending参数:

True:升序排名,数值越小排名越靠前

False:降序排名,数值越大排名越靠前(分数排行必选)

例子:

py 复制代码
df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五"],
    "年龄": [18, 22, 19],
    "分数": [80, 95, 72]
})

df['分数排名'] = df['分数'].rank(method="min",ascending=False)

增操作

py 复制代码
df = pd.DataFrame({
    "name": ["张三", "李四", "王五"],
    "age": [18, 22, 19],
    "score": [88, 92, 75]
})

# 新增一列
df['money'] = 100  # 写法1:固定值
df['gender'] = ['男','女','男'] # 写法2:列表
# 写法3:通过已有的列(series)计算新的列
# print(str(df['age']))
df['num'] = df['name'] + "_0"
# 写法4:通过函数计算新值
df['func'] = (lambda : 5)()

# 通过assign()方式新增列
# 新增1列
data = df.assign(c1=11)
# 新增多列
data = df.assign(c2=22,c3=['xxx','yyy','zzz'])

删除与去重操作

drop()

默认删除指定索引值的行,如果有参数axis=1,则删除指定列名的列

返回删除后的数据,原数据没有被删除

py 复制代码
df.drop(
    labels,        # 要删除的索引名/列名
    axis=0,        # axis=0 删除行;axis=1 删除列
    index=None,    # 直接指定行索引(推荐)
    columns=None,  # 直接指定列名(推荐)
    inplace=False, # True 原地修改原df;False 返回新df,原数据不变
)

删除列:

py 复制代码
# 删除单列
df1 = df.drop(columns="score")

# 删除多列
df1 = df.drop(columns=["age", "score"])

# 旧写法 axis=1
df1 = df.drop(["age", "score"], axis=1)

删除行:

py 复制代码
# 删除索引0这一行
df1 = df.drop(index=0)

# 删除索引 0、3 两行
df1 = df.drop(index=[0, 3])

del()

永久删除原数据的数据

drop_duplicates ()

去重函数参数

py 复制代码
df.drop_duplicates(
    subset=None,    # 指定依据哪些列判断重复;默认所有列
    keep="first",   # first:保留第一条;last:保留最后一条;False:全部删掉
    inplace=False
)

整行完全重复才去重

py 复制代码
df.drop_duplicates() #默认情况

只看 name + age 两列,相同就判定重复

py 复制代码
df1 = df.drop_duplicates(subset=["name", "age"])

修改操作

py 复制代码
import pandas as pd
df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五"],
    "年龄": [18, 22, 19],
    "分数": [80, 95, 72]
})

# 1.某列赋值
df['分数'] = 100

# 2.修改某列的列值
df['年龄'] = [21,22,23]

# 3.replace 将性别这列的张三改为张四
df.replace({'姓名': {'张三': '张四'}}, inplace=True)

Pandas的高级处理

空值处理

例子数据:

py 复制代码
# 构造带空值的数据
data = {
    "姓名": ["张三", None, "李四", "王五", "赵六", "钱七", "孙八"],
    "年龄": [22, 25, np.nan, 30, 28, None, 35],
    "工资": [8500, 9200, 7800, np.nan, 10500, 9800, np.nan],
    "部门": ["技术部", "市场部", "", "技术部", None, "人事部", "市场部"],
    "入职日期": ["2022-03-15", "2021-11-22", np.nan, "2023-01-05", "2022-07-30", "2020-09-12", None]
}
df = pd.DataFrame(data)

判断空值

py 复制代码
# 1. 逐个单元格判断是否为空
print(df.isna())

# 2. 统计每列缺失值数量
print("\n每列缺失值个数:")
print(df.isna().sum())

# 3. 统计每列缺失值占比
print("\n每列缺失占比:")
print(df.isna().mean().round(2))

# 4. 查看非空值数量
print("\n每列非空值数量:")
print(df.notna().sum())

isna() / isnull() 完全等价,检测空值

notna() / notnull() 等价,检测非空

Pandas 中None 会自动转为 NaN,但空字符串不会,必须手动替换:replace("", np.nan)

默认情况下:all() 和 any() 都会开启 skipna=True,自动跳过 NaN/None 缺失值,不参与布尔运算。

py 复制代码
# all():所有元素都为真,才返回 True;只要有一个假 / 空字符,就返回 False
print(df.all())
# 姓名       True
# 年龄       True
# 工资       True
# 部门      False
# 入职日期     True
print(df.any()) #有一个元素为真直接返回True;全部为假就返回 False
# 姓名      True
# 年龄      True
# 工资      True
# 部门      True
# 入职日期    True

删除空值

py 复制代码
# 默认删除【任意一列存在空值】的整行
df_drop_row = df.dropna()

# 只删除 全部列都是空 的行
df_drop_all = df.dropna(how="all")

# 删除存在空值的整列
df_drop_col = df.dropna(axis=1)

# 子集:只看姓名、年龄两列有空才删除
df_drop_sub = df.dropna(subset=["姓名", "年龄"])

填充空值

py 复制代码
# 1. 全部空值统一填充固定值
df_fill_all = df.fillna("未知")

# 2. 不同列分别填充
df_fill_dict = df.fillna({
    "姓名": "匿名用户",
    "年龄": df["年龄"].mean(),    # 年龄用均值填充
    "工资": df["工资"].median(),  # 工资用中位数填充
    "部门": "未分配",
    "入职日期": "2000-01-01"
})

如果有字符串为空值是不处理的,只处理NaN

替换值

替换指定缺失值(比如把空字符串改成 NaN)

py 复制代码
df_replace = df.replace("", np.nan)

数据合并

例子数据:

py 复制代码
# 表1:员工基础信息
df1 = pd.DataFrame({
    "员工id": [1, 2, 3, 4, 5],
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
    "部门": ["技术部", "市场部", "人事部", "技术部", "财务部"]
})

# 表2:员工薪资信息
df2 = pd.DataFrame({
    "员工id": [2, 3, 4, 6, 7],
    "工资": [9000, 8500, 12000, 7800, 11000],
    "入职年限": [3, 2, 5, 1, 4]
})

# 表3:追加用数据(同结构)
df3 = pd.DataFrame({
    "员工id": [6, 7],
    "姓名": ["孙八", "周九"],
    "部门": ["市场部", "人事部"]
})

pd.concat ()

常用参数:

py 复制代码
pd.concat(
    # objs:必填参数,接收列表,里面放需要拼接的DataFrame/Series对象
    objs,
    # axis:拼接轴向,默认0
    # axis=0 → 纵向拼接(上下合并,堆叠行),按列名对齐
    # axis=1 → 横向拼接(左右合并,新增列),按行索引对齐
    axis=0,
    # join:索引对齐方式,仅对无法匹配的索引生效,默认outer
    # outer:并集,保留所有索引/列,缺失位置填充NaN
    # inner:交集,只保留两张表共有的索引/列,丢弃不匹配部分
    join='outer',
    # ignore_index:是否重置行索引,默认False
    # False:沿用原始各自索引,容易出现索引重复混乱
    # True:拼接后行索引从0开始重新顺序编号
    ignore_index=False,
    # keys:给每组拼接的数据添加外层索引,用于标记数据来源
    # 传入列表,长度和objs内表数量一致,后续可根据key筛选单张表数据
    keys=None,
    # levels:配合keys生成多层索引时,自定义层级数量,一般极少手动设置
    levels=None,
    # names:给keys生成的外层索引命名,搭配keys使用,给层级索引起字段名
    names=None,
    # verify_integrity:校验最终结果行索引是否存在重复,默认False
    # True:如果拼接后索引有重复值,直接抛出异常报错,防止脏数据
    verify_integrity=False,
    # sort:拼接完成后是否自动对列名(axis=0)或行索引(axis=1)排序,默认False
    sort=False,
    # copy:是否强制拷贝数据,默认True
    # True:创建新数据副本,不修改原数据内存
    # False:尽可能复用原数据内存视图,节省内存,一般无需修改
    copy=True
)

纵向合并数据

py 复制代码
res_concat_row = pd.concat([df1, df3], axis='rows', ignore_index=True)
print(res_concat_row)
#    员工id  姓名   部门
# 0     1  张三  技术部
# 1     2  李四  市场部
# 2     3  王五  人事部
# 3     4  赵六  技术部
# 4     5  钱七  财务部
# 5     6  孙八  市场部
# 6     7  周九  人事部

横向合并数据

py 复制代码
# 左右拼列,按行索引对齐,不是按某一列匹配
# 只看行号对齐,不会根据员工 id 匹配,索引错位就会拼错
new_data = pd.concat([df1, df2], axis='columns')
print(new_data)
#    员工id  姓名   部门  员工id     工资  入职年限
# 0     1  张三  技术部     2   9000     3
# 1     2  李四  市场部     3   8500     2
# 2     3  王五  人事部     4  12000     5
# 3     4  赵六  技术部     6   7800     1
# 4     5  钱七  财务部     7  11000     4

merge()

参数:

py 复制代码
pd.merge(
    left,                # 左表 DataFrame
    right,               # 右表 DataFrame
    on=None,             # 两张表**列名完全相同**的关联键(主键),可以单列/多列列表
    left_on=None,         # 左表用作关联的列(左右键名字不一样时用)
    right_on=None,        # 右表用作关联的列
    left_index=False,    # 是否用左表索引作为关联键
    right_index=False,   # 是否用右表索引作为关联键
    how='inner',         # 连接方式:inner/left/right/outer/cross
    sort=False,           # 是否按关联键排序结果
    suffixes=('_x', '_y'), # 左右表重名列后缀,左表_x,右表_y
    copy=True,           # 是否复制数据,默认True
    indicator=False,     # 新增一列标记数据来自左/右/两者
    validate=None        # 校验连接关系:1:1 / 1:m / m:1 防笛卡尔积
)

例子:

py 复制代码
res = pd.merge(df1, df2, on="员工id")
print(res)
#    员工id  姓名   部门     工资  入职年限
# 0     2  李四  市场部   9000     3
# 1     3  王五  人事部   8500     2
# 2     4  赵六  技术部  12000     5

how="inner" 默认【内连接】

只保留两边都存在匹配键的数据,取交集。

分组聚合

语法格式:

py 复制代码
# 分组
df.groupby(分组字段)
# 分组+聚合
df.groupby(分组字段)[需要计算的列].聚合函数()

单层分组

py 复制代码
# 单层分组
df.groupby('部门')['工资'].max()
# 部门
# 人事部     8800
# 市场部    10500
# 技术部     9200

多层分组

py 复制代码
# 多层分组
df.groupby(['部门','性别'])['工资'].max()
# 部门   性别
# 人事部  女      7200
#      男      8800
# 市场部  女     10500
#      男      9800
# 技术部  女      7800
#      男      9200

获取某个分组数据

get_group(分组值):从分组结果里,单独提取某一个指定分组的全部数据,返回该分组对应的子 DataFrame。

py 复制代码
data = df.groupby('部门').get_group('技术部')
print(data)
#     部门  姓名 性别    工资  工龄
# 0  技术部  张三  男  8500   3
# 1  技术部  李四  男  9200   5
# 2  技术部  王五  女  7800   2

聚合函数

统计数值类
函数字符串 作用
sum 求和
mean 算术平均值
median 中位数(抗极端异常值)
min 最小值
max 最大值
std 标准差
var 方差
quantile 分位数,默认 0.5 中位数
计数统计类
函数字符串 作用
count 非空元素个数(排除 NaN)
size 分组总行数(包含 NaN 行,只看行数)
nunique 去重后唯一值数量
文本 / 特征类
函数字符串 作用
first 分组内第一条数据
last 分组内最后一条数据
agg 函数
py 复制代码
#写法 1:单列单聚合
# 按部门分组,工资求平均
df.groupby("部门")["工资"].mean()

#写法 2:单列多个聚合(列表传参)
df.groupby("部门")["工资"].agg(["sum", "mean", "max", "min"])

#写法 3:多列不同聚合(最常用,支持自定义列名)
df.groupby("部门").agg(
    工资总和=("工资", "sum"),
    平均工资=("工资", "mean"),
    员工人数=("姓名", "count"),
    工龄最大值=("工龄", "max")
)
nunique 去重计数
py 复制代码
# 每个部门有多少个不同姓名的员工
df.groupby("部门")["姓名"].nunique()
transform 聚合

聚合结果行数和原表一致,常用于新增列

py 复制代码
# 新增一列:每行对应所在部门平均工资
df["部门均值工资"] = df.groupby("部门")["工资"].transform("mean")

分组过滤

语法格式:

py 复制代码
df.groupby(分组键).filter(func)

filter函数:

1.入参(传给函数的东西)

每次循环传入单个分组的子 DataFrame

2.出参(函数必须 return 的东西)

只能是单独 1 个布尔值

True:保留这一整个分组全部数据

False:直接删掉这一整个分组

py 复制代码
# 只要平均工资大于9000的部门
data = df.groupby('部门').filter(lambda group: group['工资'].mean() > 9000 )
print(data)
#     部门  姓名 性别     工资  工龄
# 3  市场部  赵六  女  10500   4
# 4  市场部  钱七  男   9800   3

交叉表与透视表

数据:

py 复制代码
df = pd.DataFrame({
    "部门": ["技术部","技术部","技术部","市场部","市场部","人事部","人事部"],
    "姓名": ["张三","李四","王五","赵六","钱七","孙八","周九"],
    "性别": ["男","男","女","女","男","女","男"],
    "工资": [8500,9200,7800,10500,9800,7200,8800]
})

交叉表

语法规则:

py 复制代码
pd.crosstab(
    index=df["行字段"],
    columns=df["列字段"],
    values=df["数值列"],    # 可选,指定后必须搭配aggfunc
    aggfunc="聚合函数",
    margins=True,
    normalize=True          # 转为占比百分比
)

例子:

py 复制代码
ct = pd.crosstab(
    index=df["部门"],
    columns=df["性别"]
)
print(ct)
# 性别   女  男
# 部门       
# 人事部  1  1
# 市场部  1  1
# 技术部  1  2

透视表

语法规则:

py 复制代码
pd.pivot_table(
    data=df,
    index="行字段",        # 放在行上的分组维度
    columns="列字段",     # 放在列上的分组维度
    values="要聚合的数值列",
    aggfunc="聚合方式",   # mean/sum/count/max/min 等
    fill_value=0,         # 空值填充
    margins=True          # 开启合计行/列
)

例子:

py 复制代码
pt = pd.pivot_table(
    df,
    index="部门",
    columns="性别",
    values="工资",
    aggfunc="mean"
)
print(pt)
# 性别         女       男
# 部门                  
# 人事部   7200.0  8800.0
# 市场部  10500.0  9800.0
# 技术部   7800.0  8850.0
相关推荐
三品吉他手会点灯3 小时前
嵌入式机器学习 - 学习笔记1.1.1 - 什么是机器学习?
c语言·人工智能·笔记·嵌入式硬件·学习·机器学习
qq5752173193 小时前
Notepad++下载和安装
笔记·notepad++·记事本
程序猿小泓3 小时前
从 Claude Code 学 Agent Harness:一个前端工程师的 AI Agent 学习笔记
前端·人工智能·学习
换个昵称24 小时前
大模型学习8下-高性能推理引擎vLLM学习笔记
笔记·学习·vllm
LONGZHIQIN4 小时前
C#基础复习笔记
开发语言·笔记·c#
得闲喝茶4 小时前
跨表数据匹配——VLOOKUP、XLOOKUP
大数据·数据库·笔记·信息可视化·数据分析·excel
报错小能手5 小时前
OpenTenBase学习——集群编译&部署
学习
渣渣灰飞5 小时前
MySQL 系统学习 第二阶段 第四章:DCL(Data Control Language)第二节:权限管理与角色(Role)
数据库·学习·mysql
霸道流氓气质6 小时前
Java 工程师 AI 智能体(Agent)完整学习路线
java·人工智能·学习
想要入门的程序猿6 小时前
摄影测量学习()
数码相机·学习