R / Richie全部文章 ↑

Python · 3 分钟阅读

Pandas 基础操作

2026 年视角:Pandas 仍是数据分析师日常的主力(数据清洗、Excel/CSV 交互、ETL)。pyarrow 后端默认开启、Polars 在性能敏感场景是强力替代、2.x 引入的可空 dtype(Int64、string)已稳定。本篇用 2.2+ 风格演示。

准备

pip install "pandas[pyarrow]" openpyxl
import pandas as pd
print(pd.__version__)        # 2.2+
pd.options.future_info       # 2.x 引入,提醒你哪些 future warnings

核心对象

# Series:带标签的一维数组
s = pd.Series([1, 2, 3], index=["a", "b", "c"])

# DataFrame:表格
df = pd.DataFrame(
    {
        "name": ["Alice", "Bob", "Carol"],
        "age":  [30, 25, 40],
        "city": ["BJ", "SH", "GZ"],
    }
)

读 / 写文件

格式 读 写
CSV pd.read_csv(...) df.to_csv(..., index=False)
Excel pd.read_excel(...) df.to_excel(..., index=False)
Parquet pd.read_parquet(...) df.to_parquet(...)
JSON pd.read_json(...) df.to_json(..., orient="records")
SQL pd.read_sql(...) df.to_sql(...)
# 常用参数
df = pd.read_csv(
    "data.csv",
    dtype_backend="pyarrow",   # 列类型交给 pyarrow
    parse_dates=["created_at"],
    na_values=["-", "N/A"],
)

df.to_parquet("data.parquet", engine="pyarrow")

大量数据落地用 Parquet:列式压缩、保留类型、比 CSV 小一个数量级。

速览 DataFrame

df.head(5)              # 前 5 行
df.tail(3)              # 后 3 行
df.info()               # 字段 / 类型 / 缺失
df.describe()           # 数值列统计
df.shape                # (行数, 列数)
df.dtypes               # 每列类型
df.columns              # 列名
df.index                # 索引

选择数据

列

df["name"]                      # 单列(Series)
df[["name", "age"]]             # 多列(DataFrame)

行:loc / iloc

df.loc[0]                       # 按标签(默认整数索引=位置)
df.loc[0:2, ["name", "age"]]    # 行 + 列
df.loc[df["age"] > 30, "name"]  # 条件 + 单列

df.iloc[0]                      # 按位置
df.iloc[0:3, 0:2]               # 前 3 行前 2 列

条件过滤

df[df["age"] > 30]
df.query("age > 30 and city == 'BJ'")
df.query("age > @threshold")     # 用 @ 引用外部变量

修改

# 新列
df["is_adult"] = df["age"] >= 18

# 改类型
df["age"] = df["age"].astype("Int64")   # 2.x 可空整数

# 改名
df = df.rename(columns={"name": "full_name"})

# 排序
df.sort_values("age", ascending=False)

astype("Int64")(大写 I)是可空整型;int64(小写)不允许 NaN。

缺失值

df.isna()                       # 布尔矩阵
df.isna().sum()                 # 每列缺失数
df.dropna()                     # 丢缺失行
df.fillna(0)                    # 用 0 填
df.fillna({"age": df["age"].median(), "city": "UNKNOWN"})

聚合

df.groupby("city")["age"].mean()
df.groupby("city").agg(
    avg_age=("age", "mean"),
    n=("age", "count"),
)
df.value_counts("city")         # 频次表
df["age"].agg(["min", "max", "mean", "std"])

合并

# 纵向
pd.concat([df1, df2], ignore_index=True)

# 横向(SQL 风格 join)
pd.merge(df1, df2, on="id", how="left")     # left/right/inner/outer

应用函数

# 列上:lambda 或函数
df["age2"] = df["age"].apply(lambda x: x * 2)

# 行上:axis=1
df["info"] = df.apply(lambda r: f"{r['name']} ({r['age']})", axis=1)

# 更快的矢量化
import numpy as np
df["age2"] = np.where(df["age"] > 30, df["age"], 0)

apply 是 Python 级循环,慢;能用 numpy 矢量化就别用。

实战:从 CSV 到 Parquet

import pandas as pd

df = pd.read_csv("orders.csv", dtype_backend="pyarrow")
print(df.shape, df.dtypes.to_dict())

# 清洗
df = df.dropna(subset=["order_id"])
df["amount"] = df["amount"].astype("Float64")
df = df.query("amount >= 0")

# 落地
df.to_parquet("orders.parquet", engine="pyarrow", index=False)

一些经验

  • 优先用 pyarrow 后端(dtype_backend="pyarrow"),字符串和数值更省内存
  • 一次过 100 万行先用 info(memory_usage="deep") 看内存
  • 大数据 + 性能敏感:考虑 Polars,API 接近但快一截
  • inplace=True 在 2.x 里被逐步弃用,直接用赋值返回
  • SettingWithCopyWarning 出现 = 链式赋值不明确,写法上 df = df.assign(...) 或显式 df.loc[...]

参考