自学教程 学编程从这里开始

Pandas 与 NumPy 结合使用

承接《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)

十三、高频坑清单

  1. 转ndarray之后丢失索引、列名,计算完想合并回原表要自己对齐
  2. 视图模式(copy=False)修改数组,原DataFrame同步变化,容易误改数据
  3. 原生numpy int数组无法存放NaN,必须用Pandas的Int64扩展类型
  4. 混合类型DataFrame(同时有字符串+数字)转ndarray后全部变成object类型,数值运算失效
  5. 不要用numpy的逻辑判断直接和Pandas布尔索引混用,优先保证索引对齐
  6. np.random.seed是全局种子,会影响全局所有numpy随机操作
  7. 大表to_numpy()会一次性把全部数据载入内存,和分块读取思路冲突

十四、速查表

需求写法
DataFrame转数组df.to_numpy()
数组转DataFramepd.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 条笔记