先导内容
0. 数据分析工作流
基本流程
数据分析通常不是“直接画图”,而是:明确问题 → 获取数据 → 清洗数据 → 探索分析 → 建模或统计 → 可视化表达 → 输出结论。
业务问题
-> 数据读取
-> 数据质量检查
-> 缺失值/异常值处理
-> 特征构造
-> 分组聚合/统计检验/建模
-> 图表展示
-> 可复现报告0.1 常用库
| 库 | 作用 |
|---|---|
NumPy | 高性能数组、数学计算、线性代数 |
Pandas | 表格数据处理、清洗、聚合、时间序列 |
Matplotlib | 基础绘图库,控制能力强 |
Seaborn | 基于 Matplotlib 的统计图表 |
SciPy | 科学计算、统计检验、优化 |
scikit-learn | 机器学习预处理、建模、评估 |
Jupyter | 交互式分析和报告 |
0.2 环境准备
python -m venv .venv
source .venv/bin/activate
python -m pip install numpy pandas matplotlib seaborn scipy scikit-learn jupyter1. NumPy
介绍
NumPy(Numerical Python)是 Python 科学计算的基础库,核心是
ndarray多维数组。它用连续内存和 C 级循环提升性能,是 Pandas、SciPy、scikit-learn 等库的底层基础之一。
1.1 NumPy 与 Python 列表对比
| 维度 | Python 列表 | NumPy 数组 |
|---|---|---|
| 数据类型 | 可混合类型 | 通常同一类型 |
| 运算方式 | Python 层循环 | 向量化计算 |
| 内存布局 | 存储对象引用 | 连续内存块 |
| 适合场景 | 通用容器 | 大规模数值计算 |
import numpy as np
nums = [1, 2, 3]
arr = np.array([1, 2, 3])
print([x * 2 for x in nums]) # 列表需要显式循环
print(arr * 2) # NumPy 自动逐元素计算1.2 ndarray
核心属性
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int64)
print(a.ndim) # 维度数量:2
print(a.shape) # 每个维度的长度:(2, 3)
print(a.size) # 元素总数:6
print(a.dtype) # 元素类型:int64
print(a.itemsize) # 单个元素字节数创建数组
np.array([1, 2, 3]) # 从列表创建
np.zeros((2, 3)) # 全 0 数组
np.ones((2, 3)) # 全 1 数组
np.full((2, 3), 7) # 指定填充值
np.eye(3) # 单位矩阵
np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
np.linspace(0, 1, 5) # 生成等间距数
np.random.default_rng(42).normal(size=3) # 推荐的新随机数生成器写法常见数据类型
常见
dtype
- 布尔:
bool_- 整数:
int8、int16、int32、int64、uint8、uint16、uint32、uint64- 浮点:
float16、float32、float64- 复数:
complex64、complex128- 字符串/对象:
str_、object_
a = np.array([1, 2, 3], dtype=np.float64)
b = a.astype(np.int32) # 类型转换会返回新数组1.3 索引、切片和布尔筛选
切片边界
Python 和 NumPy 的切片都是左闭右开:
[start:end]包含start,不包含end。
a = np.array([10, 20, 30, 40, 50])
print(a[0]) # 10
print(a[-1]) # 50
print(a[1:4]) # [20, 30, 40]
print(a[::2]) # [10, 30, 50]
mask = a >= 30
print(a[mask]) # [30, 40, 50]m = np.array([[1, 2, 3], [4, 5, 6]])
print(m[0, 1]) # 第 0 行第 1 列:2
print(m[:, 1]) # 取所有行的第 1 列
print(m[1, :]) # 取第 1 行所有列1.4 广播机制
广播
广播让不同形状的数组在兼容时自动扩展维度,避免手动循环。核心规则是从右向左比较维度:相等或其中一个为 1 即兼容。
scores = np.array([[80, 90, 100], [70, 85, 95]])
bonus = np.array([1, 2, 3])
print(scores + bonus)
# 每一行都会加上 bonus,对列进行广播1.5 形状变换和拼接
a = np.arange(12)
print(a.reshape(3, 4)) # 改为 3 行 4 列
print(a.reshape(2, -1)) # -1 表示自动推断该维度
x = np.array([[1, 2], [3, 4]])
y = np.array([[5, 6]])
np.vstack([x, y]) # 按行拼接
np.hstack([x, y.T]) # 按列拼接1.6 常用数学和统计函数
a = np.array([1, 2, 3, 4, 5])
print(np.mean(a)) # 均值
print(np.median(a)) # 中位数
print(np.std(a)) # 标准差
print(np.min(a)) # 最小值
print(np.max(a)) # 最大值
print(np.argmax(a)) # 最大值索引
print(np.cumsum(a)) # 累计和m = np.array([[1, 2, 3], [4, 5, 6]])
print(m.sum(axis=0)) # 按列求和
print(m.sum(axis=1)) # 按行求和1.7 线性代数
a = np.array([[1, 2], [3, 4]])
b = np.array([[10], [20]])
print(a @ b) # 矩阵乘法
print(np.linalg.det(a)) # 行列式
print(np.linalg.inv(a)) # 逆矩阵
print(np.linalg.solve(a, b)) # 解线性方程组 ax=b1.8 性能注意点
性能建议
- 优先使用向量化运算,避免 Python 层
for循环。- 能预分配数组就不要在循环中反复
append。- 大数组切片通常是视图,修改切片可能影响原数组;需要独立副本时使用
.copy()。
a = np.array([1, 2, 3, 4])
b = a[1:3] # b 是视图
b[0] = 99
print(a) # [1, 99, 3, 4]
c = a[1:3].copy()
c[0] = 100
print(a) # 原数组不再受影响2. Pandas
概述
Pandas 是基于 NumPy 的表格数据分析库,核心数据结构是
Series和DataFrame。它适合处理 CSV、Excel、SQL 查询结果、日志表、业务报表和时间序列。
2.1 核心数据结构
Series
import pandas as pd
s = pd.Series([80, 90, 95], index=["Alice", "Bob", "Cindy"], name="score")
print(s["Alice"]) # 按标签访问
print(s.iloc[0]) # 按位置访问
print(s.mean()) # 均值DataFrame
df = pd.DataFrame(
{
"name": ["Alice", "Bob", "Cindy"],
"age": [20, 21, 20],
"score": [88, 95, 76],
}
)
print(df.head()) # 查看前 5 行
print(df.info()) # 查看字段、类型、缺失情况
print(df.describe()) # 数值列统计摘要2.2 数据读取和写出
import pandas as pd
df = pd.read_csv("users.csv")
excel = pd.read_excel("users.xlsx", sheet_name="Sheet1")
df.to_csv("users_clean.csv", index=False)
df.to_excel("users_clean.xlsx", index=False)from sqlalchemy import create_engine
engine = create_engine("mysql+pymysql://user:password@localhost:3306/db")
df = pd.read_sql("select id, name, score from users", engine)2.3 选择数据
df = pd.DataFrame(
{
"name": ["Alice", "Bob", "Cindy"],
"age": [20, 21, 20],
"score": [88, 95, 76],
}
)
print(df["name"]) # 取一列,返回 Series
print(df[["name", "score"]]) # 取多列,返回 DataFrame
print(df.loc[0, "name"]) # 按标签定位
print(df.iloc[0, 0]) # 按位置定位
high = df[df["score"] >= 90] # 布尔筛选
print(high)2.4 数据清洗
缺失值
df = pd.DataFrame(
{
"name": ["Alice", "Bob", None],
"score": [88, None, 76],
}
)
print(df.isna().sum()) # 每列缺失数量
df1 = df.dropna() # 删除含缺失值的行
df2 = df.fillna({"name": "unknown", "score": 0})重复值
df = df.drop_duplicates(subset=["name"], keep="first")类型转换
df["score"] = pd.to_numeric(df["score"], errors="coerce")
df["created_at"] = pd.to_datetime(df["created_at"], errors="coerce")字符串处理
df["name"] = df["name"].str.strip().str.lower()
df["domain"] = df["email"].str.extract(r"@(.+)$")2.5 特征构造
df["passed"] = df["score"] >= 60
df["level"] = pd.cut(
df["score"],
bins=[0, 60, 80, 100],
labels=["不及格", "良好", "优秀"],
include_lowest=True,
)
df["score_rank"] = df["score"].rank(ascending=False, method="dense")2.6 分组聚合
result = (
df.groupby("age")
.agg(
count=("name", "count"),
avg_score=("score", "mean"),
max_score=("score", "max"),
)
.reset_index()
)
print(result)2.7 排序、合并和透视表
df_sorted = df.sort_values(["score", "age"], ascending=[False, True])orders = pd.DataFrame({"user_id": [1, 1, 2], "amount": [10, 20, 30]})
users = pd.DataFrame({"user_id": [1, 2], "name": ["Alice", "Bob"]})
merged = orders.merge(users, on="user_id", how="left")pivot = pd.pivot_table(
merged,
values="amount",
index="name",
aggfunc="sum",
)2.8 时间序列
df["created_at"] = pd.to_datetime(df["created_at"])
df = df.set_index("created_at").sort_index()
daily = df["score"].resample("D").mean() # 按天重采样
rolling = df["score"].rolling(window=7).mean() # 7 日滚动均值2.9 常见坑
Pandas 注意事项
- 链式赋值可能导致修改未生效,优先使用
.loc[row, col] = value。NaN != NaN,判断缺失要用isna()。- 大文件不要一次性读入内存,可以用
chunksize分块处理。object类型列可能混有字符串、数字和缺失值,建模前要清理类型。
# 推荐写法:明确指定行列
df.loc[df["score"] < 60, "passed"] = False2.10 大文件处理
total = 0
for chunk in pd.read_csv("big.csv", chunksize=100_000):
# 每次只处理一部分数据,降低内存压力
total += chunk["amount"].sum()
print(total)3. Matplotlib
概述
Matplotlib 是 Python 最基础的绘图库。复杂控制用 Matplotlib,快速统计图可用 Seaborn。
3.1 基本折线图
import matplotlib.pyplot as plt
x = [1, 2, 3, 4]
y = [10, 20, 25, 30]
plt.figure(figsize=(6, 4))
plt.plot(x, y, marker="o", label="score")
plt.title("Score Trend")
plt.xlabel("Day")
plt.ylabel("Score")
plt.legend()
plt.tight_layout()
plt.show()3.2 常用图表
plt.bar(["A", "B", "C"], [10, 20, 15]) # 柱状图
plt.hist([1, 1, 2, 3, 3, 3], bins=3) # 直方图
plt.scatter([1, 2, 3], [3, 2, 5]) # 散点图
plt.boxplot([[1, 2, 3], [2, 3, 10]]) # 箱线图3.3 与 Pandas 结合
df.groupby("age")["score"].mean().plot(kind="bar")
plt.ylabel("Average Score")
plt.tight_layout()
plt.show()4. Seaborn
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme(style="whitegrid")
tips = sns.load_dataset("tips")
sns.scatterplot(data=tips, x="total_bill", y="tip", hue="time")
plt.show()
sns.boxplot(data=tips, x="day", y="total_bill")
plt.show()
sns.heatmap(tips[["total_bill", "tip", "size"]].corr(), annot=True, cmap="Blues")
plt.show()图表选择
- 趋势:折线图
- 排名/对比:柱状图
- 分布:直方图、密度图、箱线图
- 关系:散点图、热力图
- 组成:堆叠柱状图、饼图谨慎使用
5. 机器学习预处理
5.1 训练集和测试集
from sklearn.model_selection import train_test_split
X = df[["age", "study_hours"]]
y = df["passed"]
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y, # 分类任务中保持标签比例
)5.2 缺失值、标准化和独热编码
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
numeric_features = ["age", "study_hours"]
category_features = ["city"]
numeric_pipeline = Pipeline(
steps=[
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
]
)
category_pipeline = Pipeline(
steps=[
("imputer", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore")),
]
)
preprocessor = ColumnTransformer(
transformers=[
("num", numeric_pipeline, numeric_features),
("cat", category_pipeline, category_features),
]
)5.3 建模示例
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
model = Pipeline(
steps=[
("preprocess", preprocessor),
("classifier", LogisticRegression(max_iter=1000)),
]
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
print(classification_report(y_test, pred))数据泄漏
标准化、填补缺失值、特征选择等步骤必须只在训练集上
fit,再应用到测试集。使用Pipeline可以减少数据泄漏风险。
6. 分析报告和可复现性
6.1 Notebook 建议
Notebook 习惯
- 一个 Notebook 只解决一个明确问题。
- 重要参数放在开头,避免散落在各处。
- 图表标题、坐标轴、单位要写清楚。
- 最终结论单独成节,写明口径、样本范围和局限。
6.2 项目目录建议
analysis-project/
data/
raw/ # 原始数据,不手动修改
processed/ # 清洗后的中间数据
notebooks/
reports/
src/
tests/
pyproject.toml
README.md6.3 常用检查清单
- 是否明确数据时间范围和来源。
- 是否检查缺失值、重复值、异常值。
- 是否保留清洗和转换逻辑。
- 是否避免手动修改原始数据。
- 是否标注图表单位和统计口径。
- 是否说明结论的不确定性和适用边界。