承接《Pandas 处理大文件》,本篇讲解 Pandas 和 NumPy 的协同用法:数组与DataFrame互相转换、底层ndarray访问、向量化运算、numpy函数在pandas上应用、随机模块配合、数值替换、矩阵运算、性能对比、常见坑,保持系列教程统一风格。
前置导入
import pandas as pd
import numpy as np
df = pd.DataFrame({
"a": [1, 2, 3, 4, 5],
"b": [10, 20, 30, 40, 50],
"c": [0.1, 0.2, 0.3, 0.4, 0.5]
})
print(df)
一、基础关系
Pandas 是基于 NumPy 构建的:
- Series、DataFrame 的底层存储本质就是
ndarray - NumPy 擅长:同质数组、矩阵运算、底层向量化、数值计算
- Pandas 擅长:带标签、索引、缺失值、异构表格、业务字段管理
- 配合原则:标签对齐交给Pandas,密集数值计算交给NumPy
二、DataFrame / Series 转 ndarray
3种写法,推荐优先 .to_numpy()
# 方式1:to_numpy()(官方推荐)
arr = df.to_numpy()
print(arr)
print(type(arr))
# 方式2:values 旧写法,兼容老代码,不推荐新项目
arr2 = df.values
# 单列Series转数组
arr_series = df["a"].to_numpy()
区别:
to_numpy()支持参数控制dtype,语义清晰;.values未来不再推荐。
关键参数 copy
# copy=True 返回新数组,修改数组不会影响原df
arr = df.to_numpy(copy=True)
# copy=False 视图,共享内存,修改数组会改动原数据
arr_view = df.to_numpy(copy=False)
三、ndarray 构建 DataFrame / Series
# 二维数组转DataFrame
arr = np.array([[1,2],[3,4],[5,6]])
df_new = pd.DataFrame(arr, columns=["x","y"])
# 一维数组转Series
s = pd.Series(np.array([10,20,30]), index=["p1","p2","p3"])
四、NumPy 函数直接作用于 Pandas 对象
大部分 ufunc(通用向量化函数)可以直接传入Series/DataFrame,保留索引、列名
# 平方根
df["sqrt_a"] = np.sqrt(df["a"])
# 取对数
df["log_b"] = np.log(df["b"])
# 向上取整
df["ceil_c"] = np.ceil(df["c"] * 10)
# 三角函数
df["sin_a"] = np.sin(df["a"])
对比:Pandas内置方法
.sqrt()等价,但 np 的函数体系更丰富。
np.where 条件赋值(高频)
替代多层if,向量化条件判断
# 如果a>3标记1,否则0
df["flag"] = np.where(df["a"] > 3, 1, 0)
# 多条件嵌套
df["level"] = np.where(df["b"]>30, "high", np.where(df["b"]>15, "mid", "low"))
五、NumPy 随机模块配合 Pandas
np.random 生成数组,作为新列
# 均匀随机数
df["rand"] = np.random.uniform(0,1,size=len(df))
# 正态分布
df["norm"] = np.random.normal(loc=50, scale=5, size=len(df))
# 随机选择
df["tag"] = np.random.choice(["A","B","C"], size=len(df), p=[0.5,0.3,0.2])
注意:
np.random和pd.sample的随机种子互相独立。
六、矩阵运算、聚合、统计
# 矩阵转置
arr_t = df[["a","b"]].to_numpy().T
# 点积
mat = np.array([[1],[2]])
dot = df[["a","b"]].to_numpy() @ mat
# 全局最大值、最小值
max_all = np.max(df[["a","b"]].to_numpy())
min_all = np.min(df[["a","b"]].to_numpy())
# 按行、按列聚合
col_sum = np.sum(df[["a","b"]].to_numpy(), axis=0)
row_sum = np.sum(df[["a","b"]].to_numpy(), axis=1)
提示:如果需要保留索引,优先用 Pandas 的
.sum(axis=1),numpy聚合后丢失标签信息。
七、数组索引批量筛选、替换
arr = df["a"].to_numpy()
mask = arr % 2 == 0
# 把偶数行的b列赋值为0
df.loc[mask, "b"] = 0
八、astype 类型转换,利用numpy dtype
Pandas 的 dtype 很多就是 numpy dtype:
# 转为int16、float32节省内存,和之前大文件章节呼应
df["a"] = df["a"].astype(np.int16)
df["c"] = df["c"].astype(np.float32)
九、缺失值处理的差异
重点坑:
- NumPy 的
np.nan是浮点类型;整数数组不能直接存 np.nan - Pandas 有
Int64(可空整数扩展类型),numpy原生int数组不支持NaN
# numpy int数组放nan会报错
# np.array([1,2,np.nan], dtype=int) # 报错
# pandas 支持可空整数
s = pd.Series([1,2,None], dtype="Int64")
十、向量化 vs apply(性能重点)
不要在循环、apply里写numpy计算;直接把numpy ufunc作用在整列,速度远快于逐行apply
# ✅ 推荐:向量化,底层C实现
df["a_plus"] = np.add(df["a"], df["b"])
# ❌ 不推荐:逐行apply,python循环很慢
# df["a_plus"] = df.apply(lambda row: row["a"] + row["b"], axis=1)
十一、np.unique 获取唯一值
arr = df["a"].to_numpy()
vals, counts = np.unique(arr, return_counts=True)
print("唯一值:", vals)
print("频次:", counts)
等价于 value_counts,适合拿到原始数组做科学计算。
十二、综合实战模板
def add_norm_col(df, col_name, mean=0, std=1):
"""新增正态分布随机列"""
df[col_name] = np.random.normal(loc=mean, scale=std, size=len(df))
return df
def np_where_label(df, cond_col, threshold, label_gt, label_le):
"""np.where打标签"""
df["label"] = np.where(df[cond_col] > threshold, label_gt, label_le)
return df
df = add_norm_col(df, "score", mean=60, std=10)
df = np_where_label(df, "score", 60, "pass", "fail")
print(df)
十三、高频坑清单
- 转ndarray之后丢失索引、列名,计算完想合并回原表要自己对齐
- 视图模式(copy=False)修改数组,原DataFrame同步变化,容易误改数据
- 原生numpy int数组无法存放NaN,必须用Pandas的Int64扩展类型
- 混合类型DataFrame(同时有字符串+数字)转ndarray后全部变成object类型,数值运算失效
- 不要用numpy的逻辑判断直接和Pandas布尔索引混用,优先保证索引对齐
- np.random.seed是全局种子,会影响全局所有numpy随机操作
- 大表to_numpy()会一次性把全部数据载入内存,和分块读取思路冲突
十四、速查表
| 需求 | 写法 |
|---|---|
| DataFrame转数组 | df.to_numpy() |
| 数组转DataFrame | pd.DataFrame(arr, columns=[]) |
| 向量化条件分支 | np.where(cond, val1, val2) |
| 向量化数学函数 | np.sqrt / np.log / np.sin |
| 生成随机数组作为列 | np.random.normal / uniform / choice |
| 统计唯一值+频次 | np.unique(arr, return_counts=True) |
| 节省内存类型 | astype(np.int16) / np.float32 |
0 条笔记