Python 数据科学基本功:CSV、JSON、pandas 与 NumPy 实战要点

2026-05-26 17 预计阅读时间:1 分钟
来源:realpython.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:10 分钟

做数据科学项目,最常打交道的就是文件读写、表格操作和数值计算。看似基础,但细节一错,后续分析全跑偏。这篇文章围绕 CSV/JSON 文件处理、pandas DataFrame 操作和 NumPy 数组计算三个核心板块,把容易踩坑的地方和实用模式梳理清楚,最后附一段可直接运行的整合示例。

CSV 文件:读写的隐坑比你想的多

CSV 是数据交换最普遍的格式,但它的"简单"只是表象。

读取时的常见问题:

  • 编码不是 UTF-8(中文数据尤其常见),直接 open 会报 UnicodeDecodeError
  • 分隔符不一定是逗号——有些文件用 tab 或分号。
  • 首行可能不是列名,或者列名里有空格。

用 pandas 读 CSV 时,关键参数要显式指定:

import pandas as pd

df = pd.read_csv(
    "sales_data.csv",
    encoding="gbk",          # 中文 Windows 导出的文件常见 gbk
    sep=",",                 # 非逗号分隔时改这里
    header=0,                # 首行是列名;无列名时设 None
    skiprows=[1, 2],         # 跳过前两行垃圾数据
    na_values=["NA", "null"] # 把这些字符串自动转为 NaN
)

写入时要注意:

pandas 默认写 CSV 会带行索引,大多数下游系统不需要这列:

df.to_csv("output.csv", index=False, encoding="utf-8-sig")

utf-8-sig 加了 BOM 头,Excel 打开不会乱码——这是个值得记住的小技巧。

JSON 文件:嵌套结构的拆解才是难点

JSON 比 CSV 灵活,但也更"乱"。真实数据经常是嵌套的,直接 json.load 之后拿到一个深层字典,怎么拍平成表格?

基础读写:

import json

# 读取
with open("config.json", "r", encoding="utf-8") as f:
    data = json.load(f)

# 写入
with open("output.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

ensure_ascii=False 保证中文不变成 \uXXXX 转义;indent=2 让输出可读。

嵌套 JSON → DataFrame:

假设数据是这样的结构:

[
  {"name": "Alice", "scores": {"math": 90, "english": 85}},
  {"name": "Bob", "scores": {"math": 78, "english": 92}}
]

直接 pd.DataFrame(data) 只会得到 scores 列里塞着字典。正确做法是用 json_normalize

from pandas import json_normalize

flat_df = json_normalize(data)
# 结果:name | scores.math | scores.english 三列,干净整齐
print(flat_df)

这比手写循环拆字典快得多,也更不容易出错。

pandas DataFrame:五个高频操作模式

DataFrame 是数据科学的主力工具。以下五个操作覆盖日常 80% 的场景。

1. 筛选与条件查询

# 单条件
high_math = df[df["math"] > 80]

# 多条件组合
mask = (df["math"] > 80) & (df["english"] < 90)
result = df.loc[mask, ["name", "math", "english"]]

注意:多条件必须用 & / |,不能用 Python 的 and / or;每个条件要用括号包起来,否则运算优先级会出错。

2. 分组聚合

summary = df.groupby("department").agg(
    avg_salary=("salary", "mean"),
    head_count=("salary", "count"),
    max_salary=("salary", "max")
)

用命名聚合(agg 里传元组)比之后重命名列名更清晰。

3. 缺失值处理

# 查看每列缺失数量
missing = df.isna().sum()

# 填充:数值列用中位数,分类列用 "未知"
df["age"] = df["age"].fillna(df["age"].median())
df["city"] = df["city"].fillna("未知")

别盲目 fillna(0)——零值和中位数含义完全不同,选哪个要看业务语境。

4. 合并两张表

merged = pd.merge(
    orders, customers,
    on="customer_id",      # 连接键
    how="left"             # 左连接:保留所有订单
)

how 参数选 leftrightinner 还是 outer,取决于你想保留哪边的数据。先想清楚业务需求再选。

5. 列类型转换

df["date"] = pd.to_datetime(df["date_str"], format="%Y-%m-%d")
df["amount"] = df["amount"].astype(float)

日期和金额列不转换类型,后续排序、计算、绘图全会出问题。

NumPy 数组:向量化思维替代循环

NumPy 的核心优势是向量化运算——用数组表达式替代逐元素循环,速度差几十倍。

创建与基本运算:

import numpy as np

# 从列表创建
arr = np.array([1, 2, 3, 4, 5])

# 批量运算——不需要写循环
squared = arr ** 2          # [1, 4, 9, 16, 25]
scaled = arr * 2.5 + 10     # [12.5, 15.0, 17.5, 20.0, 22.5]

# 统计
print(arr.mean(), arr.std(), arr.max(), arr.min())

二维数组与矩阵操作:

matrix = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])

# 按行求和
row_sums = matrix.sum(axis=1)   # [6, 15, 24]

# 按列均值
col_means = matrix.mean(axis=0) # [4., 5., 6.]

# 矩阵乘法
result = matrix @ matrix.T      # 等价于 np.dot(matrix, matrix.T)

广播机制——形状不同也能运算:

row_vector = np.array([[10, 20, 30]])   # shape (1, 3)
col_vector = np.array([[1], [2], [3]])  # shape (3, 1)

# 广播:自动扩展成 (3, 3) 再相加
broadcast_result = row_vector + col_vector
# [[11, 21, 31],
#  [12, 22, 32],
#  [13, 23, 33]]

理解广播能让你少写大量 reshape 和循环代码。

布尔索引——比循环筛选快得多:

data = np.random.randn(10000)   # 一万个随机数
positives = data[data > 0]      # 一步筛出所有正数
count = (data > 0).sum()        # 正数个数,sum 对 True/False 计数

整合实战:从原始文件到分析结果

下面这段代码模拟了一个完整的小流程:读 CSV → 清洗 → NumPy 计算 → 输出 JSON。可以直接复制运行。

import pandas as pd
import numpy as np
import json
from pathlib import Path

# ---- 1. 生成示例 CSV(如果文件不存在)----
sample_csv = Path("sample_scores.csv")
if not sample_csv.exists():
    pd.DataFrame({
        "student": ["Alice", "Bob", "Carol", "Dave", "Eve"],
        "math": [92, 78, 85, None, 90],
        "english": [88, 95, 79, 82, None],
        "department": ["A", "B", "A", "B", "A"]
    }).to_csv(sample_csv, index=False)

# ---- 2. 读取并清洗 ----
df = pd.read_csv(sample_csv)
df["math"] = df["math"].fillna(df["math"].median())
df["english"] = df["english"].fillna(df["english"].median())

# ---- 3. pandas 分组统计 ----
group_stats = df.groupby("department").agg(
    avg_math=("math", "mean"),
    avg_english=("english", "mean"),
    count=("student", "count")
).round(2)

# ---- 4. NumPy 向量化计算 ----
math_arr = df["math"].to_numpy()
eng_arr = df["english"].to_numpy()
total_arr = math_arr + eng_arr           # 向量化求和
df["total"] = total_arr
df["rank"] = df["total"].rank(ascending=False).astype(int)

# ---- 5. 输出 JSON ----
output = {
    "group_summary": group_stats.to_dict(),
    "top_student": df.loc[df["rank"] == 1, ["student", "total"]].to_dict("records")[0],
    "math_stats": {
        "mean": float(np.mean(math_arr)),
        "std": float(np.std(math_arr)),
        "max": float(np.max(math_arr))
    }
}

with open("analysis_result.json", "w", encoding="utf-8") as f:
    json.dump(output, f, ensure_ascii=False, indent=2)

print("=== 分组统计 ===")
print(group_stats)
print("\n=== 排名表 ===")
print(df[["student", "math", "english", "total", "rank"]])
print("\n=== JSON 已写入 analysis_result.json ===")

运行后你会看到分组均值、排名表,以及一个结构清晰的 JSON 文件。把这段代码里的文件路径和列名换成你自己的数据,就能直接用。

检查清单:上手前的几个确认项

每次拿到新数据集,先过一遍这张清单,能省掉后续大量排查时间:

检查项 怎么做 常见坑
编码 pd.read_csv(..., encoding=?) 中文文件默认 UTF-8 会炸
分隔符 df.head() 看列是否挤成一列 tab/分号分隔的文件被当逗号解析
缺失值 df.isna().sum() 空格字符串不算 NaN,需手动指定
列类型 df.dtypes 日期是字符串、金额是 object 类型
嵌套 JSON json_normalize 直接 DataFrame 会得到字典列
数组形状 arr.shape 广播出错多半是形状没对齐

这些基本功不花哨,但决定了后续建模、可视化的地基是否稳固。花二十分钟把这些模式跑通,比事后花两小时排查一个编码错误划算得多。


相关推荐