python模块 pands-操作excel

1、安装pandas

#更新pip
python -m pip install "pandas==1.1.5" -i https://mirrors.aliyun.com/pypi/simple/
#安装pandas
python -m pip remove "pandas==1.1.5" -i https://mirrors.aliyun.com/pypi/simple/

image

2、基础概念

2.1、pandas两个核心对象

  • pandas里最核心的两个对象:Series和DataFrame
  • Series是“一列数据”。
    • 可以理解成:带标签的一维数组。
    • 结构:索引(index)+值(values)
    • 类似:Excel里的某一列
  • DataFrame是“表格数据”
    • 可以理解成:由多列Series组成的二维表
    • 结构:行索引(index)+列名(columns)+每个单元格的值
    • 类似:Excel表格、SQL数据表
  • 两者关系:
    • DataFrame的每一列,实际上就是一个Series
    • 所以可以把DataFrame看成“多个Series按列排成的一张表”

imageimage

2.2、Pandas支持的数据类型

  • 具体类型是Python、Numpy各种类型的混合:
    • float
    • int
    • string
    • bool
    • datetime64[nsr]、datetime64[nsr,tz]、timedelta[ns]
    • category
    • object
#在不同数据类型间转换
df.astype(
    dtyp              #指定转换的数据类型,可以使用numpy或者python中的数据类型:int/float/bool/str
    copy=True         #是否生成新的副本,而不是替换原数据框
    errors="raise"    #转换出错时是否抛出错误,raise/ignore
)

示例

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 查看各列的数据类型
df1.dtypes

# 将df里所有的列转换成str
df1.astype('str')
# 修改某一列数据的数据类型
df1['语文'].astype('str')
# 转换错误。忽略异常
df1['语文'].astype('int', errors='ignore')

3、Series对象

1、创建Series对象

import pandas as pd
# 创建Series对象,使用列表
data1 = pd.Series([1, 2, 3, 4])
# 创建Series对象,使用字典,默认将字典的key作为索引
stu = {"name": "张三", "age": 23, "sex": "男"}
data2 = pd.Series(stu)
# 创建Series对象,使用标量
data3 = pd.Series(20, index=list("abcd"))

>>>print(data1)
0    1
1    2
2    3
3    4
dtype: int64
>>>print(data2)
name    张三
age     23
sex      男
dtype: object
>>>print(data3)
a    20
b    20
c    20
d    20
dtype: int64

# 创建Series对象,使用列表,指定索引
data1_1 = pd.Series([1, 2, 3, 4], index=["a", "b", "c", "d"])
# 创建Series对象,使用列表,使用list指定索引
data1_2 = pd.Series([1, 2, 3, 4], index=list("abcd"))

>>>print(data1_1)
>>>print(data1_2)
a    1
b    2
c    3
d    4
dtype: int64

# 创建Series对象,使用字典,指定的索引,取交集
data2_1 = pd.Series(stu, index=["name", "sex"])
# 创建Series对象,使用字典,指定的索引,如果字典的key中不存在该索引,则改索引的值未Nan
data2_2 = pd.Series(stu, index=["name", "sex", "birth"])

>>>print(data2_1)
name    张三
sex      男
dtype: object
>>>print(data2_2)
name      张三
sex        男
birth    NaN
dtype: object

2、查看Series对象

# 查看Series对象的索引
>>>print(data1.index)
RangeIndex(start=0, stop=4, step=1)
# 查看Series对象的值
>>>print(data1.values)
[1 2 3 4]

4、DataFrame对象

1、创建DataFrame对象

import pandas as pd
# 创建两个字典
a_dict = {"bj": 11, "sh": 12, "gz": 13}
b_dict = {"bj": 21, "sh": 22, "gz": 23}
# 生成两个Series对象
a_series = pd.Series(a_dict)
b_series = pd.Series(b_dict)

# 创建dataFrame对象,使用两个Series对象。将Series对象作为字典的值传入DataFrame
data1 = pd.DataFrame({"a": a_series, "b": b_series})
# 创建dataFrame对象,使用一个Series对象。
data2 = pd.DataFrame(a_series)
# 创建dataFrame对象。使用列表,列表元素是字典
data3 = pd.DataFrame([a_dict, b_dict])
# 创建dataFrame对象,使用二维数组
import numpy as np
data4 = pd.DataFrame(np.random.randint(10, size=(3, 4)), index=list("abc"), columns=list("ABCD"))

>>>print(data1)
     a   b
bj  11  21
sh  12  22
gz  13  23
>>>print(data2)
     0
bj  11
sh  12
gz  13
>>>print(data3)
   bj  sh  gz
0  11  12  13
1  21  22  23
>>>print(data4)
   A  B  C  D
a  1  3  1  5
b  3  1  2  8
c  4  6  9  8

# 创建dataFrame对象,指定行索引和列索引
data3_1 = pd.DataFrame([a_dict, b_dict], index=["a", "b"])
data2_1 = pd.DataFrame(a_series, columns=["a"])

>>>print(data3_1)
   bj  sh  gz
a  11  12  13
b  21  22  23
>>>print(data2_1)
     a
bj  11
sh  12
gz  13

2、查看DataFrame对象

import pandas as pd
# 获取dataFrame对象的index(行索引)
print(data1.index)
# 获取dataFrame对象的columes(列索引)
print(data1.columns)
# 获取dataFrame对象的values
print(data1.values)

5、读取文件

5.1、读取excel文件

  • 读Excel还需要额外库,一般是openpyxl(读.xlsx)或xlrd(老的.xls)

1、基本语法

df = pd.read_excel(
    file_path,
    sheet_name=0,       # 要读取的第一张工作表(默认)。sheet_name="Sheet1",按名称。sheet_name=[0, 1],读多张表,返回 dict。sheet_name=None,读所有表,返回 dict
    header=0,           # 第0行作为列名。header=None使用整数作为列索引
	index_col=0,        # 用第0列做行索引。
    usecols=None,       # 只读哪些列。usecols=["姓名", "年龄"]
    skiprows=None,      # 跳过前几行(跳过表头上方无用行)
	engine="openpyxl"   # 指定用 openpyxl 引擎读取
)

2、读取excel文件

import pandas as pd
# 读取excel文件,默认第一个sheet
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")
# 读取excel文件,指定sheet(sheet名称或sheet索引)
df2 = pd.read_excel("stu_data.xlsx", sheet_name="score",engine="openpyxl")
df3 = pd.read_excel("stu_data.xlsx", sheet_name=0, engine="openpyxl")

# 读取excel文件,所有sheet
df4 = pd.read_excel("stu_data.xlsx", sheet_name=None, engine="openpyxl")

# 读取excel文件,只读取指定的列
df5 = pd.read_excel("stu_data.xlsx", sheet_name=0, usecols=[0, 1, 2], engine="openpyxl")
df6 = pd.read_excel("stu_data.xlsx", sheet_name=0, usecols=["姓名", "性别", "语文"], engine="openpyxl")

# 设置显示的行数和列数
pd.options.display.max_rows = 3
pd.options.display.max_columns = 3

5.2、读取csv文件

  • 需要设置编码格式,Python中用得比较多的两种编码格式是UTF-8和gbk,默认编码格式是UTF-8。

1、基本语法

df = pd.read_csv(
    file_path,
    encoding="utf-8",     # 如果报错,换成 encoding="gbk"
	sep=","               # 指定分隔符,默认使用逗号
)

2、读取csv文件

import pandas as pd
#读取csv文件,指定文件编码格式utf-8
df1 = pd.read_csv("stu_data.csv",encoding="utf-8")
df2 = pd.read_csv("stu_data.csv",encoding="utf-8",sep=",")

5.3、读取txt文件

1、基本语法

df = pd.read_table(
    file_path,
    encoding="utf-8",       # 中文可能用 encoding="gbk"
	sep=",",                # 指定分隔符,默认使用制表符\t
	delim_whitespace=True   # 自动用连续空白字符分隔(或 sep=r"\s+")
)

2、读取txt文件

import pandas as pd
# 读取txt文件。分隔符默认使用制表符\t
df1 = pd.read_table("stu_data.txt", encoding="utf-8", sep="\t")

5.4、读取mysql

  • pandas读MySQL,一般是借助SQLAlchemy+数据库驱动,然后用pd.read_sql()或pd.read_sql_query()。
import pandas as pd
import pymysql
# 配置mysql连接引擎
conn = pymysql.connect(
    host='localhost',
    user='user',
    passwd='passwd',
    db='mydb',
    port=3306,
    charset='utf-8'
)
# 读取数据表(index_col,columns)
df = pd.read_sql(
    sql='sql语句',
    conn=conn,
    index_col=None,  # 被用做行索引的列名称
    columns=None  # 需要读取的列
)

6、保存数据

6.1、保存数据csv

df.to_csv(
    file_path,               # 保存路径
    index=False,             # 不保存行索引
    encoding="utf-8",        # 中文+Excel 推荐用 utf-8-sig
	sep=",",                 # 指定分隔符,默认使用逗号
	mode='w'                 # Python写模式,读写方式:r,r+,w,w+,a,a+
	columns=[]               # 需要导出的列,示例columns=['学号','性别','身高']
	header=[]                # 指定导出数据的列的新列名,可直接提供1ist。示例header=['sno','height','weight']
)

6.2、保存数据到Excel

# 保存一个DataFrame到Excel文件
df.to_excel(
    file_path,           # 保存路径
    index=False,         # 不把行索引写入 Excel
    sheet_name="Sheet1"  # 工作表名称
	columns=[]               # 需要导出的列,示例columns=['学号','性别','身高']
	header=[]                # 指定导出数据的列的新列名,可直接提供1ist。示例header=['sno','height','weight']
)

# 保存多个DataFrame到同一个Excel文件
with pd.ExcelWriter(r"D:\data\multi_sheet.xlsx") as writer:
    df.to_excel(writer, sheet_name="Sheet1", index=False)
    # 假设还有另一个 df2
    df2.to_excel(writer, sheet_name="Sheet2", index=False)

7、数据操作

image

7.1、了解数据

  • head():返回前n行(观察索引值),默认是5行
  • tail():返回后n行(观察索引值),默认是5行
  • info():查看数据表中所有列的数据类型。
  • shape():以元组的形式返回行数、列数。注意shape方法获取行数和列数时不会把行索引和列索引计算在内。
  • describe():获取所有数值类型字段的分布情况,即均值是多少、最值是多少、方差及分位数分别是多少。

7.2、DataFrame的列操作

7.2.1、新增列(或修改列)

1、基本语法

# 新增加一列。根据新数据添加,val是列值,index是行索引
df[cloumn_new] = pd.series([val1, val2, ..., val3], index=[index1, index2, ..., index3])

# 新增加一列。使用常数
df['cloumn_new’]=value
df.cloumn_new=1         #注意不会报错!但并没有新增加一列

# 新增加一列。使用简单四则运算
df[cloumn_new] = df["cloumn1"] + df["cloumn2"]
df['cloumn_new] = df[cloumn1'] * 100
df['cloumn_new] = df.cloumn1 * 100

# 新增加一列。使用内部函数计算
import math
df['cloumn_new']=math.sqrt(value)
df['cloumn_new']=math.sqrt(df.cloumn1)    #报错:TypeError: cannot convert the series to <class 'float'>

import numpy
df['cloumn_new']=numpy.sqrt(df.cloumn1)

2、示例

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

df1["自然"] = pd.Series([99, 44, 55], index=[1, 3, 5])
df1["总分"] = df1["语文"] + df1["数学"] + df1["英语"] + df1["自然"]

7.2.2、删除列

1、基本语法

df.drop(
    index=None,      # 要删除的行索引,多个时用列表
    columns=None,    # 要删除的列索引,多个时用列表
    inplace=Ture     # 更改原数据,默认是False
)

# 直接删除原数据框相应的一列,建议尽量少用
del df["columns"]
del df.column        # 不允许

# 删除并返回这一列(column)
df.pop(column) 

2、示例

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 删除一行
df1.drop(index=2, inplace=True)
# 删除两列
df1.drop(columns=["姓名", "性别"], inplace=True)

# 删除一列
del df1["语文"]
# 不允许,报错AttributeError: 数学
del df1.数学

7.2.3、修改列值

1、整列统一修改

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")
df1["语文"] = df1["语文"] + 100

2、只改满足条件的行的某列

  • 见“条件筛选,筛选行”

7.2.4、获取列

1、基本语法

# 获取单列,结果为Series
df.column
df["column"]

# 获取多列,结果为DataFream
df[["column1", "column2", ..., "columnN"]]

2、示例

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

print(df1.姓名)
print(df1["姓名"])
print(df1[["姓名", "性别"]])

7.2.5、修改列名

1、基本语法

# 修改列名称,修改全部列名称(必须和源数据的列数一致)
df.columns = ["column1", "column2", ..., "columnN"]

# 修改列名称,修改指定列名称。该方法默认返回一个新对象
df.rename(
    columns = {旧名称1:新名称1, 旧名称2:新名称2, ..., 旧名称N:新名称N},
    inplace=True               # 更改原数据,默认是False
)

2、示例

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

df1.columns = ["Name", "Sex", "Chinese", "Mathematics", "English"]
df1.rename(columns={"姓名": "Name", "英语": "English"}, inplace=True)

7.2.6、改变列顺序

1、按新的列顺序重新指定

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 手动调整顺序
df2 = df1[["姓名", "性别", "英语", "数学", "语文"]]

2、把某列移到最前面

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 手动调整顺序
cols = ["性别"] + [c for c in df1.columns if c != "性别"]
df2 = df1[cols]

7.2.7、条件筛选,筛选行

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 获取 "语文" > 60的行
print(df1[df1["语文"] > 60])
# 获取 "语文" > 60,且"数学" > 60的行
print(df1[(df1["语文"] > 60) & (df1["数学"] > 60)])

7.3、loc和iloc

  • loc和iloc是pandas里最重要的两个索引方式,用来“按行+列”精确取数据、改数据。
    • loc:按“标签”取/改(行索引名、列名)
    • iloc:按“位置(下标)”取/改(第几行、第几列,从0开始)
  • 索引方式不同:
    • loc[行标签, 列标签]
    • iloc[行位置, 列位置]
  • 切片的左右端是否包含:
    • loc包含右端的数据,即[]
    • iloc不包含右端的数据,即[)
  • 不要搞混标签和位置。假设行索引是 1, 2, 3(不是 0,1,2):
    • df.loc[1],行标签为 1 的那一行
    • df.iloc[1],“第二行”

7.3.1、loc按“标签”

1、获取行,按行标签(index)取

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col="姓名")

# 获取单行,取行标签为 "张三" 的那一行(Series)
print(df1.loc["张三"])
# 取多行(DataFrame)
print(df1.loc[["张三", "李四"]])
# 按标签切片,注意:**包含右端点** "李四"
print(df1.loc["张三": "李四"])

2、获取列:按列名取

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col="姓名")

# 获取单列,"语文"列(Series)
print(df1.loc[:, "语文"])
# 获取多列(DataFrame)
print(df1.loc[:, ["语文", "英语"]])
# 按标签切片
print(df1.loc[:, "语文":"英语"])

3、获取单元格:行列一起用

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col="姓名")

# 获取单值。获取"张三"的"数学"成绩
print(df1.loc["张三", "数学"])
# 获取连续范围的值。行:"张三": "李四", 列:"数学":"英语"
print(df1.loc["张三": "李四", "数学":"英语"])

4、条件筛选,筛选行

  • 模式:df.loc[行条件, 列选择]
import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col="姓名")

# 获取"语文" > 60 的行
df1.loc[df1["语文"] > 60, ["语文", "数学", "英语"]]
# 条件修改:"语文" > 60 的行,"英语" 加 100
df1.loc[df1["语文"] > 60, "英语"] = df1.loc[df1["语文"] > 60, "英语"] + 100

7.3.2、iloc按“位置”(整数下标)

1、获取行,按行位置取

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col="姓名")

# 获取单行,取行位置为 0 的那一行,即第一行(Series)
print(df1.iloc[0])
# 取多行(DataFrame)
print(df1.iloc[[0, 2]])
# 按标签切片,注意:**不包含右端点**
print(df1.iloc[0:2])

2、获取列:按列位置取

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col="姓名")

# 获取单列,"语文"列(Series)
print(df1.iloc[:, 0])
# 获取多列(DataFrame)
print(df1.iloc[:, [0, 3]])
# 按标签切片
print(df1.iloc[:, 1:3])

3、获取单元格:行列一起用

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col="姓名")

# 获取单值。第 2 行第 2 列的值
print(df1.iloc[1, 1])
# 获取连续范围的值。前3行前3列
print(df1.iloc[0: 3, 0:3])

4、条件筛选,筛选行(通常先用条件筛行,再 iloc 选列)

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col="姓名")

# 先按条件筛行,之后取第 1 到第 2 列
print(df1[df1["语文"] >= 60].iloc[:, 0:2])

7.4、操作单元格

1、获取、修改单元格

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 获取单元格
df1.姓名[1]
df1["姓名"][1]
df1.loc[1,"姓名"]
df1.iloc[1,0]

# 修改单元格(推荐使用loc、iloc)
df1.loc[1,"姓名"] = "倩倩"

# 条件筛选,修改指定列的值
df1["性别"][df1["性别"].isin(["男"])] = "man"
df1.性别[df1.性别.isin(["男"])] = "man"
df1.语文[df1.语文 == 88] = 188
df1[df1.语文 == 99].语文 = 199    # 注意,没修改,也没有报错

2、replace():替换匹配到的值

  • 见“replace():替换匹配到的值”

8、索引

8.1、设置索引

1、基本语法

#指定某一列作为索引
df.set_index(
    keys,                    #被指定为索引的列名,复合索引用list
    drop=True,               #建立索引后,删除该列
    append=False,            #不在原索引基础上添加索引,默认是直接替换原索引
    inplace=False,           #不修改原数据
    erify_integrity=False    #
)

#重置索引(重置索引为 0,1,2,...)
df.reset_index(
    drop=False,
    inplace=False
)

2、示例1

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 单索引
df1.set_index(keys='姓名', inplace=True, drop=False)
# 复合索引
df1.set_index(keys=['姓名', "性别"], inplace=True, drop=False)

3、示例2

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")
df1.set_index(keys='姓名', inplace=True)

# 将索引还原列
# df1.reset_index(inplace=True)
# 删除索引
df1.reset_index(inplace=True, drop=True)

8.2、自定义索引

1、创建 DataFrame 时指定索引

# 创建数据时,创建索引
df = pd.DataFrame({'var1': 1.0,
                   'var2': [1, 2, 3, 4],
                   'var3': ['test', 'python', 'test', 'hello'],
                   'var4': 'cons'},
                  index=[1, 2, 3, 4])

2、后期直接赋值修改索引

import pandas as pd
df = pd.DataFrame({'var1': 1.0,
                   'var2': [1, 2, 3, 4],
                   'var3': ['test', 'python', 'test', 'hello'],
                   'var4': 'cons'},
                  index=[1, 2, 3, 4])
# 后期直接赋值修改索引
df.index = ["stu1", "stu2", "stu3", "stu4"]

8.3、引用和修改索引

  • 注意:索引仍然是有存储格式的,注意区分数值型和字符型的引用方式
import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col="姓名")

# 引用索引
df1.index

# 修改单索引名
df1.index.name = "name"
# 修改复核索引名
# df1.index.names = ['name', 'sex']

# 修改索引值
df1.index = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']

8.4、更新索引

  • reindex可以使用数据框中不存在的数值建立索引,并据此扩充新索引值对应的索引行/列,同时进行缺失值填充操作。

1、基本语法

df.reindex(
    labels                #类数组结构的数值,将按此数值重建索引,非必需
    copy=True             #建立新对象而不是直接更改原df/series缺失数据的处理方式
    method                #针对已经排序过的索引l,确定数据单元格无数据时的填充方法,非必需
		pad/ffill             #用前面的有效数值填充
		backfill/bfill        #用后面的有效数值填充
		nearest               #使用最接近的数值进行填充
    fill_value=np.NaN     #将缺失值用什么数值替代
    limit=None            #向前/向后填充时的最大步长
)

2、示例

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 索引存在,直接获取所在的行。索引不存在的,使用NaN填充
print(df1.reindex([1, 2, 3, 9, 10]))
# 缺值处理,使用前一个值填充
print(df1.reindex([1, 2, 3, 9, 10], method='ffill'))
# 缺值处理,使用指定值
print(df1.reindex([1, 2, 3, 9, 10], fill_value=5))

9、Pandas数据排序

  • Pandas 里“排序”主要有两类:
    • 按 值排序:sort_values()(最常用,按某一列或多列排序)
    • 按 索引排序:sort_index()(按行索引或列索引的顺序)

1、基本语法

#按 值 排序
df.sort_values(
    by,                 #单列排序或者多列排序(列表)
    ascending=True,     #升序排序(默认)。False降序排序
    inplace=False,      #不修改原数据
    na_position="last"  #缺失值的排列顺序,first/last
)

#按 索引 排序
df.sort_index(
    level=None,         #指定用于排序的级别顺序号/名称(多重索引时)
    ascending=True,     #升序排序。False降序排序
    inplace=False,      #不修改原数据
    na_position="last"  #缺失值的排列顺序,first/last
)

2、按值排序

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

#单字段排序
df2 = df1.sort_values(by='语文',ascending=False)
#多字段排序
df3 = df1.sort_values(by=['语文','数学'],ascending=[False,True])

3、按索引排序

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl", index_col=[0,1])

#单字段排序
df2 = df1.sort_index(level='姓名',ascending=False)
#多字段排序
df3 = df1.sort_index(level=['姓名','性别'],ascending=[False,True])

10、常用函数

10.1、isin():判断是否属于某个集合

  • isin() 用来做“是否属于某个集合”的判断,非常适合用来按列表批量过滤行或列。
    • 对 Series:判断每个元素是否在给定列表中,返回布尔 Series
    • 对 DataFrame:对每个单元格判断,返回布尔 DataFrame
  • isin() 右侧的“集合”可以是:
    • list或元组:[1, 2, 3]
    • set:{1, 2, 3}(大集合用 set 会更快)
    • Series或Index:df["col"].isin(other_series)
  • isin() 与 == 、in 的区别
    • x in [1,2,3] 是对“单个值”判断
    • df["col"] == 1 是对整列判断是否等于一个值
    • df["col"].isin([1,2,3]) 是对整列判断是否在一组值中(更通用)

1、在Series上用isin(最常用场景)

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 返回布尔 Series
mask = df1["姓名"].isin(["张三", "李四", "王二"])
>>>print(mask)
0     True
1    False
2    False
3     True
4     True
5    False
6    False
7    False
8    False
Name: 姓名, dtype: bool

>>>print(df1[mask])
   姓名 性别  语文  数学  英语
0  张三  男  88  11  99
3  李四  男  44  66  66
4  王二  男  55  33  99

2、在DataFrame上用isin

  • 对整张表调用df.isin(values)会对每个单元格判断是否在values里,返回同样形状的布尔表。
  • 一般不直接拿这个结果筛行,而是配合其它操作(比如统计、替换等)。
import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

bool_df = df1.isin(["张三", 88])
>>>print(bool_df)
      姓名     性别     语文     数学     英语
0   True  False   True  False  False
1  False  False  False  False  False
2  False  False  False  False  False
3  False  False  False  False  False
4  False  False  False  False  False
5  False  False  False  False  False
6  False  False  False  False  False
7  False  False   True  False  False
8  False  False  False   True  False

3、条件筛选,筛选行

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 用 isin 做“白名单过滤”
valid_ids = ["张三", "李四", "王二"]
df_valid = df1[df1["姓名"].isin(valid_ids)]
>>>print(df_valid)
   姓名 性别  语文  数学  英语
0  张三  男  88  11  99
3  李四  男  44  66  66
4  王二  男  55  33  99

# 用 isin 做“黑名单过滤”(排除某些值)(~ 是取反)
valid_ids = ["张三", "李四", "王二"]
df_valid = df1[~df1["姓名"].isin(valid_ids)]
>>>print(df_valid)
   姓名 性别  语文  数学  英语
1  小倩  女  77  55  33
2  小红  女  99  99  55
5  麻子  男  66  33  44
6  小翠  女  77  11  33
7  赵一  男  88  22  22
8  钱二  男  77  88  33

# 多个条件组合
df_filtered = df1[df1["性别"].isin(["男"]) & (df1["语文"] >= 60)]
>>>print(df_filtered)
   姓名 性别  语文  数学  英语
0  张三  男  88  11  99
5  麻子  男  66  33  44
7  赵一  男  88  22  22
8  钱二  男  77  88  33

4、在多列上用 isin

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

cols = ["语文", "数学", "英语"]
values = [88, 99]
# 返回["语文", "数学", "英语"]三列中包含[88, 99]任意一个值的行
mask = df1[cols].isin(values).any(axis=1)
df_selected = df1[mask]
print(df_selected)
>>>
   姓名 性别  语文  数学  英语
0  张三  男  88  11  99
2  小红  女  99  99  55
4  王二  男  55  33  99
7  赵一  男  88  22  22
8  钱二  男  77  88  33

cols = ["语文", "数学", "英语"]
values = [88, 99, 11]
# 返回["语文", "数学", "英语"]三列中包含[88, 99, 11]所有值的行
mask = df1[cols].isin(values).all(axis=1)
df_selected = df1[mask]
print(df_selected)
>>>
   姓名 性别  语文  数学  英语
0  张三  男  88  11  99

df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")
mask = df1.isin({'语文': [88, 99], '性别': ['女']}).any(axis=1)
df_selected = df1[mask]
print(df_selected)
>>>
   姓名 性别  语文  数学  英语
0  张三  男  88  11  99
1  小倩  女  77  55  33
2  小红  女  99  99  55
6  小翠  女  77  11  33
7  赵一  男  88  22  22

10.2、query():简化条件表达式

  • query() 是 DataFrame 用来按“字符串表达式”筛选行的函数,相当于把之前用布尔条件写的过滤,写成一条更简洁的“类 SQL 语句”。
    • 写法:df.query("条件表达式")
    • 表达式里直接用“列名”当变量(比 df[df['col'] > 10] 更简洁)
  • 支持的逻辑运算符:
    • >、<、>=、<=、==、!=
    • and、or 、not
    • in、not in
  • query 对列名有要求,特殊列名要用反引号包起来

1、单条件

# 普通写法
df1[df1["语文"] > 80]
# query写法
df1.query("语文 > 80")

2、多条件

# 普通写法
df1[(df1["语文"] > 60) & (df1["数学"] >= 60)]
# query写法
df1.query("语文 > 60 and 数学 > 60")

3、字符串/类别条件

(1)字符串等于某个值

  • 注意:字符串常量要用引号包起来,可以用单引号 ' 或双引号 "。
df1.query("姓名 == '钱二'")

(2)多个取值之一(类似 isin)

#等价于df1[df1["姓名"].isin(["张三", "李四"])]
df1.query("姓名 in ['张三', '李四']")

4、query中使用变量(本地Python变量)

  • 变量要用@引用
import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

score = 60
df1.query("语文 > @score and 数学 > @score")

10.3、apply():调用func

  • DataFrame.apply()
    • 对DataFrame的每一列(axis=0,默认)或每一行(axis=1)调用func。
    • func接收Series(raw=False时)或ndarray(raw=True时),返回标量/Series/list等。
  • Series.apply()
    • 对Series中每个元素或对整个Series(若func接收Series)进行操作(通常是按元素)。
#对DataFrame的每一列(axis=0,默认)或每一行(axis=1)调用func
df.apply(
    func,        
    axis=0    #针对每列进行计算。1是针对每行进行计算
)

#对Series的每个值调用func
df['cloumn'].apply(
    func,
    convertDType
)

1、Series,对每个值使用内部函数

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

import numpy
#对语文列的每个值开平方
df1['cloumn_new']=df1['语文'].apply(numpy.sqrt)

2、Series,对每个值使用自定义函数

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

def fun(col):
    col = col - 10
    return col
#对语文列的每个值减10
df1['cloumn_new']=df1['语文'].apply(fun)

3、DataFrame,对每行、每列使用内部函数

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

df2 = df1[["语文", "数学", "英语"]]
# 每列求和
df2.apply(sum, axis=0)
# 每行求和
df2.apply(sum, axis=1)

4、DataFrame,对每行、每列使用自定义函数

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

df2 = df1[["语文", "数学", "英语"]]
def fun(arg):
    print(arg)
# 打印每行,返回Series
df2.apply(fun, axis=0)
# 打印每行,返回Series
df2.apply(fun, axis=1)

10.4、assign():返回新的DataFrame

  • signature: DataFrame.assign(**kwargs)
  • 概述
    • 功能:返回一个新的DataFrame,通过kwargs中指定的“表达式”新增或替换列(不在原地修改)。
    • kwargs的值可以是标量、Series/ndarray/Index(会按照索引对齐或广播),也可以是可调用对象(callable),callable会以整个DataFrame作为参数被调用并应返回一个可赋给列的对象(Series/array/scalar)。
  • 主要特性与规则
    • 不会原地修改:assign返回的是新的DataFrame,原DataFrame保持不变(除非你把返回值赋回原变量)。
    • 可链式调用友好:因为返回DataFrame,适合在方法链中使用(method chaining)。
    • 可用可调用(callable)作为列值:callable会接收当前的DataFrame。使用callable(常见写法:lambda df: ...)。
    • kwargs必须是合法的Python标识符(即作为关键字名),如果列名不是合法标识符,可用dict + **的方式传入。
    • 如果传入Series,会根据索引对齐;传入标量则广播到所有行;传入长度不一致的可迭代对象会抛错。
    • 多个kwargs按从左到右顺序求值,后面的可调用可以引用前面已创建的列。
import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 新增简单列(标量/表达式)。总分是语文、数学、英语的和,cloumn_new1列的值都是常数5
df2 = df1.assign(总分=df1['语文'] + df1['数学'] + df1['英语'],cloumn_new1=5)
# 链式调用。总分是语文、数学、英语的和,然后将总分大于180的加10作为cloumn_new1
df3 = df1.assign(总分=lambda x: x.语文 + x.数学 + x.英语).query('总分 > 180').assign(cloumn_new1=lambda x: x.总分 + 10)
# 可用可调用作为列值。callable 接收整个 DataFrame(这里是 x),返回可赋给列的结果
df4 = df1.assign(语文=lambda x: x['语文'] + 100)
# 使用 Series 并按索引对齐
s = pd.Series([100, 200, 300], index=[0,1,2])
df5 = df1.assign(cloumn_new1=s)
# 多列且 callable 可引用前面创建的列(从左到右求值)
df6 = df1.assign(sum=lambda x: x.语文 + x.数学 + x.英语, double_sum=lambda x: x['sum'] * 2)

10.5、insert():插入新列,修改原DataFrame

  • 特性
    • 就地修改 DataFrame(inplace):会在原 DataFrame 上插入列并返回 None(不像 assign 返回新表)。
    • 插入位置受 loc 控制(这是 insert 最大的优点:可以控制列的顺序)。
    • 默认不允许重复列名,避免意外覆盖或混淆。
    • Series 会按 index 对齐(不是简单位置对齐),这点与 df['col'] = series 相同。
    • 在大表上虽然是“就地”API,但内部可能触发内存重分配或拷贝(取决于 pandas 实现),所以仍需关注性能与内存。
df.insert(
    loc               #整数,插入列的位置索引(0 表示最左侧,len(df.columns) 表示最右侧之后)。必须在 [0, n_columns] 范围内,否则会抛 IndexError。
    column            #要插入的列名(可以是任何可哈希对象,如字符串、元组,支持 MultiIndex 列名)。
    value             #要插入的值,可以是标量(会广播)、列表/ndarray、Series 或可被转换为列的对象。
                          #如果是 Series,会按索引对齐并填入 NaN(若索引不完全匹配)。
                          #如果是可迭代且长度与行数不匹配,会抛 ValueError。
    allow_duplicates  #默认 False。若为 False 且 column 名已存在,会抛 ValueError;若设为 True,则允许插入同名列(产生重复列名)。
)

示例:

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 在最前面增加一列,并用行索引加1000作为其值
df1.insert(0, "学号", df1.index + 1000)
# 在最后面增加一列,并用常数10作为其值
df1.insert(len(df1.columns), "附加分", 10)

10.6、replace():替换匹配到的值

  • 用于把 DataFrame 或 Series 中匹配的值替换为其他值,功能比简单的赋值/map 更强,支持标量、列表、字典、正则表达式、按列映射等多种用法。
def replace(
    to_replace=None,
    value=None,
    inplace: bool = False,
    limit=None,
    regex: bool = False,
    method: str = "pad",
)
  • 参数说明
    • to_replace:要替换的值,可以是标量(如 0、'a')、list/tuple(如 [1,2])、dict(详见下)、正则表达式(当 regex=True 时)等。
    • value:替换后的值(当 to_replace 是单个值或 list 时使用)。如果 to_replace 是 dict,则通常忽略 value(在 dict 中指定每个替换对)。
    • inplace:是否就地修改(True)或返回新对象(False,默认)。
    • limit:每个列/行允许的最大替换次数(按 API 语义是每列/每匹配项的上限)。
    • regex:布尔或 dict。True 表示把字符串当正则表达式匹配;也可以对不同列使用不同的 regex(传 dict)。
    • method:在某些 pandas 版本中,支持用前值/后值填充(如 'pad'/'ffill' 或 'bfill'),用法有限且版本差异存在。

示例:

import pandas as pd
import numpy as np
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")
df1["自然"] = pd.Series(["1,001", "1,003", "1,005", "1,007"], index=[1, 3, 5, 7])

# 替换全表。把所有88替为188
df2 = df1.replace(88, 188)
# 替换全表。把 NaN 变为 0(注意:常用df.fillna(0))
df2 = df1.replace({np.nan: 0})
# 替换全表。把88或99都替成0
df2 = df1.replace([88,99], 0)
# 替换全表。把88、99分别替成188、199
df2 = df1.replace([88,99], [188,199])
# 替换全表。把88、99分别替成188、199
df2 = df1.replace({88:188, 99:199})

# 替换指定列(常用)。语文列把 88→188, 66→166;数学列把11→111)
df2 = df1.replace({'语文': {88: 188, 66: 166}, '数学': {11: 111}})

# 替换全表。使用正则,将逗号去掉
df2 = df1.replace(r"[\,]", "", regex=True)
# 替换指定列(修改原表)。使用正则,将逗号去掉
#df1["自然"].replace(r"[\,]", "", regex=True, inplace=True)

# 替换全表。使用 regex(例如把只含空白或 'n/a' 的标为 NaN)
df.replace({r'^\s*':np.nan, r'(?i)n/?a':np.nan}, regex=True)

11、分组

11.1、分组基本使用

  • 基本概念
    • 分割(split): 根据一个或多个键把数据分组。
    • 应用(apply): 对每个分组执行聚合、转换或自定义操作。
    • 合并(combine): 把每个分组的结果合并成最终的 Series/DataFrame。
    • groupby 返回的是一个 GroupBy 对象(惰性计算),真正计算发生在调用 agg/transform/apply/filter/first/last 等方法时。
  • 常见构造方式
    • df.groupby('key'),单列分组
    • df.groupby(['k1', 'k2']),多列分组
    • df.groupby(df['date'].dt.month),用表达式或 Series 分组
    • df.groupby(pd.Grouper(key='date', freq='M')),时间频率分组(按月)
    • df.groupby(level=0),对 MultiIndex 的某一层分组
    • df.groupby(['k1'], as_index=False),group keys 保持为列(默认 as_index=True)

1、基本语法

df.groupby(
    by: MultiIndex | Any | None = ...,    #用于分组的列名/函数
    axis=0,                               #0(按行分组)或 1(按列分组,较少用)。
    level: Any | None = ...,              #用于 MultiIndex 指定层次。
    as_index: Literal[True] = True,       #True(默认)把分组键作为结果索引;False 则把分组键保留为列。
    sort: bool = ...,                     #是否对组键排序(默认 True),若 False 则保留出现顺序(更快)。
    group_keys: bool = ...,               #在 apply 时是否把分组键加到结果索引/结果中(默认 True)。
    observed: bool | Any = ...,           #对分类变量分组时是否只考虑出现过的分类(默认为 False,True 可提高性能)。
    dropna: bool = ...
) -> DataFrameGroupBy[tuple, Literal[True]]

2、示例

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 按单列分组
dfg = df1.groupby("性别")
# 按多列分组
#dfg = df1.groupby(['性别','语文'])

# 查看dfg里面的数据
print(dfg.groups)
# 查看具体描述(count、mean、min等)
print(dfg.describe())
# 查看分组数量
print(dfg.ngroups)
# 只适用单列分组
print(dfg.get_group('女').mean())

11.2、分组聚合

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")
# 根据性别进行分组
dfg = df1.groupby('性别')

# (基本聚合)对表格进行聚合。获取每个分组的每列的和
df2 = dfg.agg(sum)
# (多函数聚合)对表格进行聚合。获取每个分组的,语文列的和、数学列的平均值、英语列的最大值
df3 = dfg.agg({'语文':'sum', '数学':'mean', '英语': 'max'})

# 对某列进行聚合。获取每个分组的,语文列的最大值
df4 = dfg["语文"].agg(max)
# 对某列进行聚合。获取每个分组的,语文、数学列的最大值和最小值
df5 = dfg["语文", "数学"].agg([max, min])

# 可以自定义函数
  • 常用方法
    • agg/aggregate:聚合(可接受函数名、函数列表、字典或命名聚合)。返回每组的标量或多列统计。
    • transform:对每组返回与输入同长度的结果(常用于按组计算并广播回行级别,如归一化)。
    • apply:把整个分组(DataFrame/Series)传入函数,自由度高,可返回任意形状,但常慢且要注意索引合并问题。
    • filter:按组条件保留/丢弃整组(函数接收分组 DataFrame,返回 True/False)。
    • get_group(name):返回指定组的 DataFrame。
    • groups:字典,组键→行索引列表。
    • size()/count():size 返回每组行数(计空值);count 忽略 NA。
    • nth(n)/head(n)/tail(n):组内取第 n 条或前/后 n 条记录。
    • ngroup():把组编号化(0..n-1)。
    • cumcount():组内累积计数(常用于选 top-n)。
    • rolling/expanding on groupby:groupby(...).rolling(...).mean() 等(注意索引需先 sort)。
import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")
# 根据性别进行分组
dfg = df1.groupby('性别')

# 命名聚合(pandas 0.25+ 推荐):
df2 = dfg.agg(total_val=('语文','sum'), avg_v2=('数学','mean'))
# transform 示例(组内标准化并返回与原表同形状)。语文列减其平均值,并赋值给语文_new列
df1['语文_new'] = dfg['语文'].transform(lambda x: (x - x.mean()))
# filter(只保留样本数 >= 3 的组)。返回组长度大于等于4
dfg3 = dfg.filter(lambda x: len(x) >= 4)
# apply 自定义(返回 DataFrame)。 nlargest用来快速取“值最大的前 n 个”。获取每组语文列最大的行
def top_row(g):
	return g.nlargest(1, '语文')
dfg4=dfg.apply(top_row)
#获取每组 top n(更快的做法):
#idx = dfg['语文'].idxmax()
#df1.loc[idx]
#组内排名/编号:
df1['rank_in_group'] = dfg['语文'].rank(ascending=False)
df1['cumcount'] = dfg.cumcount()

12、数据合并

12.1、垂直合并

  • 作用:
    • DataFrame.append 用来把另一个 DataFrame、Series、字典或可迭代的这些对象“追加”到当前 DataFrame 的末尾,返回一个新的 DataFrame(不就地修改)。
    • Series.append 则用于合并两个 Series,返回新的 Series。
    • 重点:append 在 pandas 1.4 开始被弃用,并在后续版本移除。推荐使用 pd.concat 或直接赋值的方式替代。
  • 注意:
    • 返回新对象:append 不会就地修改原 DataFrame,你需要把结果赋回: df = df.append(new_row, ignore_index=True)
    • 列对齐:当被追加对象的列与 df 不一致时,结果取并集,缺失处填 NaN。
    • Series 追加:如果把 Series append 到 DataFrame(作为一行),Series 的索引会被当作列名进行对齐。
    • dtype 变化:追加可能导致列 dtype 变化(例如把数值列与字符串合并会变为 object)。
    • NA/缺失:如果索引不对齐会产生 NaN。
    • 性能低:append 在循环中多次调用会导致 O(n^2) 的拷贝,效率很差。
df.append(
    other                   #可以是 DataFrame、Series、字典或这些的列表/可迭代。
    ignore_index=False      #True 则重新索引(0..n-1);False 则保留原索引(可能出现重复索引)。
    verify_integrity=False  #True 时如果结果索引有重复会抛错(代价更高)。
)

示例:

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

# 追加一行(dict)
df2 = df1.append({'姓名':'黄酒','性别':'男','语文':'100'},ignore_index=True)

# 追加一个DataFrame
df3 = df1.append(df2, ignore_index=True)
# 追加多个DataFrame
df4 = df1.append([df2,df3], ignore_index=True)

12.2、水平合并

  • DataFrame.merge是用于按一个或多个键把两个 DataFrame 进行关系型连接(类似 SQL 的 JOIN)。
  • 常见 join 类型:inner(交集)、left(左表全部)、right(右表全部)、outer(并集)、cross(笛卡尔积)。
merge(
    right: DataFrame | Series[Any],
    how: Any = "inner",
    on: Any | None = ...,
    left_on: Any | None = ...,
    right_on: Any | None = ...,
    left_index: bool = False,
    right_index: bool = False,
    sort: bool = False,
    suffixes: Any = ...,
    copy: bool = True,
    indicator: bool | str = False,
    validate: Any | None = None
) -> DataFrame
  • 参数说明:
    • left, right:左/右 DataFrame。
    • how:'left'/'right'/'inner'/'outer'/'cross'(默认为 'inner')。
    • on:在左右表中同名列作为连接键(若未指定且左右均有相同列,会默认使用这些列)。
    • left_on / right_on:分别指定左右表用于连接的列名或序列(用于列名不同时或其中一方用索引)。
    • left_index / right_index:若为 True,则使用对应表的索引(Index)作为连接键(可与 left_on/right_on 组合使用以形成多键)。
    • suffixes:当左右表存在同名非键列时,用于区分列名的后缀,如 ('_l','_r')。
    • indicator:若 True,会在结果中加入一列 _merge(或指定名字)标明行来自 left/right/both,便于调试。
    • validate:可选用于校验连接键的唯一性关系,参数值:
      • 'one_to_one' / '1:1'
      • 'one_to_many' / '1:m'
      • 'many_to_one' / 'm:1'
      • 'many_to_many' / 'm:m'(默认不校验)若校验失败会抛出异常,方便捕获逻辑错误。
    • sort:是否按连接键对结果排序(默认 False 更快)。
    • copy:是否复制数据(通常为 True)。

示例:

import pandas as pd
df1 = pd.DataFrame({
    "A":["A0","A1","A2","A3","A4"],
    "B":["B0","B1","B2","B3","B4"],
    "C":["C0","C1","C2","C3","C4"]
})

df2 = pd.DataFrame({
    "A":["A0","A1","A2","A3","A5"],
    "C":["C0","C1","C2","C3","C5"],
    "D":["A0","A1","A2","A4","A5"]
})

# 水平合并,默认是how = 'inner',做交集
df3 = pd.merge(df1, df2)
# 水平合并,做交集,并指定使用相同的列进行连接
df4 = pd.merge(df1, df2, on="A")
# 水平合并,做并集,并指定使用不同的列进行连接
df5 = pd.merge(df1, df2, how='outer', left_on="A", right_on="D")

# 水平合并,做交集,并指定使用索引进行连接
df1.set_index(keys='A')
df2.set_index(keys='D')
df6 = pd.merge(df1, df2, left_index=True,right_index=True)

12.3、按指定的轴合并

  • 沿指定轴把多个 pandas 对象(DataFrame、Series 或这些对象的列表/字典)合并起来,类似 SQL/Excel 的“合并/合并”但按轴对齐索引/列。
concat(
    objs: Iterable[NDFrame | None] | Mapping[HashableT1@concat, NDFrame | None],
    *,
    axis: Any = 0,
    join: Literal['inner', 'outer'] = "outer",
    ignore_index: bool = False,
    keys: Iterable[HashableT2@concat] | None = None,
    levels: Sequence[list[HashableT3@concat] | tuple[HashableT3@concat, ...]] | None = None,
    names: list[HashableT4@concat] | None = None,
    verify_integrity: bool = False,
    sort: bool = False,
    copy: bool = True
) -> DataFrame: ...
  • 常用参数:
    • objs:要合并的可迭代对象(list/tuple/dict)。若传 dict,键可作为 keys 用于产生 MultiIndex。
    • axis:0(按行合并/纵向,默认)或 1(按列合并/横向)。
    • join:'outer'(并集,默认,缺失处用 NaN 填充)或 'inner'(交集,只保留共有的索引/列)。
    • ignore_index:若 True,重新生成连续数值索引(0..N-1),常用于垂直合并后丢弃原索引。
    • keys:为每个被合并对象生成最外层的 MultiIndex(常用于保留来源标识或分组合并)。
    • levels/names:与 keys 一起控制生成的 MultiIndex 的层级与命名。
    • verify_integrity:若 True,会检查结果索引是否有重复(如有则抛错)。
    • sort:控制在合并时是否对非对齐的索引/列进行排序(默认为 False,更快)。在某些 pandas 版本中若合并非对齐列会有警告,建议显式指定 sort。
    • copy:是否复制数据(默认 True,通常不用改)。

示例:

import pandas as pd
df1 = pd.DataFrame({
    "A":["A0","A1","A2","A3","A4"],
    "B":["B0","B1","B2","B3","B4"],
    "C":["C0","C1","C2","C3","C4"]
})
df2 = pd.DataFrame({
    "A":["A0","A1","A2","A3","A5"],
    "C":["C0","C1","C2","C3","C5"],
    "D":["A0","A1","A2","A4","A5"]
})

# 水平合并(按 index 对齐列)
df3 = pd.concat([df1,df2], axis=1, ignore_index=True)

# 垂直合并
df4 = pd.concat([df1,df2], axis=0, ignore_index=True)
print(df3)
# 垂直合并,只保留共有列/索引(交集)
df5 = pd.concat([df1,df2], axis=0, ignore_index=True, join='inner')

# 使用 keys 生成 MultiIndex(保留来源)
df6 = pd.concat([df1,df2], keys=['df1','df2'])
# dict 作为 objs(键变为 keys):
df7 = pd.concat({'A': df1, 'B': df2})

13、长、宽格式

 

  • stack(): 把列(column-level)搬到行索引(index-level),结果通常是 Series(带 MultiIndex)。
  • unstack(): 把索引的一层搬到列上,结果通常是 DataFrame(把 index-level 变为 columns-level)。
df.stack(
    level: Any = ...,
    dropna: bool = ...,
    sort: bool = ...,
    future_stack: Literal[False] = False
) -> (DataFrame | Series[Any])


df.unstack(
    self,
    level: IndexLabel = -1,
    fill_value: int | _str | dict | None = None,
    sort: _bool = True,
) -> DataFrame: ...
  • 常用参数:
    • level(int、str 或 tuple):指定要搬动的层级(索引或列的层号或层名)。支持单层或多层。
    • dropna(仅 stack):默认 True,表示丢弃全为 NaN 的条目;设为 False 可保留含 NaN 的组合(pandas 版本差异中有变动)。
    • fill_value(unstack 的部分 pandas 版本支持):在 reshape 时用指定值填充缺失(若支持请参考你当前 pandas 版本文档)

示例:

import pandas as pd
df1 = pd.read_excel("stu_data.xlsx", engine="openpyxl")

#转化成 Series
df2 = df1.stack()
#恢复原 df
df3 = df2.unstack()

#数据转置
df4 = df1.T

14、缺失值

1、认识缺失值

import pandas as pd
import numpy as np

# 系统默认的缺失值是 None 和 np.nan
df1 = pd.DataFrame([[1,None,3],[4,5,np.nan],[7,8,9]])

# info()方法返回每一列的缺失情况
df1.info()

#如果是缺失值则返回True,如果不是缺失值返回False
df1.isna()
df1.isnull()
#如果是缺失值则返回False,如果不是缺失值返回True
df1.notna()
df1.notnull()

# any()默认axis=0,检查列,有缺失值为真
df1.isna().any(axis=0)
# all()默认axis=0,检查列,全部是缺失值为真
df1.isna().all(axis=0)

2、填充缺失值

  • fillna 用于把缺失值(NaN/None/pd.NA)替换为指定的值或用某种方法填充,常用于缺失值处理(单列、整表或按组填充)。
  • 默认不就地修改(inplace=False),会返回新对象;可用 inplace=True 就地修改(小心 chained assignment)。
df.fillna(
    value: Any | NAType | dict | Series[Any] | DataFrame | None = ...,
    *,
    axis: Any | None = ...,
    limit: int = ...,
    inplace: Literal[False] = False
) -> DataFrame
  • 常用参数:
    • alue:标量 / dict / Series / DataFrame(按列/索引对齐)。当为 dict 时,key 对应列名(axis 默认或 axis=0);当为 Series 时按索引对齐(DataFrame.fillna(df.mean()) 是常见写法)。
    • method:填充方法,'ffill'/'pad' (向前填充),'bfill'/'backfill' (向后填充)。与 value 互斥(两者不能同时使用)。
    • axis:{0/'index', 1/'columns'},按行或按列
    • limit:仅对 method 生效,表示连续 NaN 最多填充多少个(控制跨度)。
    • inplace:是否原地修改(True 就地,返回 None)。
    • downcast:尝试在填充后把数据类型向更小类型转换(通常不常用)。
import pandas as pd
import numpy as np

# 系统默认的缺失值是 None 和 np.nan
df1 = pd.DataFrame({"A":[None,2,3],"B":[4,np.nan,6],"C":[7,8,None]})

#用标量填充整列/整表
#df1["A"] = df1["A"].fillna(0)
df2 = df1.fillna(0)
#按列指定不同填充值(dict)
df3 = df1.fillna({'A': 0, 'B': 'unknown', 'C': df1['C'].mean()})
#使用前值、后值填充
df4 = df1.fillna(method="ffill").fillna(method="bfill")

3、删除缺失值

  • dropna 用于删除含有缺失值(NaN / None / NaT / pd.NA 等)的行或列。
df.dropna(
    *,
    axis: Any = ...,
    how: Any = ...,
    thresh: int | None = ...,
    subset: Any | None = ...,
    inplace: Literal[False] = False,
    ignore_index: bool = ...
) -> DataFrame: ...
  • 常用参数:
    • axis: 0 或 'index'(按行删除,默认);1 或 'columns'(按列删除)。
    • how: 'any'(默认)表示只要有任一 NAN 就删除;'all' 表示仅当全部为 NAN 时才删除。
    • thresh: 整数,保留那些至少有 thresh 个非 NAN 值的行/列(优先级高于 how)。
      • 例如 thresh=2 意味着“至少有 2 个非空值才保留”。
    • subset: 列表/索引标签,指定在判断是否删除时只考虑的那些标签。
      • 当 axis=0(按行)时,subset 指列名(只看这些列是否为 NAN来决定是否删除行)。
      • 当 axis=1 时,subset 指索引标签(只看这些索引位置是否为 NAN 来决定是否删除列)。
    • inplace: 是否就地修改(True 返回 None,False 返回新对象,默认 False)。

示例:

import pandas as pd
import numpy as np
df1 = pd.DataFrame({"A":[None,2,3,None],"B":[4,np.nan,6,None],"C":[7,8,9,None]})

#删除含任一 NAN 的行(默认):
df2 = df1.dropna()
#删除所有值都为 NAN 的行:
df3 = df1.dropna(how='all')
#删除含任一 NAN 的列:
df4 = df1.dropna(axis=1)
#删除所有值都为 NAN 的行:
df5 = df1.dropna(axis=1,how='all')
#仅根据部分列判断删除(如只要 'A' 为 NAN 就删该行):
df6 = df1.dropna(subset=['A'])
#仅当 'A' 与 'B' 都为 NAN 时才删除行:
df7 = df1.dropna(subset=['A','B'], how='all')
#保留至少两个非 NAN 的行:
df8 = df1.dropna(thresh=2)

15、重复行

  • 两个核心函数
    • duplicated(): 返回布尔 Series,标记每一行是否为重复(相对于之前出现过的行)。
    • drop_duplicates(): 删除重复行,返回去重后的 DataFrame(默认不就地)。

1、标识重复行

duplicated(
    subset: Hashable | Sequence[Hashable] | None = None,
    keep: Literal["first"] | Literal["last"] | Literal[False] = "first",
)
  • 常用参数:
    • subset:用于判重的列名列表或单列;默认为 None 表示使用所有列。
    • keep:{'first', 'last', False}
      • 'first'(默认):保留第一次出现,后续重复项标 True;
      • 'last':保留最后一次出现,前面的重复项标 True;
      • False:把组内所有重复项都标为 True(不会保留任何重复项)。

示例:

import pandas as pd
import numpy as np
df1 = pd.DataFrame({"A":[1,1,3,4,3,5],"B":[1,1,3,4,3,1],"C":[1,1,3,4,3,5],"D":[1,1,3,4,3,5]})

#标记重复行(除第一次外)
#df1['is_dup'] = df1.duplicated()
#标记重复行(除第一次外),按指定列判断重复
#df1['is_dup'] = df1.duplicated(["B","C"])

#取出重复行(含所有重复项):
df2 = df1[df1.duplicated()]
#去掉重复(保留第一次):
df3 = df1[~df1.duplicated()]

2、删除重复行

drop_duplicates(
    subset: Hashable | Iterable[Hashable] | None = ...,
    *,
    keep: Any = ...,
    inplace: Literal[False] = False,
    ignore_index: bool = ...
) -> DataFrame
  • 常用参数:
    • subset:用于判重的列名列表或单列;默认 None 表示用所有列判断重复。
    • keep:{'first', 'last', False}
    • 'first'(默认):保留第一次出现,删除后续重复行;
    • 'last':保留最后一次出现,删除前面的重复行;
    • False:删除所有出现多次的行(即完全移除重复组中所有行,仅保留出现一次的那些)。
    • inplace:是否原地修改 DataFrame(True 返回 None);推荐默认 False,然后显式赋值以避免链式赋值问题。
    • ignore_index:若 True,返回结果会重建连续整数索引(0..n-1)。
import pandas as pd
import numpy as np
df1 = pd.DataFrame({"A":[1,1,3,4,3,5],"B":[1,1,3,4,3,1],"C":[1,1,3,4,3,5],"D":[1,1,3,4,3,5]})

# 按全部列去重(保留第一次)
df2 = df1.drop_duplicates()
# 按指定列去重(只看特定列)
df3 = df1.drop_duplicates(["B","C"])
# 删除所有出现多次的行,只保留完全唯一的行
df4 = df1.drop_duplicates(keep=False) 

16、时间

16.1、Timestamp和Period

  • Timestamp:表示“时间点”(一个精确的瞬时点)。pandas.Timestamp 扩展自 Python 的 datetime,支持纳秒精度、时区(tz)、时间算术(Timedelta)等。
  • Period:表示“时间区间/周期”(一个有频率的时段),例如“2020-01”(表示 2020 年 1 月这一整个月)、“2020Q1”(第一季度)。Period 有一个频率属性 freq(如 'D','M','Q','A' 等)。

示例:

import pandas as pd
from datetime import datetime

#直接创建
time1 = pd.Timestamp('2025-01-01')
time2 = pd.Timestamp('2025-01-01 1:2:3')
time3 = pd.Timestamp (2025,1,1,1,2,3)

#使用datetime
time4 = pd.Timestamp(datetime(2025,1,1))
time5 = pd.Timestamp(datetime(2025,1,1,1,2,3))

#从字符串/数组(更常用
time6 = pd.to_datetime('2025-01-01')
time7 = pd.to_datetime(['2025-01-01','2025-02-01'])

#常用属性:.year, .month, .day, .hour, .minute, .second, .microsecond, .nanosecond
time1.year
#常用属性:.weekday(), .dayofweek, .dayofyear, .is_month_end, .is_month_start 等
time1.weekday()

#从 Unix 时间/纳秒数:
# 以秒为单位
time8 = pd.Timestamp(1609459200, unit='s')
# 以纳秒整数
time9 = pd.to_datetime(1609459200000000000)

示例:

import pandas as pd
from datetime import datetime

#创建单个 Period:
time1 = pd.Period('2025-01', freq='M')       # 2025 年 1 月(按月)
time2 = pd.Period('2025Q1', freq='Q')        # 2025 第一季度
time3 = pd.Period('2025-01-15 1:2:3', freq='D')    # 按日的周期(即那一天)
#从时间戳/字符串批量转 Period:
time4 = pd.to_datetime(['2025-01-03','2025-02-05']).to_period('M')
#生成 PeriodIndex(区间序列):
time5 = pd.period_range(start='2025-01', periods=6, freq='M')

#常用属性 / 方法(单个 Period)
time1.freq             # 频率,如 'M'
time1.start_time       # 返回该周期的起始 Timestamp(含时间)
time1.end_time         # 返回该周期的结束 Timestamp
time1.asfreq(new_freq, how='start'/'end')   # 改变频率并指定取哪端
time1.to_timestamp(how='start'/'end')       # 转为 Timestamp(周期的起点或终点)
time1 + n / time1 - n  # 按周期偏移(整数 n)
time1.ordinal          # 内部序号(整数),可用于排序或比较

#把时间戳列转换为“所属月”再 groupby:
df1 = pd.DataFrame({"date":['2025-01-01 01:01:01', '2025-01-02 01:01:01', '2025-02-01 01:01:01', '2025-02-02 01:01:01'], 'A':[1,2,3,4]})
df1['month'] = pd.to_datetime(df1['date']).dt.to_period('M')
df1.groupby('month').groups

16.2、时间转换

import pandas as pd
from datetime import datetime
df1 = pd.DataFrame({"date":['2025-01-01 01:01:01', '2025-01-02 01:01:01', '2025-02-01 01:01:01', '2025-02-02 01:01:01']})

#使用pd.Timestamp()直接转换
#转换date列的第一个
time1 = pd.Timestamp(df1['date'][0])
#转换date列所有的
df_time2 = df1['date'].apply(pd.Timestamp)

#使用to_datetime转换
#转换date列所有
df_time3 = pd.to_datetime(df1['date'])

df2 = pd.DataFrame({'Year':[2025,2026], 'Month':[1,1], 'Day':[2,2], 'Hour':[3,3]})
df_time3 = pd.to_datetime(df2[['Year', 'Month', 'Day', 'Hour']])

16.3、时间索引

1、创建时间索引

import pandas as pd
from datetime import datetime
#创建DatetimeIndex对象
index1 = pd.DatetimeIndex(['2025-01-01 01:01:01', '2025-01-02 01:01:01', '2026-02-01 01:01:01', '2026-02-02 01:01:01'])
#创建DatetimeIndex对象,指定start、end
index2 = pd.date_range('2025-01-01', '2025-01-10')
#创建DatetimeIndex对象,指定个数,间隔是天
index3 = pd.date_range('2025-01-01', periods=10)
#创建DatetimeIndex对象,指定个数,并指定间隔是小时
index4 = pd.date_range('2025-01-01', periods=10, freq='H')
#创建DatetimeIndex对象,指定个数,并指定间隔是5小时
index5 = pd.date_range('2025-01-01', periods=10, freq='5H')

#创建DatetimeIndex对象,指定个数,间隔是天,并跳过休息日
index6 = pd.bdate_range('2025-01-01', periods=10)

2、使用时间索引

import pandas as pd
from datetime import datetime
df1 = pd.DataFrame({"date":['2025-01-01 01:01:01', '2025-01-02 01:01:01', '2026-02-01 01:01:01', '2027-02-02 01:01:01'], 'A':[1,2,3,4]})
#转化时间,并创建索引
df2 = df1.set_index(df1['date'].apply(pd.Timestamp))

#索引过滤
df2['2025']
df2['2025':'2026']
df2['2025-01']
df2['2025-01':'2026-02']

index对象

import pandas as pd
# 创建index对象
ind = pd.Index([1, 2, 3, 4])
print(ind)

# 获取元素,
print(ind[0])
print(ind[-4])

print(ind[0:2])
print(ind[-4:-2])
print(ind[::2])

1

#                                                                                                                           #
posted @ 2025-12-26 10:18  麦恒  阅读(56)  评论(0)    收藏  举报