Pandas
pandas的一些基础语法
Pandas 基本语法总结
Pandas 是 Python 中常用的数据分析库,主要用于处理表格数据。
Pandas 中最重要的两种数据结构是:
1
2
Series:一维带标签的数据
DataFrame:二维表格数据
在机器学习中,Pandas 经常用于读取数据、查看数据、筛选样本、处理缺失值、构造特征以及划分特征和标签。
一般导入方式为:
1
import pandas as pd
这里的 pd 是 pandas 的简写,之后可以通过 pd.xxx() 调用 Pandas 中的函数。
通常也会同时导入 NumPy:
1
2
import numpy as np
import pandas as pd
创建 Series
Series 可以理解为一列带有索引的数据。
从列表创建 Series
1
2
s = pd.Series([80, 90, 75])
print(s)
输出:
1
2
3
4
0 80
1 90
2 75
dtype: int64
左边是索引,右边是数据。
指定索引
1
2
3
4
5
6
s = pd.Series(
[80, 90, 75],
index=["张三", "李四", "王五"]
)
print(s)
输出:
1
2
3
4
张三 80
李四 90
王五 75
dtype: int64
通过索引可以取得对应的数据:
1
s["李四"]
结果为:
1
90
创建 DataFrame
DataFrame 可以理解为一张带有行索引和列名的二维表格。
从字典创建 DataFrame
1
2
3
4
5
6
7
8
9
data = {
"name": ["张三", "李四", "王五", "赵六"],
"age": [18, 20, 19, 21],
"score": [85.0, 92.0, 76.0, 88.0],
"city": ["北京", "上海", "北京", "广州"]
}
df = pd.DataFrame(data)
print(df)
输出:
1
2
3
4
5
name age score city
0 张三 18 85.0 北京
1 李四 20 92.0 上海
2 王五 19 76.0 北京
3 赵六 21 88.0 广州
其中:
1
2
0、1、2、3 → 行索引
name、age、score、city → 列名
从二维列表创建 DataFrame
1
2
3
4
5
6
7
8
df = pd.DataFrame(
[
["张三", 18, 85.0],
["李四", 20, 92.0],
["王五", 19, 76.0]
],
columns=["name", "age", "score"]
)
columns 用于指定每一列的列名。
查看数据
查看前几行
1
df.head()
默认查看前 5 行。
查看前 3 行:
1
df.head(3)
查看后几行
1
df.tail()
默认查看最后 5 行。
查看最后 2 行:
1
df.tail(2)
查看形状
1
df.shape
如果结果为:
1
(1000, 20)
表示:
1
2
1000 行数据
20 列数据
通常一行代表一个样本,一列代表一个特征或标签。
查看列名和索引
1
2
df.columns
df.index
把所有列名转换成普通列表:
1
df.columns.tolist()
查看数据类型
1
df.dtypes
可能得到:
1
2
3
4
5
name object
age int64
score float64
city object
dtype: object
常见的数据类型包括:
1
2
3
4
5
6
int64 整数
float64 浮点数
object 字符串或混合类型
bool 布尔值
datetime64 日期时间
category 分类数据
查看整体信息
1
df.info()
info() 可以查看:
1
2
3
4
5
数据行数和列数
每一列的名称
每一列的非空数量
每一列的数据类型
占用的内存
它是拿到一个新数据集后最常用的检查方法之一。
查看统计信息
1
df.describe()
默认统计数值列,并给出:
1
2
3
4
5
6
7
8
count 非空数据数量
mean 平均值
std 标准差
min 最小值
25% 下四分位数
50% 中位数
75% 上四分位数
max 最大值
如果希望同时查看字符串列:
1
df.describe(include="all")
选择列
选择一列
1
df["score"]
结果是一个 Series。
也可以写成:
1
df.score
但是更推荐使用:
1
df["score"]
因为这种写法不受空格、特殊字符和方法重名的影响。
选择多列
1
df[["name", "score"]]
注意需要使用两层中括号:
1
2
外层中括号:从 DataFrame 中选择数据
内层中括号:列名组成的列表
结果仍然是一个 DataFrame。
使用 loc 选择数据
loc 根据行标签和列名选择数据。
基本格式为:
1
df.loc[行, 列]
选择一行
1
df.loc[0]
表示选择索引标签为 0 的行。
选择指定行和列
1
df.loc[0, "name"]
结果为:
1
张三
选择多行多列:
1
df.loc[[0, 2], ["name", "score"]]
使用标签切片
1
df.loc[0:2, "name":"score"]
需要注意,loc 的标签切片通常包含结束位置。
因此:
1
df.loc[0:2]
会选择索引标签 0、1、2 对应的三行。
使用 iloc 选择数据
iloc 根据整数位置选择数据,使用方式和 NumPy 索引类似。
基本格式为:
1
df.iloc[行位置, 列位置]
选择一个元素
1
df.iloc[0, 2]
表示选择第 1 行、第 3 列的数据。
Python 的位置索引从 0 开始。
使用位置切片
1
df.iloc[:3, :2]
表示:
1
2
选择前 3 行
选择前 2 列
与普通 Python 切片相同,iloc 切片不包含结束位置。
条件筛选
单个条件
筛选成绩不低于 85 分的数据:
1
df[df["score"] >= 85]
其中:
1
df["score"] >= 85
会生成一个布尔值 Series:
1
2
3
4
5
0 True
1 True
2 False
3 True
Name: score, dtype: bool
再用这个布尔值 Series 从原表中选择满足条件的行。
多个条件
筛选年龄不低于 19 岁并且成绩不低于 85 分的数据:
1
df[(df["age"] >= 19) & (df["score"] >= 85)]
筛选来自北京或者上海的数据:
1
df[(df["city"] == "北京") | (df["city"] == "上海")]
其中:
1
2
3
& 并且
| 或者
~ 取反
每个条件都需要使用小括号括起来。
不能直接使用 Python 的 and 和 or 连接两个 Series 条件。
isin
筛选某一列属于多个指定值的数据:
1
df[df["city"].isin(["北京", "上海"])]
排除这些值:
1
df[~df["city"].isin(["北京", "上海"])]
between
筛选成绩在 80 到 90 分之间的数据:
1
df[df["score"].between(80, 90)]
默认包含 80 和 90。
query
也可以使用字符串表达式筛选:
1
df.query("age >= 19 and score >= 85")
如果需要引用 Python 变量,可以在变量名前加 @:
1
2
min_score = 85
df.query("score >= @min_score")
修改和新增列
修改一列
1
df["score"] = df["score"] + 5
这会给 score 列的每一个值加 5。
新增一列
1
df["passed"] = df["score"] >= 60
结果中的 passed 列为布尔值:
1
2
True 及格
False 不及格
根据现有列计算新特征:
1
df["score_ratio"] = df["score"] / 100
使用 assign
assign() 会返回一个新的 DataFrame:
1
2
3
4
new_df = df.assign(
passed=df["score"] >= 60,
score_ratio=df["score"] / 100
)
原来的 df 不会被修改。
根据条件赋值
把不及格成绩统一改为 60:
1
df.loc[df["score"] < 60, "score"] = 60
推荐使用 loc 同时指定行和列,避免链式赋值问题。
删除数据
删除列
1
df = df.drop(columns=["city"])
删除多列:
1
df = df.drop(columns=["city", "age"])
删除行
根据索引标签删除行:
1
df = df.drop(index=[0, 2])
原地修改
部分 Pandas 方法支持:
1
df.drop(columns=["city"], inplace=True)
inplace=True 表示直接修改原对象。
为了让数据处理步骤更清晰,也可以不使用 inplace,而是把结果重新赋值:
1
df = df.drop(columns=["city"])
处理缺失值
缺失值通常显示为:
1
2
3
NaN
None
NaT
检查缺失值
1
df.isna()
统计每一列的缺失值数量:
1
df.isna().sum()
统计整个表的缺失值总数:
1
df.isna().sum().sum()
isnull() 和 isna() 的作用相同:
1
df.isnull()
删除缺失值
删除含有缺失值的行:
1
df = df.dropna()
只在指定列缺失时删除该行:
1
df = df.dropna(subset=["score"])
删除所有值都缺失的行:
1
df = df.dropna(how="all")
填充缺失值
使用固定值填充:
1
df["city"] = df["city"].fillna("未知")
使用平均值填充数值列:
1
df["score"] = df["score"].fillna(df["score"].mean())
使用中位数填充:
1
df["age"] = df["age"].fillna(df["age"].median())
在存在极端值时,中位数通常比平均值更加稳定。
使用前一个有效值填充:
1
df["score"] = df["score"].ffill()
使用后一个有效值填充:
1
df["score"] = df["score"].bfill()
处理重复值
检查重复行
1
df.duplicated()
统计重复行数量:
1
df.duplicated().sum()
删除重复行
1
df = df.drop_duplicates()
根据指定列判断重复:
1
df = df.drop_duplicates(subset=["name"])
保留最后一次出现的数据:
1
df = df.drop_duplicates(subset=["name"], keep="last")
修改数据类型
使用 astype
1
2
3
df["age"] = df["age"].astype(int)
df["score"] = df["score"].astype(float)
df["city"] = df["city"].astype("category")
如果数据中存在无法转换的字符串,直接使用 astype 会报错。
使用 to_numeric
1
df["score"] = pd.to_numeric(df["score"], errors="coerce")
其中:
1
errors="coerce"
表示把无法转换为数值的数据变成 NaN,之后可以再统一处理缺失值。
字符串处理
Pandas 可以通过 .str 对一列字符串进行处理。
去除字符串两端的空格:
1
df["name"] = df["name"].str.strip()
转换为小写:
1
df["city"] = df["city"].str.lower()
判断是否包含指定文字:
1
df[df["name"].str.contains("张", na=False)]
替换字符串:
1
df["city"] = df["city"].str.replace("市", "", regex=False)
拆分字符串:
1
2
3
4
5
df[["province", "city"]] = df["address"].str.split(
"-",
n=1,
expand=True
)
日期时间处理
把字符串转换为日期时间:
1
df["date"] = pd.to_datetime(df["date"])
如果可能存在错误数据:
1
df["date"] = pd.to_datetime(df["date"], errors="coerce")
提取日期信息:
1
2
3
4
df["year"] = df["date"].dt.year
df["month"] = df["date"].dt.month
df["day"] = df["date"].dt.day
df["weekday"] = df["date"].dt.dayofweek
其中:
1
2
dayofweek 为 0 表示星期一
dayofweek 为 6 表示星期日
计算两个日期之间的时间差:
1
df["days"] = (df["end_date"] - df["start_date"]).dt.days
排序
按值排序
按照成绩从小到大排序:
1
df.sort_values(by="score")
按照成绩从大到小排序:
1
df.sort_values(by="score", ascending=False)
按照多列排序:
1
2
3
4
df.sort_values(
by=["city", "score"],
ascending=[True, False]
)
表示先按照城市升序排列,同一城市内再按照成绩降序排列。
按索引排序
1
df.sort_index()
降序排列:
1
df.sort_index(ascending=False)
索引操作
设置索引
1
df = df.set_index("name")
此时 name 列会变成行索引。
保留原来的 name 列:
1
df = df.set_index("name", drop=False)
恢复默认索引
1
df = df.reset_index()
如果不需要保留原索引:
1
df = df.reset_index(drop=True)
经过筛选或删除行以后,经常使用:
1
df = df.reset_index(drop=True)
重新生成从 0 开始的连续索引。
常用统计方法
1
2
3
4
5
6
7
8
df["score"].sum() # 求和
df["score"].mean() # 平均值
df["score"].median() # 中位数
df["score"].std() # 标准差
df["score"].min() # 最小值
df["score"].max() # 最大值
df["score"].count() # 非空值数量
df["score"].nunique() # 不同值的数量
查找最大值所在的索引:
1
df["score"].idxmax()
查找最小值所在的索引:
1
df["score"].idxmin()
value_counts
统计每一个值出现的次数:
1
df["city"].value_counts()
统计比例:
1
df["city"].value_counts(normalize=True)
同时统计缺失值:
1
df["city"].value_counts(dropna=False)
相关系数
计算数值列之间的相关系数:
1
df.corr(numeric_only=True)
只查看某一列与其他列的相关系数:
1
df.corr(numeric_only=True)["score"]
相关系数接近 1 表示强正相关,接近 -1 表示强负相关,接近 0 表示线性相关性较弱。
map 和 apply
使用 map 替换一列中的值
1
2
3
4
5
6
7
city_code = {
"北京": 0,
"上海": 1,
"广州": 2
}
df["city_code"] = df["city"].map(city_code)
map() 常用于对 Series 中的每一个元素进行映射。
如果原数据中存在字典没有包含的值,映射结果会是 NaN。
使用 apply 调用函数
1
2
3
4
5
6
7
8
9
def score_level(score):
if score >= 90:
return "优秀"
elif score >= 60:
return "及格"
else:
return "不及格"
df["level"] = df["score"].apply(score_level)
也可以使用匿名函数:
1
df["score_ratio"] = df["score"].apply(lambda x: x / 100)
简单的数值运算应优先使用向量化写法:
1
df["score_ratio"] = df["score"] / 100
这种写法通常比 apply() 更简洁、速度也更快。
分组统计
groupby() 用于按照某一列或多列对数据进行分组。
按一列分组
计算每个城市的平均成绩:
1
df.groupby("city")["score"].mean()
如果希望结果仍然是 DataFrame:
1
df.groupby("city", as_index=False)["score"].mean()
同时计算多个统计量
1
2
3
result = df.groupby("city")["score"].agg(
["count", "mean", "max", "min"]
)
使用更清楚的列名:
1
2
3
4
5
result = df.groupby("city").agg(
student_count=("name", "count"),
average_score=("score", "mean"),
highest_score=("score", "max")
).reset_index()
按多列分组
1
df.groupby(["city", "gender"])["score"].mean()
表示先按照城市分组,再在每个城市中按照性别分组。
使用 transform
为每一行添加所在城市的平均成绩:
1
df["city_mean"] = df.groupby("city")["score"].transform("mean")
agg() 通常会减少数据行数,transform() 返回的结果则与原数据行数相同,因此可以直接作为新列加入原表。
合并数据
concat
按行拼接两个结构相同的表:
1
2
3
4
5
all_data = pd.concat(
[train_df, test_df],
axis=0,
ignore_index=True
)
其中:
1
2
axis=0 按行拼接
ignore_index=True 重新生成连续索引
按列拼接:
1
new_df = pd.concat([feature_df, label_df], axis=1)
merge
假设有两张表:
1
2
3
4
5
6
7
8
9
students = pd.DataFrame({
"student_id": [1, 2, 3],
"name": ["张三", "李四", "王五"]
})
scores = pd.DataFrame({
"student_id": [1, 2, 4],
"score": [85, 92, 78]
})
按照 student_id 合并:
1
2
3
4
5
6
result = pd.merge(
students,
scores,
on="student_id",
how="inner"
)
how 的常见取值:
1
2
3
4
inner 只保留两个表中都存在的键
left 保留左表中的所有键
right 保留右表中的所有键
outer 保留两个表中的所有键
左连接:
1
2
3
4
5
6
result = pd.merge(
students,
scores,
on="student_id",
how="left"
)
如果两个表用于连接的列名不同:
1
2
3
4
5
6
7
result = pd.merge(
left_df,
right_df,
left_on="student_id",
right_on="id",
how="left"
)
数据透视表
pivot_table
按照城市统计不同性别的平均成绩:
1
2
3
4
5
6
7
table = pd.pivot_table(
df,
values="score",
index="city",
columns="gender",
aggfunc="mean"
)
其中:
1
2
3
4
values 需要统计的数值
index 行方向的分组字段
columns 列方向的分组字段
aggfunc 聚合方法
crosstab
统计两个分类变量的组合次数:
1
pd.crosstab(df["city"], df["gender"])
计算每一行的比例:
1
2
3
4
5
pd.crosstab(
df["city"],
df["gender"],
normalize="index"
)
读取文件
读取 CSV 文件
1
df = pd.read_csv("data.csv")
常用参数:
1
2
3
4
5
6
df = pd.read_csv(
"data.csv",
encoding="utf-8",
usecols=["name", "age", "score"],
na_values=["?", "unknown"]
)
其中:
1
2
3
encoding 文件编码
usecols 只读取指定列
na_values 把指定内容识别为缺失值
如果 CSV 文件没有表头:
1
2
3
4
5
df = pd.read_csv(
"data.csv",
header=None,
names=["feature_1", "feature_2", "label"]
)
读取大型文件时,可以分块读取:
1
2
for chunk in pd.read_csv("large_data.csv", chunksize=10000):
print(chunk.shape)
读取 Excel 文件
1
df = pd.read_excel("data.xlsx")
读取指定工作表:
1
2
3
4
df = pd.read_excel(
"data.xlsx",
sheet_name="Sheet1"
)
读取其他常见格式
1
2
df = pd.read_json("data.json")
df = pd.read_parquet("data.parquet")
Parquet 通常比 CSV 更节省空间,并且能够保存列的数据类型,适合存储较大的表格数据。
保存文件
保存为 CSV:
1
2
3
4
5
df.to_csv(
"result.csv",
index=False,
encoding="utf-8-sig"
)
其中:
1
2
index=False 不保存行索引
encoding="utf-8-sig" 方便使用 Excel 打开中文 CSV
保存为 Excel:
1
df.to_excel("result.xlsx", index=False)
保存为 Parquet:
1
df.to_parquet("result.parquet", index=False)
Pandas 与 NumPy 相互转换
DataFrame 转换为 NumPy 数组
1
array = df.to_numpy()
选择特征列后再转换:
1
x = df[["age", "score"]].to_numpy()
选择标签列:
1
y = df["passed"].to_numpy()
NumPy 数组转换为 DataFrame
1
2
3
4
5
6
7
8
9
10
array = np.array([
[18, 85],
[20, 92],
[19, 76]
])
df = pd.DataFrame(
array,
columns=["age", "score"]
)
在机器学习中准备特征和标签
假设数据中 target 是需要预测的标签,其余列是输入特征:
1
2
x = df.drop(columns=["target"])
y = df["target"]
这里:
1
2
x 特征
y 标签
如果模型只需要指定的几个特征:
1
2
3
4
feature_columns = ["age", "income", "score"]
x = df[feature_columns]
y = df["target"]
划分训练集和测试集:
1
2
3
4
5
6
7
8
9
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(
x,
y,
test_size=0.2,
random_state=42,
stratify=y
)
其中:
1
2
3
test_size=0.2 20% 的数据作为测试集
random_state=42 固定随机结果,便于复现实验
stratify=y 让训练集和测试集中的类别比例接近原数据
stratify=y 一般用于分类问题,回归问题通常不使用。
分类变量编码
使用 map 编码二分类
1
2
3
4
df["gender_code"] = df["gender"].map({
"男": 0,
"女": 1
})
使用 get_dummies 独热编码
1
2
3
4
5
encoded_df = pd.get_dummies(
df,
columns=["city"],
dtype=int
)
假设 city 中有北京、上海和广州,编码后可能得到:
1
2
3
city_北京
city_上海
city_广州
每一列的值为 0 或 1。
如果希望少保留一列:
1
2
3
4
5
6
encoded_df = pd.get_dummies(
df,
columns=["city"],
drop_first=True,
dtype=int
)
实际机器学习项目中,应避免分别对训练集和测试集独立编码,否则两边可能出现不同的列。更完整的项目通常使用 Scikit-learn 的预处理流水线统一处理。
随机抽样和打乱数据
随机抽取 3 行:
1
df.sample(n=3, random_state=42)
随机抽取 20% 的数据:
1
df.sample(frac=0.2, random_state=42)
随机打乱所有行并重新设置索引:
1
2
3
4
df = df.sample(
frac=1,
random_state=42
).reset_index(drop=True)
避免链式赋值
下面的写法可能产生警告,并且修改结果不一定符合预期:
1
df[df["score"] < 60]["score"] = 60
推荐使用:
1
df.loc[df["score"] < 60, "score"] = 60
如果需要得到一份独立的筛选结果并继续修改,可以显式复制:
1
2
high_score_df = df[df["score"] >= 85].copy()
high_score_df["level"] = "优秀"
一个完整的数据处理例子
假设原始文件 students.csv 中包含:
1
2
3
4
5
6
name,age,score,city,passed
张三,18,85,北京,1
李四,20,92,上海,1
王五,19,,北京,0
赵六,21,88,广州,1
赵六,21,88,广州,1
完整处理过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import pandas as pd
from sklearn.model_selection import train_test_split
# 1. 读取数据
df = pd.read_csv("students.csv")
# 2. 查看数据基本情况
print(df.head())
print(df.shape)
df.info()
print(df.isna().sum())
# 3. 删除重复行
df = df.drop_duplicates().reset_index(drop=True)
# 4. 转换数据类型
df["age"] = pd.to_numeric(df["age"], errors="coerce")
df["score"] = pd.to_numeric(df["score"], errors="coerce")
# 5. 处理缺失值
df["age"] = df["age"].fillna(df["age"].median())
df["score"] = df["score"].fillna(df["score"].median())
df["city"] = df["city"].fillna("未知")
# 6. 构造新特征
df["score_ratio"] = df["score"] / 100
# 7. 对分类变量进行独热编码
df = pd.get_dummies(
df,
columns=["city"],
dtype=int
)
# 8. 选择特征和标签
x = df.drop(columns=["name", "passed"])
y = df["passed"]
# 9. 划分训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(
x,
y,
test_size=0.2,
random_state=42
)
print(x_train.shape)
print(x_test.shape)
这个例子完成了:
1
2
3
4
5
6
7
8
9
1. 读取 CSV 文件
2. 查看数据基本信息
3. 删除重复数据
4. 转换数据类型
5. 填充缺失值
6. 构造新特征
7. 编码分类变量
8. 分离特征和标签
9. 划分训练集和测试集
常见注意事项
Pandas 操作通常按照索引对齐
1
2
3
4
s1 = pd.Series([10, 20], index=["a", "b"])
s2 = pd.Series([1, 2], index=["b", "a"])
print(s1 + s2)
结果为:
1
2
3
a 12
b 21
dtype: int64
Pandas 会按照索引标签对齐后再计算,而不是简单地按照位置计算。
count 和 size 的区别
1
df["score"].count()
只统计非空值数量。
1
df["score"].size
统计所有元素数量,包括缺失值。
筛选后索引可能不连续
1
result = df[df["score"] >= 85]
筛选结果会保留原来的索引。
如果需要连续索引:
1
result = result.reset_index(drop=True)
不要默认修改原数据
很多 Pandas 方法会返回一个新对象,而不会直接修改原数据:
1
2
3
df.dropna()
df.sort_values("score")
df.reset_index()
因此通常需要重新赋值:
1
2
3
df = df.dropna()
df = df.sort_values("score")
df = df.reset_index(drop=True)
总结
Pandas 的核心作用是高效处理带有行列标签的表格数据。
在机器学习项目中,Pandas 主要完成以下任务:
1
2
3
4
5
6
7
8
9
10
11
12
1. 从 CSV、Excel 等文件中读取数据
2. 查看数据的形状、类型和统计信息
3. 选择需要的行和列
4. 根据条件筛选样本
5. 处理缺失值和重复值
6. 转换数据类型
7. 构造新的特征
8. 分组统计和合并表格
9. 编码分类变量
10. 分离特征和标签
11. 把数据转换为 NumPy 数组
12. 保存处理后的结果
对于机器学习入门来说,最常用的 Pandas 语法包括:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
pd.Series()
pd.DataFrame()
pd.read_csv()
pd.read_excel()
pd.to_datetime()
pd.to_numeric()
pd.get_dummies()
pd.concat()
pd.merge()
df.head()
df.info()
df.describe()
df.loc[]
df.iloc[]
df.isna().sum()
df.fillna()
df.dropna()
df.drop_duplicates()
df.sort_values()
df.groupby()
df.value_counts()
df.reset_index()
df.to_numpy()
df.to_csv()