01|数据分析师的工具栈:Python vs HiveSQL
一个真实的工作日
新人小王早上接到产品需求:"看看上周新功能发布后,下沉用户的 7 日留存有没有提升。"他打算这么干:
- 在 Hive 上写 SQL 把最近一个月的用户行为表 + 用户画像表 join 起来,按城市等级筛出"下沉用户"
- 把按日聚合后的留存矩阵导出来,几万行,丢进 Jupyter
- 用 pandas 算环比、做对比图,写进周报
SQL 收敛数据,Python 做分析和呈现 —— 这就是数据分析师 90% 的日常。
各自擅长什么
| 维度 | HiveSQL | Python (pandas/numpy) |
|---|---|---|
| 数据规模 | TB / PB | GB(单机内存) |
| 计算引擎 | MapReduce / Tez / Spark | 单机 |
| 适合的活 | 取数、宽表、聚合、Join | 清洗、建模、统计、可视化 |
| 不适合 | 字符串复杂清洗、迭代算法 | 全量扫描 PB 数据 |
| 输出 | 中间表 / 看板数据源 | 报告 / 图表 / 模型 |
一句话:SQL 把 PB 收敛到 GB,Python 把 GB 提炼成结论。
哪些场景该用哪个
用 HiveSQL 更合适
- 跑日活、月活、留存等例行指标
- 多张事实表 join 出用户行为宽表
- 按分区扫数据,几亿行内做聚合统计
- 业务侧周期性看板的数据源
用 Python 更合适
- 一次性的深度分析(A/B 实验后效果归因)
- 需要复杂条件判断的清洗(正则、分词、模糊匹配)
- 统计推断(卡方、t 检验、回归)
- 可视化报告和 Excel 给非技术同事
必须两个一起用
绝大多数实战场景:HiveSQL 取数 → Python 分析。下面是一个可直接照搬的最小骨架:SQL 端在仓库里把几亿行行为表压成每用户一行的窄表,Python 端只拿这张小表做分层和可视化。
import pandas as pd
from pyhive import hive
# 1) SQL 在 Hive 集群里就地聚合:几亿行 -> 几十万行
sql = """
SELECT user_id,
MAX(dt) AS last_active_day,
COUNT(*) AS order_count,
SUM(pay_amount) AS total_amount
FROM dwd.user_behavior
WHERE dt BETWEEN '2026-04-01' AND '2026-04-30'
GROUP BY user_id
"""
conn = hive.Connection(host="hive-server", port=10000, database="dwd")
df = pd.read_sql(sql, conn) # 网络传输的只是聚合后的小表
# 2) Python 端做 SQL 难写的活:分箱、打分、透视
ref_day = pd.Timestamp("2026-05-01")
df["last_active_day"] = pd.to_datetime(df["last_active_day"])
df["recency_days"] = (ref_day - df["last_active_day"]).dt.days
df["R"] = pd.qcut(df["recency_days"], 5, labels=[5, 4, 3, 2, 1]).astype(int)
df["F"] = pd.qcut(df["order_count"].rank(method="first"), 5, labels=range(1, 6)).astype(int)
df["M"] = pd.qcut(df["total_amount"].rank(method="first"), 5, labels=range(1, 6)).astype(int)
print(df[["user_id", "recency_days", "order_count", "R", "F", "M"]].head())预期输出(示例):
user_id recency_days order_count R F M
0 100231 3 12 5 5 5
1 100232 27 1 2 1 2
2 100233 14 4 3 3 3
3 100234 45 2 1 2 1
4 100235 9 7 4 4 4分工一目了然:GROUP BY 这步如果搬到 Python 用 read_csv 全量拉下来再 groupby,要先把几亿行原始数据传过网络、塞进内存;而在 Hive 里 GROUP BY 后只回传几十万行。qcut 这种分位数分箱在 SQL 里要写一大段 NTILE + 子查询,pandas 一行就够。
不该有的两个误区
误区 1:Python 万能,能不写 SQL 就不写
错。把上亿行用户行为表 pd.read_csv() 进内存?8GB 的笔记本直接爆。SQL 在数据仓库就地计算的优势不可替代。
误区 2:SQL 够用了,pandas 是花架子
错。SQL 写不动这些场景:
- 用滑动窗口做异常检测(窗口函数能做但写起来痛苦)
- 文本清洗(jieba 分词、正则替换)
- 调用机器学习模型给数据打标
- 出一张配色精致的报告图
用一组数字看清"代价最低"
"数据量大就走 SQL"不是口号,是有量级差的。下面这组数字来自一个常见场景:一张约 3 亿行、列存(ORC)后约 60GB 的用户行为表,目标是按 user_id 聚合出每人的订单数与消费额(结果约 50 万行、几十 MB)。
| 做法 | 扫描/传输数据量 | 大致耗时 | 内存峰值 | 结果 |
|---|---|---|---|---|
Hive 集群里 GROUP BY 后取回 | 集群内分布式扫描 60GB | 1–3 min | 单机仅放 50 万行结果 | ✅ 顺畅 |
pd.read_sql 全表拉回再 groupby | 网络传输 ~60GB | 数十分钟 + | 远超 16GB,OOM | ❌ 跑不动 |
差距的根源不是 pandas 慢,而是"在哪算":Hive 把计算推到数据所在的集群、用几十个节点并行扫描,只回传聚合结果;硬拉回单机则要先扛住 60GB 的网络传输和内存,普通 16GB 笔记本直接 OOM。
反过来,一旦数据已经收敛到 GB 级以内,Python 的向量化又比绕回 SQL 划算。对一张 100 万行的 DataFrame 算一个衍生字段(如 total / count):
| 写法 | 100 万行耗时 | 相对速度 |
|---|---|---|
for 逐行循环 / iterrows | ~1.8 s | 1× |
df.apply(axis=1) | ~0.9 s | 2× |
向量化 df['a'] / df['b'] | ~0.01 s | ~180× |
结论很直接:选引擎看数据在哪、有多大——还在仓库里、量级在 TB/PB,就用 SQL 就地收敛;已经落到内存、量级在 GB,就用 pandas 向量化,别再来回搬数据,也别在 pandas 里写 for 循环。
学习重点的优先级
如果你时间有限,先掌握这些:
HiveSQL(按重要性)
- 窗口函数:
row_number / rank / sum() over (partition by ...) - 分区表的正确使用:
WHERE dt = ...是不是真的下推 - 复杂 join:
LEFT SEMI JOIN、关联键去重前后差异 - 数据倾斜的识别和处理
Python(按重要性)
- pandas
merge / groupby / pivot三件套 - 时间序列处理(
to_datetime、resample、时区) - 缺失值与异常值处理
- matplotlib 调出能交付的图(不是默认配色那种)
后面 11 篇会把这两条线分别讲透,然后用一个 RFM 综合案例串起来。
小结
工具是手段不是目的。判断该用什么的标准只有一条:这一步操作,在哪种引擎上代价最低。数据量大就走 SQL,逻辑复杂就拖到 Python。
下一篇先把 Python 环境搭起来,让你能跟着代码跑。

评论功能暂未开放
还没有评论
快来发表第一条评论吧