跳转至

NumPy 与 Pandas 速查

Python 数据处理的两块地基:NumPy 是 C 速度的多维数组(PyTorch 张量的直系亲戚,见PyTorch 实践),Pandas 是带标签的表格层。核心理念一句话:把循环换成向量化操作。环境见Python 环境与包管理。

NumPy:ndarray 基础

import numpy as np

a = np.array([1, 2, 3, 4])            # dtype=int64
f = np.zeros((3, 4))                  # 全 0 矩阵;ones/empty/full 类似
r = np.arange(0, 10, 2)               # [0 2 4 6 8];linspace(0, 1, 11) 等分
rng = np.random.default_rng(42)       # 新版随机 API(别再用 np.random.seed 那套)
x = rng.normal(size=(1000, 3))

a * 2 + 1                             # 整个数组运算——没有循环
f.shape, f.dtype, f.ndim              # (3, 4), float64, 2
m = f.reshape(4, 3)                   # 改形状(不拷贝,视图)
t = f.T                               # 转置;f.T @ f 即线性代数

广播规则:从末尾维度向前对齐,两边维度相等或有一方为 1 即可:

A = np.ones((4, 3))
b = np.array([1, 2, 3])        # shape (3,)
A + b                          # (4,3) + (3,) → b 广播到每行 ✅
A + np.array([[1],[2],[3],[4]])   # (4,3) + (4,1) → 按列广播 ✅
A + np.array([1, 2])           # ❌ (3,) 与 (4,) 末维对不上 → ValueError

索引与聚合:axis 心智

x = np.array([[1, 2, 3],
              [4, 5, 6]])

x[1, 2]                        # 6(行, 列)
x[:, 0]                        # 第一列
x[x > 3]                       # 布尔掩码:[4 5 6]——过滤标配
x[(x > 2) & (x < 6)]           # 条件组合用 & | ~,【不要】用 and/or

x.sum(axis=0)                  # 沿行方向压 → 每列的和 [5 7 9]
x.sum(axis=1)                  # 沿列方向压 → 每行的和 [6 15]
x.argmax(axis=1)               # 每行最大值的位置
np.where(x > 3, 1, -1)         # 向量化的 if-else
np.clip(x, 2, 4)               # 截断

axis 记法:axis=0 是"把这个维度压扁"。三维数组 x.sum(axis=0) 后第 0 维消失。

向量化:为什么快

n = 1_000_000
py_list = list(range(n))
arr = np.arange(n)

%timeit [v * 2 + 1 for v in py_list]      # ~50ms(Python 循环 + 对象开销)
%timeit arr * 2 + 1                       # ~1ms(连续内存 + SIMD,无对象)

规则:循环里能写成数组运算就别写 for;实在要循环的分支逻辑用 np.where/掩码表达。C++ 心智对应:ndarray ≈ std::vector<T> 的连续内存 + 整块运算,元素是裸标量而不是 PyObject——这就是 50 倍差距的来源。

Pandas:DataFrame 常用操作

import pandas as pd

df = pd.read_csv("data.csv", parse_dates=["ts"])     # 读表
df = pd.DataFrame({
    "user": ["ada", "bob", "ada", "eve"],
    "score": [90, 75, 85, 92],
    "dept": ["sec", "dev", "sec", "ops"],
})

df.head() / df.shape / df.dtypes / df.describe()     # 先看四件套
df["score"]                       # 选一列 → Series
df[["user", "score"]]             # 选多列 → DataFrame
df.loc[df["score"] >= 85]         # 条件过滤行
df.loc[df["score"] >= 85, "user"] # 行 + 列组合
df.iloc[0, 1]                     # 按位置:第 0 行第 1 列
df.sort_values("score", ascending=False)
df.assign(bonus=df["score"] * 0.1)                   # 生成新列(不改原表)

分组聚合(groupby 是灵魂)

df.groupby("dept")["score"].mean()                    # 每部门平均分
df.groupby("dept").agg(
    avg=("score", "mean"),
    best=("score", "max"),
    n=("user", "count"),
)                                                     # 命名聚合,输出即新表
df.value_counts("dept")                               # 频次统计

表连接与缺失值

dept_info = pd.DataFrame({"dept": ["dev", "ops"], "floor": [3, 5]})
df.merge(dept_info, on="dept", how="left")            # SQL 风格 join

df.isna().sum()                   # 每列缺失数
df.fillna({"score": 0})           # 填充
df.dropna(subset=["user"])        # 丢缺失行
df.drop_duplicates()              # 去重

常见坑

坑 说明
链式赋值不生效 df[df.a>1]["b"] = 0 改的是副本!用 df.loc[mask, "b"] = 0
iterrows() 慢一个量级 行循环是 Python 级——改向量化,或 itertuples() 折衷
dtype=object 列 字符串列其实是 Python 对象指针,运算不享受向量化
SettingWithCopyWarning 视图 vs 副本模糊;先 .copy() 再改,或一步 loc
广播对不上 shape 末尾对齐规则;reshape(-1, 1) 手动补维度
大表 merge 后行数暴涨 join 键有重复 → 笛卡尔积;merge 后先 len 对账
inplace=True 多数场景是伪就地,链式里更危险——新式写法基本不用它

与本站其他文章的关系

  • ndarray ↔ PyTorch 张量:torch.from_numpy(arr) 零拷贝互转,操作语义几乎一致,上 GPU 才是分水岭(见PyTorch 实践);
  • 本篇示例在 numpy 2.3 / pandas 2.3 上全量跑通;写完的处理脚本配测试与调试的 pytest 钉住边界数据。

评论