数据分析相关面试题
目录
为什么会出现SettingWithCopyWarning这个警告?怎么解决?
NumPy 数组(ndarray)和 Python 列表 list 的区别?
Matplotlib 是什么?Seaborn 是什么?关系?
Matplotlib 两张图:figure /axes 是什么?
Statsmodels 是什么?和 Sklearn 有什么区别?
什么时候用 Statsmodels,什么时候用 Sklearn?
SQL语法
基础语法
WHERE 和 HAVING 的区别
- WHERE:对【原始数据行】过滤,发生在分组之前
- HAVING:对【分组后的结果】过滤,必须跟在 GROUP BY 后,可使用聚合函数
- WHERE 不能用聚合函数,HAVING 可以
DISTINCT 和 GROUP BY 的区别
- DISTINCT:对结果集去重,只保留唯一值,不做聚合
- GROUP BY:按字段分组,用于配合聚合函数做统计
结论:只去重用 DISTINCT,要统计用 GROUP BY
SELECT DISTINCT col FROM t;
SELECT col, COUNT(*) FROM t GROUP BY col;
LIMIT 和 OFFSET
- LIMIT n:取前 n 条
- OFFSET m:跳过前 m 条
-
等价于 LIMIT 5,10
作用:分页查询
SELECT * FROM t LIMIT 10 OFFSET 5;
NULL 判断
- 错误:col = NULL 永远不成立(NULL 不等于任何值,包括自己)
- 正确:判断是否为空用 IS NULL / IS NOT NULL
SELECT * FROM t WHERE col IS NULL;
SELECT * FROM t WHERE col IS NOT NULL;
别名能否用于 WHERE
不能!因为 SQL 执行顺序:WHERE 早于 SELECT
错误
SELECT col AS a FROM t WHERE a > 10;
正确
SELECT col AS a FROM t WHERE col > 10;
聚合函数
COUNT (*)、COUNT (1)、COUNT(列)
- COUNT(*):统计所有行,包含 NULL
- COUNT(1):与 COUNT(*) 几乎一致,效率基本相同
- COUNT(列):只统计【该列不为 NULL】的行数 -- 面试结论:统计表总行数用 COUNT(*),统计非空用 COUNT(列)
SUM/AVG 遇到 NULL
- SUM、AVG、MAX、MIN 都会自动忽略 NULL,不参与计算
- 例如 AVG(10, NULL, 20) = (10+20)/2 = 15
聚合函数能否嵌套
不能直接嵌套:SUM(AVG(col)) 报错,必须用子查询/窗口函数间接实现
SELECT SUM(avg_col)
FROM (SELECT AVG(col) AS avg_col FROM t GROUP BY id) tmp;
分组统计数量、总和、平均值
注意:SELECT 中非聚合字段必须出现在 GROUP BY 中
SELECT
col1, -- 分组字段
COUNT(*) AS cnt, -- 每组行数
SUM(col2) AS sum_col,-- 求和
AVG(col2) AS avg_col -- 平均值
FROM t
GROUP BY col1;
分组后平均值 > 100 的组
HAVING 过滤分组结果
SELECT col1, AVG(col2) AS avg_col
FROM t
GROUP BY col1
HAVING AVG(col2) > 100;
多表 JOIN
四种 JOIN 区别
- INNER JOIN:只保留两边都能匹配上的行
- LEFT JOIN:左表全部保留,右表匹配不上补 NULL
- RIGHT JOIN:右表全部保留,左表匹配不上补 NULL
- FULL JOIN:左右表都保留,匹配不上补 NULL(MySQL 不支持,可用 UNION 模拟)
LEFT JOIN 左表一定全保留吗
不一定! 如果在 WHERE 里对右表字段做过滤(IS NOT NULL / = 值),会把 LEFT JOIN 变成 INNER JOIN
JOIN 数据膨胀原因
一对多关系:1 条左表数据匹配多条右表数据
例如:1 个用户对应 5 条订单,JOIN 后行数变多
解决:先聚合右表,再 JOIN;或用 DISTINCT 去重
ON 与 WHERE 在 LEFT JOIN 中区别
- ON:JOIN 时的匹配条件,不影响左表行数
- WHERE:对 JOIN 后的结果整体过滤,会删除左表行
示例:左表全保留,只匹配 t2.status=1 的行
SELECT *
FROM t1
LEFT JOIN t2 ON t1.id = t2.id AND t2.status = 1;
示例:等价 INNER JOIN,左表不满足行会被删掉
SELECT *
FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t2.status = 1;
子查询与 WITH
相关子查询 vs 非相关子查询
非相关子查询:子查询可独立运行,只执行一次
SELECT * FROM t WHERE col IN (SELECT col FROM tmp);
相关子查询:子查询依赖外层表,每行执行一次
SELECT * FROM t1
WHERE EXISTS (SELECT 1 FROM t2 WHERE t1.id = t2.id);
EXISTS 与 IN
- IN:适合子查询结果小
- EXISTS:适合子查询结果大,逐行匹配,效率更高
- 优先用 EXISTS
查询最大值所在整行
方法1:子查询
SELECT * FROM t
WHERE col = (SELECT MAX(col) FROM t);
方法2:窗口函数
SELECT * FROM (
SELECT *, RANK() OVER(ORDER BY col DESC) AS rk
FROM t
) tmp WHERE rk = 1;
大于本组平均值
SELECT t.*
FROM t
JOIN (
SELECT group_id, AVG(col) AS group_avg
FROM t GROUP BY group_id
) tmp ON t.group_id = tmp.group_id
WHERE t.col > tmp.group_avg;
WITH AS 临时表
公共表达式,提高可读性,可多次复用
WITH tmp AS (
SELECT group_id, AVG(col) AS avg_col FROM t GROUP BY group_id
)
SELECT * FROM tmp WHERE avg_col > 100;
CASE WHEN
顺序匹配
按顺序匹配,满足第一个即停止
SELECT
score,
CASE
WHEN score >= 90 THEN '优秀'
WHEN score >= 70 THEN '良好'
WHEN score >= 60 THEN '及格'
ELSE '不及格'
END AS level
FROM t;
行转列统计数量
不满足条件为 NULL,COUNT 忽略 NULL
SELECT
COUNT(CASE WHEN gender = 1 THEN 1 END) AS male_cnt,
COUNT(CASE WHEN gender = 0 THEN 1 END) AS female_cnt
FROM t;
CASE 可放位置
SELECT、WHERE、GROUP BY、HAVING、ORDER BY 都可以用
MySQL IF 与 CASE WHEN
IF(条件, 成立值, 不成立值) 只能单分支,CASE WHEN 支持多条件,更通用
IF(score>60, '及格', '不及格')
窗口函数
三个排名函数区别
- ROW_NUMBER():连续不重复排名 1,2,3,4
- RANK():并列排名,跳号 1,1,3,4
- DENSE_RANK():并列排名,不跳号 1,1,2,3
SELECT
id,
score,
ROW_NUMBER() OVER(ORDER BY score DESC) AS rn,
RANK() OVER(ORDER BY score DESC) AS rk,
DENSE_RANK() OVER(ORDER BY score DESC) AS drk
FROM t;
窗口函数结构
不改变行数,对每行计算一个统计值
OVER (
PARTITION BY group_id -- 分组(可选)
ORDER BY col DESC -- 排序(必须)
)
窗口函数能否放 WHERE
不能!
执行顺序:WHERE → GROUP BY → 窗口函数 → ORDER BY → LIMIT
窗口函数计算晚于 WHERE,所以不能直接过滤
每组 TopN
SELECT * FROM (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY group_id ORDER BY score DESC) AS rn
FROM t
) tmp
WHERE rn <= 3;
累计和
SELECT
id,
val,
SUM(val) OVER(ORDER BY id) AS cum_sum -- 按顺序累加
FROM t;
LAG、LEAD
- LAG(col, n):取当前行【前n行】的值
- LEAD(col, n):取当前行【后n行】的值
常用于环比、同比、连续问题
SELECT
id,
LAG(val, 1) OVER(ORDER BY id) AS pre_val,
LEAD(val, 1) OVER(ORDER BY id) AS next_val
FROM t;
窗口 SUM 与 GROUP BY 区别
- GROUP BY:分组后【行数变少】
- SUM() OVER(PARTITION BY):保留所有行,附加分组统计值
去重、合并、排序
UNION 与 UNION ALL
- UNION:合并结果并【去重+排序】,慢
- UNION ALL:直接拼接,【不去重】,快
优先用 UNION ALL
查询重复数据
SELECT col, COUNT(*)
FROM t
GROUP BY col
HAVING COUNT(*) > 1;
删除重复保留一条
保留 id 最小的一条
DELETE t1
FROM t t1
JOIN t t2
WHERE t1.col = t2.col AND t1.id > t2.id;
多字段排序
先按 col1 降序,相同再按 col2 升序
SELECT * FROM t
ORDER BY col1 DESC, col2 ASC;
NULL 排序位置
MySQL:NULL 视为最小值,升序在前,降序在后,如需把 NULL 放最后:
ORDER BY ISNULL(col), col;
SQL 执行顺序
标准执行顺序
1. FROM / JOIN
2. WHERE
3. GROUP BY
4. HAVING
5. SELECT(含窗口函数、别名)
6. DISTINCT
7. ORDER BY
8. LIMIT / OFFSET
为什么别名不能用在 WHERE
别名在 SELECT 阶段生成,WHERE 执行早于 SELECT,所以看不到别名
SQL 优化
定位慢查询
- 开启慢查询日志
- 使用 EXPLAIN 查看执行计划
- 看是否走索引、是否全表扫描、是否出现 filesort
索引作用
索引是排序结构,类似目录
作用:避免全表扫描,加速 WHERE / ORDER BY / GROUP BY
索引失效场景
- 字段使用函数:WHERE YEAR(date) = 2024
- 隐式类型转换
- LIKE '%xxx%' 前模糊
- OR 连接无索引字段
- 优化器认为全表更快(数据量小)
不推荐 SELECT *
- 查询无用字段,增加 IO
- 无法使用覆盖索引
- 表结构变化后易出错
大表 JOIN 优化
- 小表驱动大表
- 关联字段建索引
- 先过滤再 JOIN
- 避免一对多导致数据膨胀
- 必要时分批查询
高频手写综合题(带注释)
分组统计 + 过滤
SELECT
dept_id,
COUNT(*) AS cnt,
AVG(salary) AS avg_sal
FROM employee
WHERE salary > 0
GROUP BY dept_id
HAVING avg_sal > 5000
ORDER BY avg_sal DESC
LIMIT 10;
行转列
SELECT
user_id,
SUM(CASE WHEN type = 1 THEN amount END) AS income,
SUM(CASE WHEN type = 2 THEN amount END) AS outcome
FROM bill
GROUP BY user_id;
每组 Top3
SELECT * FROM (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY dept_id ORDER BY salary DESC) AS rn
FROM employee
) tmp
WHERE rn <= 3;
连续 N 天登录(通用模板)
SELECT user_id
FROM (
SELECT
user_id,
dt,
-- 日期 - 行号,连续日期会得到相同值
DATE_SUB(dt, INTERVAL ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY dt) DAY) AS grp
FROM login_log
) tmp
GROUP BY user_id, grp
HAVING COUNT(*) >= 7;
环比计算
SELECT
dt,
val,
LAG(val, 1) OVER(ORDER BY dt) AS pre_val,
ROUND((val - LAG(val,1) OVER(ORDER BY dt))/LAG(val,1) OVER(ORDER BY dt),2) AS ratio
FROM daily_data;
SQL应用案例
高等教育类
统一表结构
1. 录取/生源表:存储学生高考录取、生源、专业信息
CREATE TABLE student_admit (
student_id VARCHAR(20) PRIMARY KEY, -- 学号(主键,唯一标识学生)
college VARCHAR(50), -- 学院
major VARCHAR(50), -- 专业
province VARCHAR(50), -- 生源省份
score INT, -- 录取分数
gender VARCHAR(10), -- 性别
is_first_choice TINYINT, -- 1一志愿 0调剂
admit_year INT, -- 录取年份
is_recruit TINYINT -- 1统招 0单招/综评
);
2. 报到表:存储新生报到状态
CREATE TABLE student_register (
student_id VARCHAR(20) PRIMARY KEY, -- 学号(关联生源表)
is_register TINYINT, -- 1已报到 0未报到
register_time DATETIME -- 报到时间
);
3. 毕业表:存储学生毕业、学分、挂科信息
CREATE TABLE student_graduate (
student_id VARCHAR(20) PRIMARY KEY, -- 学号
graduate_year INT, -- 毕业年份
is_graduate TINYINT, -- 1正常毕业 0未毕业
is_degree TINYINT, -- 1授予学位 0不授予
fail_course INT, -- 挂科门数
credit_complete DECIMAL(5,2) -- 已修学分
);
4. 深造/升学表:存储学生保研、考研、出国信息
CREATE TABLE student_advance (
student_id VARCHAR(20) PRIMARY KEY, -- 学号
is_recommend TINYINT, -- 1保研 0未保研
is_postgrad TINYINT, -- 1考研上岸 0未上岸
is_abroad TINYINT, -- 1出国出境 0未出国
target_school_type VARCHAR(20) -- 录取院校类型:985/211/双一流/普通
);
基础查询
查询 2024 级学生核心信息
需求:筛选2024年录取学生,展示学号、学院、专业、分数、省份
SELECT student_id, college, major, score, province
FROM student_admit
WHERE admit_year = 2024; -- 按录取年份过滤
查询未报到新生(按分数降序)
需求:关联生源表和报到表,筛选未报到学生,按录取分数排序
SELECT a.* -- 查询录取表所有字段
FROM student_admit a
LEFT JOIN student_register r ON a.student_id = r.student_id -- 左连接保证所有录取学生都保留
WHERE r.is_register = 0 OR r.is_register IS NULL -- 0=未报到,NULL=无报到记录(视为未报到)
ORDER BY score DESC; -- 按录取分数降序
河南生源 500 分以上学生
需求:多条件过滤,省份=河南省 + 分数>500
SELECT *
FROM student_admit
WHERE province = '河南省' AND score > 500;
查询含 “工程” 的专业及招生人数
需求:模糊匹配专业名,分组统计招生人数
SELECT major, COUNT(*) AS enroll_num -- 统计每个专业人数
FROM student_admit
GROUP BY major
HAVING major LIKE '%工程%'; -- 分组后过滤专业名
去重统计生源省份数量
需求:统计不重复的省份总数
SELECT COUNT(DISTINCT province) AS province_cnt
FROM student_admit; -- DISTINCT 去重,COUNT统计数量
GROUP BY 分组聚合
各学院录取人数、报到人数、报到率
需求:按学院分组,计算报到率(报到人数/录取人数)
SELECT
a.college,
COUNT(*) AS admit_cnt, -- 录取总人数
SUM(r.is_register) AS register_cnt, -- 报到人数(is_register=1求和)
-- 计算报到率,保留2位小数,*100.0转为浮点避免整数除法
ROUND(SUM(r.is_register)*100.0/COUNT(*),2) AS register_rate
FROM student_admit a
LEFT JOIN student_register r ON a.student_id = r.student_id
GROUP BY a.college; -- 按学院分组
各专业录取分数统计
需求:按专业分组,计算平均分、最高分、最低分
SELECT
major,
AVG(score) AS avg_score, -- 平均分
MAX(score) AS max_score, -- 最高分
MIN(score) AS min_score -- 最低分
FROM student_admit
GROUP BY major;
各省生源人数排序
需求:按省份分组统计人数,按人数降序
SELECT province, COUNT(*) AS cnt
FROM student_admit
GROUP BY province
ORDER BY cnt DESC;
男女学生人数及占比
需求:按性别分组,计算人数和占比
SELECT
gender,
COUNT(*) AS cnt,
-- 窗口函数SUM(COUNT(*))OVER()计算总人数,求占比
ROUND(COUNT(*)*100.0/SUM(COUNT(*))OVER(),2) AS ratio
FROM student_admit
GROUP BY gender;
各专业一志愿率统计
需求:按专业分组,计算一志愿录取率
SELECT
major,
SUM(is_first_choice) AS first_cnt, -- 一志愿录取人数
COUNT(*) AS total, -- 总录取人数
ROUND(SUM(is_first_choice)*100.0/COUNT(*),2) AS first_rate -- 一志愿率
FROM student_admit
GROUP BY major;
空值、去重、COUNT
统计录取分数为空的学生数
SELECT COUNT(*)
FROM student_admit
WHERE score IS NULL;
统计省份非空的有效生源数
SELECT COUNT(*)
FROM student_admit
WHERE province IS NOT NULL;
查询重复学号的学生
需求:按学号分组,筛选出现次数>1的重复数据
SELECT student_id, COUNT(*)
FROM student_admit
GROUP BY student_id
HAVING COUNT(*) > 1;
删除重复学号,保留一条
需求:自连接删除重复数据,保留学号最小的一条
DELETE t1
FROM student_admit t1
JOIN student_admit t2 ON t1.student_id = t2.student_id
WHERE t1.ctid < t2.ctid;
多表 JOIN
各专业报到率统计
需求:按专业分组,关联报到表计算报到率
SELECT
a.major,
COUNT(*) AS admit_cnt,
SUM(r.is_register) AS register_cnt,
ROUND(SUM(r.is_register)*100.0/COUNT(*),2) AS register_rate
FROM student_admit a
LEFT JOIN student_register r USING(student_id) -- USING简化关联字段写法
GROUP BY a.major;
所有录取学生 + 报到状态
需求:左连接保证所有录取学生保留,无报到记录填0
SELECT
a.*,
COALESCE(r.is_register, 0) AS is_register -- COALESCE:NULL替换为0
FROM student_admit a
LEFT JOIN student_register r USING(student_id);
各专业学位授予率
需求:关联毕业表,计算学位授予比例
SELECT
a.major,
COUNT(*) AS total, -- 总毕业人数
SUM(g.is_degree) AS degree_cnt, -- 授予学位人数
ROUND(SUM(g.is_degree)*100.0/COUNT(*),2) AS degree_rate -- 学位授予率
FROM student_admit a
JOIN student_graduate g USING(student_id)
GROUP BY a.major;
三表关联:各省生源深造人数
需求:生源+报到+深造三表关联,按省份统计深造人数
SELECT
a.province,
COUNT(DISTINCT a.student_id) AS student_cnt, -- 去重统计总人数
-- CASE判断:满足任一深造条件记为1,求和得深造人数
SUM(CASE WHEN adv.is_recommend=1 OR adv.is_postgrad=1 OR adv.is_abroad=1 THEN 1 ELSE 0 END) AS advance_cnt
FROM student_admit a
LEFT JOIN student_register r USING(student_id)
LEFT JOIN student_advance adv USING(student_id)
GROUP BY a.province;
子查询 & HAVING
分数高于全校平均分的学生
需求:子查询先算全校平均分,再过滤学生
SELECT *
FROM student_admit
WHERE score > (SELECT AVG(score) FROM student_admit);
报到率低于 85% 的专业
需求:子查询先计算各专业报到率,外层过滤
SELECT major, register_rate
FROM (
SELECT
major,
ROUND(SUM(r.is_register)*100.0/COUNT(*),2) AS register_rate
FROM student_admit a
LEFT JOIN student_register r USING(student_id)
GROUP BY major
) t -- 临时表
WHERE register_rate < 85;
高于本专业平均分的学生
需求:先计算各专业平均分,再关联原表过滤
SELECT a.*
FROM student_admit a
JOIN (
SELECT major, AVG(score) AS avg_major_score
FROM student_admit
GROUP BY major
) m ON a.major = m.major -- 按专业匹配平均分
WHERE a.score > m.avg_major_score; -- 分数>专业平均分
近三年录取人数逐年上升的专业
需求:窗口函数LAG获取上一年录取人数,对比逐年上升
SELECT major, admit_year, cnt
FROM (
SELECT
major,
admit_year,
COUNT(*) AS cnt,
-- LAG取上一年数据,按专业分组、年份排序
LAG(COUNT(*),1) OVER(PARTITION BY major ORDER BY admit_year) AS pre_cnt
FROM student_admit
WHERE admit_year IN (2022,2023,2024)
GROUP BY major, admit_year
) t
WHERE cnt > pre_cnt; -- 今年人数>去年人数
报到 + 毕业 + 深造的学生总数
需求:三表内连接,筛选同时满足三个条件的学生
SELECT COUNT(*) AS total
FROM student_register r
JOIN student_graduate g USING(student_id)
JOIN student_advance adv USING(student_id)
WHERE r.is_register=1 -- 已报到
AND g.is_graduate=1 -- 已毕业
-- 满足任一深造条件
AND (adv.is_recommend=1 OR adv.is_postgrad=1 OR adv.is_abroad=1);
CASE WHEN
各专业总深造率
需求:深造=保研+考研+出国,计算各专业深造比例
SELECT
a.major,
COUNT(*) AS graduate_cnt,
SUM(CASE WHEN adv.is_recommend=1 OR adv.is_postgrad=1 OR adv.is_abroad=1 THEN 1 ELSE 0 END) AS advance_cnt,
ROUND(SUM(CASE WHEN is_recommend+is_postgrad+is_abroad>=1 THEN 1 ELSE 0 END)*100.0/COUNT(*),2) AS advance_rate
FROM student_admit a
JOIN student_graduate g USING(student_id)
LEFT JOIN student_advance adv USING(student_id)
GROUP BY a.major;
分数分段 + 深造率统计
需求:CASE分数分段,统计每段人数和深造率
SELECT
CASE
WHEN score < 500 THEN '低分段'
WHEN score BETWEEN 500 AND 550 THEN '中段'
ELSE '高分段'
END AS score_level, -- 分数分段别名
COUNT(*) AS cnt,
ROUND(SUM(CASE WHEN is_recommend+is_postgrad+is_abroad>=1 THEN 1 ELSE 0 END)*100.0/COUNT(*),2) AS advance_rate
FROM student_admit a
LEFT JOIN student_advance adv USING(student_id)
GROUP BY score_level;
标记高分生源(高于校平均分)
子查询计算校平均分,CASE判断是否高分
SELECT
*,
-- 子查询计算校平均分,CASE判断是否高分
CASE WHEN score > (SELECT AVG(score) FROM student_admit) THEN 1 ELSE 0 END AS is_high_score
FROM student_admit;
考研报名率、上岸率
SELECT
major,
SUM(is_postgrad_apply) AS apply_cnt, -- 考研报名人数
SUM(is_postgrad) AS pass_cnt, -- 上岸人数
ROUND(SUM(is_postgrad)*100.0/SUM(is_postgrad_apply),2) AS pass_rate -- 上岸率
FROM student_admit a
JOIN student_advance adv USING(student_id)
GROUP BY major;
毕业结果分类统计
需求:按毕业状态、学分分类统计
SELECT
CASE
WHEN is_graduate=1 THEN '正常毕业'
WHEN credit_complete < 120 THEN '延期毕业'
ELSE '结业/肄业'
END AS graduate_type,
COUNT(*) AS cnt
FROM student_graduate
GROUP BY graduate_type;
窗口函数
专业内分数 Top10
需求:按专业分组,分数降序排名,取每组前10
SELECT *
FROM (
SELECT
*,
-- 分组内排名,连续不重复
ROW_NUMBER() OVER(PARTITION BY major ORDER BY score DESC) AS rn
FROM student_admit
) t
WHERE rn <= 10;
同专业分数分位数
需求:计算学生分数在专业内的排名百分比
SELECT
student_id, major, score,
PERCENT_RANK() OVER(PARTITION BY major ORDER BY score) AS pct
FROM student_admit;
专业人数占学院比例
需求:按学院+专业分组,计算专业人数占学院总人数比例
SELECT
college,
major,
COUNT(*) AS cnt,
-- 窗口函数按学院分组求和,计算占比
ROUND(COUNT(*)*100.0/SUM(COUNT(*))OVER(PARTITION BY college),2) AS ratio_in_college
FROM student_admit
GROUP BY college, major;
深造 / 毕业 / 报到 专项 SQL
各专业毕业率、延期毕业人数
SELECT
a.major,
COUNT(*) AS total,
SUM(g.is_graduate) AS graduate_cnt,
ROUND(SUM(g.is_graduate)*100.0/COUNT(*),2) AS graduate_rate,
-- 筛选未毕业+学分不足的延期学生
SUM(CASE WHEN g.is_graduate=0 AND g.credit_complete<120 THEN 1 ELSE 0 END) AS delay_cnt
FROM student_admit a
JOIN student_graduate g USING(student_id)
GROUP BY a.major;
各专业深造明细 + 总深造率
SELECT
major,
SUM(is_recommend) AS recommend_cnt, -- 保研人数
SUM(is_postgrad) AS postgrad_cnt, -- 考研上岸人数
SUM(is_abroad) AS abroad_cnt, -- 出国人数
-- 总深造率:满足任一深造条件/总人数
ROUND(SUM(CASE WHEN is_recommend+is_postgrad+is_abroad>=1 THEN 1 ELSE 0 END)*100.0/COUNT(*),2) AS total_advance_rate
FROM student_admit a
JOIN student_advance adv USING(student_id)
GROUP BY major;
各省生源深造率
SELECT
province,
COUNT(*) AS cnt,
ROUND(SUM(CASE WHEN is_recommend+is_postgrad+is_abroad>=1 THEN 1 ELSE 0 END)*100.0/COUNT(*),2) AS advance_rate
FROM student_admit a
LEFT JOIN student_advance adv USING(student_id)
GROUP BY province;
升学院校类型占比
SELECT
target_school_type,
COUNT(*) AS cnt,
ROUND(COUNT(*)*100.0/SUM(COUNT(*))OVER(),2) AS ratio -- 占总深造人数比例
FROM student_advance
WHERE target_school_type IS NOT NULL
GROUP BY target_school_type;
高频简答题
1. WHERE vs HAVING:
WHERE 分组前过滤原始行,不能用聚合;HAVING 分组后过滤结果,可以用聚合。
2. JOIN vs 子查询:
JOIN 性能更优,索引生效更快,优先使用 JOIN。
3. 窗口函数限制:
不能放在 WHERE 中,执行顺序晚于 WHERE。
4. SQL 优化:
关联字段建索引、先过滤再 JOIN、避免 SELECT*、减少数据膨胀、EXPLAIN 分析执行计划。
5. 口径不一致排查:
核对时间范围、是否去重、是否包含未报到 / 延期、关联逻辑、统计口径。
Python
Pandas
Pandas作用?
主要用 Pandas 做数据读取、清洗、分组聚合、多表关联和特征工程,是日常最核心的库。
如何读取 csv、excel?如何指定编码、索引?
注释:编码常用 utf-8 / gbk,中文乱码常用 encoding='gbk' 或 encoding='gb2312'。
读取 CSV
import pandas as pd
df = pd.read_csv("data.csv", encoding="utf-8")
读取 Excel
import pandas as pd
df = pd.read_excel("data.xlsx", sheet_name="Sheet1")
读取时指定索引列
import pandas as pd
df = pd.read_csv("data.csv", index_col="id")
拿到一个 DataFrame,你第一步会看什么?
先看结构、类型、空值、重复、异常值。
前5行,看数据结构
df.head()
字段类型、是否有空值、占用内存
df.info()
数值列统计(均值、分位数、最大最小)
df.describe()
行数, 列数
df.shape
每列空值数量(最重要)
df.isnull().sum()
如何按条件筛选行?如何选取列?
1. 条件筛选(最常用)
df[df["age"] > 18]
2. 多条件 & 且 | 或
& | 两边必须加括号,否则报错。
df[(df["score"] >= 60) & (df["province"] == "河南")]
3. loc:按标签(行名+列名)
df.loc[df["score"] > 90, ["name", "score"]]
4. iloc:按位置(第0行到第5行,前2列)
df.iloc[0:5, 0:2]
如何查看、处理缺失值?
- 数值列:均值 / 中位数 / 分位数
- 分类列:众数
- 空值很少:直接删
- 空值有规律:前向填充 ffill / 后向填充 bfill
查看空值
df.isnull().sum()
删除含空值的行
df = df.dropna()
填充 0
df = df.fillna(0)
连续数值用均值填充
df["score"] = df["score"].fillna(df["score"].mean())
分类字段用众数填充
df["province"] = df["province"].fillna(df["province"].mode()[0])
如何查看重复行、删除重复?
查看重复行数
df.duplicated().sum()
删除全部重复行
df = df.drop_duplicates()
按某一列去重(如按id去重,保留第一条)
df = df.drop_duplicates(subset=["id"], keep="first")
如何把字符串转时间?转整数?
转时间类型(非常常用)
df["date"] = pd.to_datetime(df["date"])
转整型
df["age"] = df["age"].astype(int)
查看类型
df.dtypes
按专业分组,统计分数的均值、最大值、人数
单指标
df.groupby("major")["score"].mean()
多分组 + 多聚合函数
reset_index() 非常常用,否则分组字段是索引,不方便后续处理。
df.groupby(["college", "major"])["score"].agg(
mean_score="mean",
max_score="max",
count="count"
).reset_index() # 把索引变回列
多表合并用什么?和 SQL 的 JOIN 对应什么?
- 按共同字段关联 → merge
- 上下堆叠数据 → concat
- 按行号 / 索引合并 → join
1. merge:按列关联(对应 SQL JOIN)
df3 = pd.merge(df1, df2, on="id", how="left") # left join
2. concat:上下拼接(追加数据)
df_all = pd.concat([df1, df2], axis=0)
3. join:按索引合并
df1.join(df2)
用 Pandas 做 Excel 透视表
pd.pivot_table(
data=df,
index="college", # 行
columns="major", # 列
values="score", # 值
aggfunc="mean" # 聚合方式
)
根据分数新增等级列(优秀 / 良好 / 及格)
方法1:np.where(简单二分类)
import numpy as np
df["is_pass"] = np.where(df["score"] >= 60, 1, 0)
方法2:apply(多分类)
df["level"] = df["score"].apply(lambda x:
"优秀" if x >= 90 else
"良好" if x >= 70 else
"及格" if x >= 60 else "不及格"
)
方法3:pd.cut(分桶,最规范)
df["level"] = pd.cut(
df["score"],
bins=[0, 60, 70, 90, 100],
labels=["不及格", "及格", "良好", "优秀"]
)
如何提取年、月、日?如何按月求和?
df["date"] = pd.to_datetime(df["date"])
df["year"] = df["date"].dt.year
df["month"] = df["date"].dt.month
df["day"] = df["date"].dt.day
按月重采样统计
df.resample("M", on="date")["amount"].sum()
如何识别并剔除异常值?
超出 ±3σ 视为异常,是数据分析标准做法。
mean = df["score"].mean()
std = df["score"].std()
upper = mean + 3 * std
lower = mean - 3 * std
df = df[(df["score"] >= lower) & (df["score"] <= upper)]
行转列、列转行(melt /pivot)
宽表 → 长表
df_long = df.melt(id_vars="id", var_name="type", value_name="value")
长表 → 宽表
df_wide = df_long.pivot(index="id", columns="type", values="value")
apply/map/applymap 区别
map:只用于 Series,元素映射apply:可用于 Series / DataFrame,支持复杂函数applymap:对 DataFrame 每个单元格操作(现已逐渐被map替代)
为什么会出现SettingWithCopyWarning这个警告?怎么解决?
对切片 DataFrame 直接修改,Pandas 不确定你改原表还是副本。
解决:加上 .copy() 即可。
df = df[df["score"]>60].copy()
如何实现 SQL 的 group by having?
例子:筛选平均分 > 60 的专业
df.groupby("major").filter(lambda x: x["score"].mean() > 60)
导出 Excel / CSV
df.to_csv("out.csv", index=False, encoding="utf-8-sig")
df.to_excel("out.xlsx", index=False, sheet_name="结果")
NumPy
NumPy作用?
用 NumPy 做数值计算、异常值判断、数据归一化,配合 Pandas 做向量化操作。
NumPy 数组(ndarray)和 Python 列表 list 的区别?
- 数组元素类型必须统一,列表可以混放任意类型。
- 数组支持矢量化运算,不用循环,速度极快。
- 数组支持多维结构(矩阵),更适合数值计算。
- 数组占用内存更小,底层连续存储。
- 提供大量数学 / 统计函数,适合数据分析、建模。
创建数组的常用方式有哪些?
import numpy as np
从列表创建
arr = np.array([1,2,3,4])
全0/全1数组
arr = np.zeros((3,4))
arr = np.ones((2,2))
固定值
arr = np.full((2,3), 5)
等差序列
arr = np.arange(0, 10, 2) # 0,2,4,6,8
arr = np.linspace(0,10,5) # 均分5个点
单位矩阵
arr = np.eye(3)
查看数组属性
arr = np.array([[1,2],[3,4]])
形状 (行数,列数)
arr.shape
维度数
arr.ndim
总元素个数
arr.size
数据类型
arr.dtype
转置
arr.T
数组索引与切片
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
取某行
arr[0]
取某行某列
arr[0, 1]
切片:所有行,前2列
arr[:, :2]
布尔索引 arr[arr > 5]
arr[arr > 5]
什么是广播(broadcasting)?为什么重要?
- 不同形状的数组在运算时,NumPy 会自动扩展维度对齐,不需要手动循环。
- 优点:代码简洁、速度极快、内存高效。
- 是 Pandas、Matplotlib、机器学习库高效的基础。
示例:
a = np.array([[1,2],[3,4]])
b = np.array([10, 20])
# b 自动广播成 [[10,20],[10,20]]
print(a + b)
常用数学 / 统计函数
arr = np.array([1,2,3,4,5])
求和
np.sum(arr)
均值
np.mean(arr)
标准差
np.std(arr)
方差
np.var(arr)
最大值
np.max(arr)
最小值
np.min(arr)
中位数
np.median(arr)
最大值下标
np.argmax(arr)
最小值下标
np.argmin(arr)
累计求和
np.cumsum(arr)
数据归一化 / 标准化
题目:把数组缩放到 [0,1] 之间
arr = np.array([1,2,3,4,5])
arr_min = arr.min()
arr_max = arr.max()
# 最小-最大归一化
arr_norm = (arr - arr_min) / (arr_max - arr_min)
题目:Z-score 标准化(均值 0,方差 1)
arr_std = (arr - np.mean(arr)) / np.std(arr)
用 NumPy 实现 3σ 异常值筛选
arr = np.array([1,2,3,4,100])
mean = arr.mean()
std = arr.std()
upper = mean + 3 * std
lower = mean - 3 * std
# 保留正常范围内数据
arr_clean = arr[(arr >= lower) & (arr <= upper)]
数组形状修改
arr = np.arange(12)
改为3行4列
arr.reshape(3,4)
展平成一维
arr.flatten()
转置
arr.reshape(3,4).T
拼接与分割
a = np.array([[1,2],[3,4]])
b = np.array([[5,6],[7,8]])
垂直拼接(上下)
np.vstack([a, b])
水平拼接(左右)
np.hstack([a, b])
均等分割
np.vsplit(a, 2)
np.hsplit(a, 2)
np.where 用法
arr = np.array([1,2,3,4,5])
满足条件返回x,否则y
new_arr = np.where(arr > 3, 100, 0)
只返回满足条件的下标
idx = np.where(arr > 3)
去重、唯一值
arr = np.array([1,2,2,3,3,3])
去重
np.unique(arr)
去重 + 计数
vals, counts = np.unique(arr, return_counts=True)
空值(NaN)相关问题
arr = np.array([1, 2, np.nan, 4])
判断是否 NaN
np.isnan(arr)
注意:np.nan == np.nan 是 False,正确删除 NaN
arr[~np.isnan(arr)]
算时自动跳过 NaN
np.nanmean(arr)
np.nansum(arr)
np.nanstd(arr)
为什么要设置 dtype?
- 控制内存占用(int8/int32/float64)
- 避免溢出、计算错误
- 提升运算速度
arr = np.array([1,2,3], dtype=np.float32)
随机数生成
0~1 均匀分布
np.random.rand(3,3)
标准正态分布
np.random.randn(3,3)
整数随机
np.random.randint(0,10, size=(2,2))
打乱顺序
np.random.shuffle(arr)
设置随机种子,保证可复现
np.random.seed(42)
Matplotlib + Seaborn
Matplotlib 是什么?Seaborn 是什么?关系?
- Matplotlib:Python 最基础的绘图库,可以画几乎所有静态图表,自由度极高。
- Seaborn:基于 Matplotlib 封装的统计可视化库,语法更简洁、图更美观,专门用于数据分析。
- 关系:Seaborn 底层是 Matplotlib,可以互相配合使用。
数据分析常用哪些图表?分别用于什么场景?
- 折线图:看趋势、时间变化
- 柱状图 / 条形图:类别对比
- 直方图 / 核密度图:看数据分布
- 箱线图:看异常值、分位数、分布范围
- 散点图:看变量相关性
- 热力图:看相关系数矩阵
- 饼图:看占比
Matplotlib 两张图:figure /axes 是什么?
figure:画布,整张图的容器axes:子图,真正画图的区域- 现在推荐用 OO 风格:
fig, ax = plt.subplots()
Matplotlib-绘制最简单的折线图
import matplotlib.pyplot as plt
import numpy as np
x = [1,2,3,4,5]
y = [2,4,6,8,10]
# 创建画布
fig, ax = plt.subplots(figsize=(8,4))
# 画折线
ax.plot(x, y, color='red', linewidth=2, marker='o', label='y=2x')
# 标题、标签
ax.set_title('折线图示例', fontsize=14)
ax.set_xlabel('X轴')
ax.set_ylabel('Y轴')
# 图例
ax.legend()
# 网格
ax.grid(True, alpha=0.3)
plt.show()
Matplotlib-画柱状图
x = ['A','B','C','D']
y = [10,25,18,32]
fig, ax = plt.subplots()
ax.bar(x, y, color='steelblue')
ax.set_title('柱状图')
plt.show()
Matplotlib-画直方图(看分布)
data = np.random.randn(1000)
fig, ax = plt.subplots()
ax.hist(data, bins=30, alpha=0.7)
ax.set_title('数据分布')
plt.show()
Matplotlib-画散点图(看相关性)
x = np.random.randn(100)
y = x * 2 + np.random.randn(100)
fig, ax = plt.subplots()
ax.scatter(x, y, s=20, alpha=0.6)
ax.set_title('散点图')
plt.show()
Matplotlib-画子图(多图并排)
fig, (ax1, ax2) = plt.subplots(1,2, figsize=(10,4))
ax1.plot([1,2,3],[4,5,6])
ax2.bar(['a','b'],[10,20])
plt.tight_layout() # 自动调整间距
plt.show()
Matplotlib-如何保存图片?
dpi=300高清bbox_inches='tight'防止标题被截断
plt.savefig('test.png', dpi=300, bbox_inches='tight')
Matplotlib-中文乱码怎么解决?
plt.rcParams['font.sans-serif'] = ['SimHei'] # 黑体
plt.rcParams['axes.unicode_minus'] = False # 负号正常显示
Seaborn-绘制柱状图(带误差棒 / 分组)
import seaborn as sns
import pandas as pd
df = pd.DataFrame({
'major':['工','工','理','理'],
'score':[80,85,75,78]
})
sns.barplot(data=df, x='major', y='score', palette='viridis')
plt.title('各专业平均分')
plt.show()
Seaborn-箱线图(识别异常值)
# 单变量箱线图
sns.boxplot(y=df['score'])
# 分组箱线图
sns.boxplot(data=df, x='major', y='score')
plt.show()
Seaborn-热力图(相关系数矩阵)
用途:看变量之间相关性,常用于生源、分数、深造率分析。
# 构造相关矩阵
corr = df.corr()
# 画热力图
sns.heatmap(corr, annot=True, cmap='coolwarm', fmt='.2f')
plt.title('相关性热力图')
plt.show()
Seaborn-散点图 + 拟合线(看线性关系)
sns.regplot(data=df, x='score', y='advance_rate')
plt.title('分数与深造率关系')
plt.show()
Seaborn-核密度图 / 分布对比
sns.kdeplot(df['score'], fill=True)
plt.title('分数分布')
plt.show()
Seaborn-分面图 FacetGrid(按类别分别画图)
g = sns.FacetGrid(df, col='major')
g.map(plt.hist, 'score')
plt.show()
Matplotlib vs Seaborn 区别?
- Matplotlib:底层、灵活、代码多、适合自定义
- Seaborn:高层、简洁、美观、适合统计分析、快速出图
- 数据分析日常:Seaborn 快速画图 + Matplotlib 微调细节
箱线图、直方图、散点图分别用来干什么?
- 箱线图:异常值 + 分位数 + 分组对比
- 直方图:数据分布(是否正态、偏态)
- 散点图:两个变量相关性、趋势
什么时候用热力图?
- 展示相关系数矩阵
- 展示交叉表、频次表
- 常用于多维度指标相关性分析(如生源分数、毕业率、深造率)
如何让图表更专业、更适合汇报?
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(figsize=(10,5))
sns.barplot(...)
plt.title('...', fontsize=14)
plt.xlabel(...)
plt.ylabel(...)
plt.tight_layout()
plt.savefig('xxx.png', dpi=300)
做数据分析时可视化流程是什么?
- 先看数据整体:直方图 / 核密度图看分布
- 看异常值:箱线图
- 看对比:柱状图 / 条形图
- 看趋势:折线图
- 看相关性:散点图 + 热力图
- 最后用 Matplotlib 统一调整字体、标题、尺寸,导出高清图用于报告。
SciPy
SciPy 是什么,在数据分析里用来干什么?
SciPy 是基于 NumPy 的科学计算库,最常用在统计检验。
数据分析中主要用于:A/B 测试、差异显著性检验、相关性分析。
常见场景:
- 不同专业深造率是否有显著差异
- 不同生源分数是否存在真实差别
- 政策 / 干预前后效果是否显著
- 两个变量是否相关
什么是 p 值?
p 值是假设检验成立的概率。
常用显著性水平 α = 0.05
- p < 0.05:差异显著,拒绝原假设
- p > 0.05:差异不显著,不能认为有真实差别
常见统计检验怎么选?
- 两组连续数据比较 → t 检验
- 多组连续数据比较 → 方差分析 ANOVA
- 两个分类变量关系 → 卡方检验
- 两个连续变量相关性 → 皮尔逊 / 斯皮尔曼相关
- 非正态分布数据 → 秩和检验(Mann-Whitney U)
导入scipy
from scipy import stats
import numpy as np
正态性检验,如何检验一组数据是否符合正态分布?
t 检验前提是数据正态,否则要用秩和检验。
# 生成测试数据
data = np.random.normal(0, 1, 1000)
# Shapiro-Wilk 检验(小样本)
stat, p = stats.shapiro(data)
# Kolmogorov-Smirnov 检验
stat, p = stats.kstest(data, 'norm', args=(np.mean(data), np.std(data)))
print(f'p值={p:.3f}')
# p>0.05 → 符合正态
# p<0.05 → 非正态
独立样本 t 检验
比较 A/B 两组均值是否有显著差异
group1 = np.array([80,82,85,87,81])
group2 = np.array([75,77,72,76,74])
# 独立样本 t 检验
stat, p = stats.ttest_ind(group1, group2)
print(f't统计量={stat:.3f}, p值={p:.3f}')
# 判断
if p < 0.05:
print("差异显著")
else:
print("差异不显著")
配对样本 t 检验
同一批学生干预前 vs 干预后成绩是否显著提高
用途:政策效果、教学干预、活动效果评估。
before = [70,72,75,68,73]
after = [76,78,82,74,79]
stat, p = stats.ttest_rel(before, after)
单样本 t 检验
样本均值是否显著不等于某个理论值
例如:本校平均分是否显著高于全省平均分
data = [82,81,85,83,80]
pop_mean = 75 # 全省均值
stat, p = stats.ttest_1samp(data, pop_mean)
方差分析 ANOVA(多组比较)
3 个及以上专业的分数是否存在显著差异
g1 = [80,82,85]
g2 = [75,77,79]
g3 = [70,72,71]
stat, p = stats.f_oneway(g1, g2, g3)
# p<0.05 → 至少一组有差异
卡方检验(分类变量关联性)
高频场景:
- 性别 vs 升学
- 生源地 vs 毕业情况
- 专业 vs 就业类型
输入是列联表(交叉表)
# 列联表:行=性别,列=是否深造
table = [[50, 100],
[30, 120]]
chi2, p, dof, expected = stats.chi2_contingency(table)
# p<0.05 → 两个分类变量显著相关
皮尔逊相关系数(线性相关)
- corr 接近 1 → 强正相关
- corr 接近 -1 → 强负相关
- p<0.05 → 相关显著
x = [1,2,3,4,5]
y = [2,4,5,7,8]
corr, p = stats.pearsonr(x, y)
斯皮尔曼等级相关(非参数,不要求正态)
数据不符合正态时用斯皮尔曼。
corr, p = stats.spearmanr(x, y)
秩和检验(非参数替代 t 检验)
数据非正态时,不能用 t 检验,用 Mann-Whitney U
stat, p = stats.mannwhitneyu(group1, group2)
t 检验和方差分析的区别?
- t 检验:两组比较
- ANOVA:三组及以上
什么时候用非参数检验?
- 样本量小
- 数据明显不符合正态分布
- 存在极端异常值
相关系数 ≠ 因果关系?
相关只能说明一起变化,不能判断谁影响谁,也可能是第三方因素导致。
p 值很小代表什么?
代表两组差异极不可能是随机误差造成的,可以认为存在真实差异。
数据分析完整统计检验流程?
- 看数据分布 → 正态检验
- 正态 → t 检验 / ANOVA
- 非正态 → 秩和检验
- 分类变量 → 卡方检验
- 看相关性 → 皮尔逊 / 斯皮尔曼
- 看 p 值判断是否显著
Statsmodels
Statsmodels 是什么?和 Sklearn 有什么区别?
Statsmodels 是 Python 用于统计建模、统计推断的库,侧重假设检验、p 值、置信区间、R²、残差分析等统计结果。
Sklearn 侧重预测、机器学习,不重视统计指标。
数据分析常用场景:
- 线性回归 / 逻辑回归
- 时间序列(ARIMA)
- 方差分析
- 拟合后看影响因素是否显著
总结
- 想解释因素影响、看显著性 → Statsmodels
- 想预测结果、准确率 → Sklearn
线性回归用来干什么?
分析哪些变量对目标有显著影响
量化影响大小(回归系数)
控制其他变量后,看某个因素的净影响
常用于:
- 分数 / 深造率受哪些因素影响
- 生源、性别、专业对毕业的影响
- 政策效果评估
最小二乘线性回归代码
import statsmodels.api as sm
import pandas as pd
import numpy as np
# 构造数据
df = pd.DataFrame({
'score': [80,85,77,79,82,83], # 因变量 Y
'study_hour': [5, 7, 4, 5, 6, 5], # 自变量 X1
'age': [19,20,19,18,20,19] # X2
})
# 1. 自变量 X 必须加常数项(截距)
X = df[['study_hour', 'age']]
X = sm.add_constant(X) # 必须加!
y = df['score']
# 2. 拟合模型
model = sm.OLS(y, X).fit()
# 3. 输出完整统计结果
print(model.summary())
注释:
add_constant不加会没有截距,结果错误.summary()是面试亮点:能看懂统计报表
如何看回归结果 summary?
1. R-squared(R²)
模型解释力,越接近 1 越好。
2. coef(系数)
变量每增加 1,y 变化多少。正 = 正向影响,负 = 负向影响。
3. P>|t|(p 值)
- p < 0.05:变量显著
- p > 0.05:不显著,可剔除
4. std err / t
系数的统计显著性。
5. 残差诊断(JB、Prob (JB))
看残差是否正态,模型是否合理。
总结
先看 R² 判断解释力度,再看每个变量的 coef 方向与大小,最后看 p 值判断是否显著,同时观察残差是否满足正态假设。
逻辑回归(分类问题)
用于二分类问题:是否毕业、是否深造、是否就业、是否报到等。
# 因变量是 0/1
y = np.array([1,1,0,1,0,1])
# 建模
model = sm.Logit(y, X).fit()
print(model.summary())
输出看:
- coef:影响方向
- P>|z|:是否显著
- 预测概率:
model.predict(X)
线性回归有哪些基本假设?
- 线性:X 与 Y 线性关系
- 独立:样本相互独立
- 正态:残差近似正态分布
- 同方差:残差方差恒定
- 无多重共线性:自变量之间不高度相关
多重共线性怎么判断、怎么处理?
判断
- 看系数符号反常、p 值不显著
- 计算VIF(方差膨胀因子)
from statsmodels.stats.outliers_influence import variance_inflation_factor
vif = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
判断标准
- VIF > 10:严重共线性
- VIF > 5:轻度共线性
处理
- 删除其中一个变量
- 合并变量
- 正则化(ridge/lasso)
异常值 / 强影响点怎么检测?
# Cook 距离
infl = model.get_influence()
cook = infl.cooks_distance[0]
Cook 距离大 → 强影响点,需要检查。
Statsmodels 时间序列能干什么?
- ARIMA / SARIMA 预测
- 平稳性检验(ADF)
- 白噪声检验
- 趋势、季节性分析
ADF 平稳性检验
from statsmodels.tsa.stattools import adfuller
stat, p, _, _, _, _ = adfuller(ts)
# p < 0.05 → 序列平稳
什么时候用 Statsmodels,什么时候用 Sklearn?
- 做统计推断、看显著性、解释影响因素、写分析报告 → Statsmodels
- 做预测、分类、模型准确率 → Sklearn
回归系数(coef)代表什么?
控制其他变量不变的情况下,该变量每增加 1,因变量平均变化多少。
p < 0.05 代表什么?
该变量对 Y 有显著影响,不是随机误差造成的。
R² 是什么意思?太低怎么办?
R² 表示模型能解释 Y 的方差比例。
太低说明:
- 缺失重要变量
- 关系不是线性
- 噪声太大
用 Statsmodels 做过什么业务分析?
用 Statsmodels 做过影响因素分析,比如分析生源分数、学习时长、专业等变量对深造率 / 毕业率的影响,通过回归系数和 p 值判断哪些因素显著,最终给出可解释的分析结论,而不只是预测
Scikit-learn(sklearn)
Scikit-learn 是什么,在数据分析里用来干什么?
Python 里最通用的机器学习库,封装了几乎所有经典算法。
数据分析里主要用于:
- 分类 / 预测(是否升学、是否报到、是否流失)
- 特征重要性分析
- 聚类(用户分群、生源分群)
- 数据预处理(归一化、编码、降维)
和 Statsmodels 区别:
- sklearn:重预测、重准确率
- Statsmodels:重统计推断、重显著性、重解释
机器学习四大类
- 分类:预测类别(0/1,是否深造)
- 回归:预测连续值(分数、收入、升学概率)
- 聚类:无标签分组(用户分层、生源分层)
- 降维:PCA 等,简化特征、可视化
模型构建标准流程
- 数据读取与探索
- 特征工程(缺失值、编码、归一化)
- 划分训练集/测试集 train_test_split
- 模型初始化
- 训练 model.fit(X_train, y_train)
- 预测 model.predict(X_test)
- 评估指标(准确率、MAE、AUC 等)
- 模型优化与特征重要性
分类特征编码:OneHot、LabelEncoder
性别、专业等离散特征 → OneHotEncoder
有序类别(低/中/高)→ LabelEncoder
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
数值特征归一化 / 标准化
标准化(均值0方差1)→ 大多数模型首选
归一化(0~1)→ 神经网络、距离模型
from sklearn.preprocessing import StandardScaler, MinMaxScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
缺失值处理 SimpleImputer
均值/中位数/众数填充
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy='median')
数据集划分
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
线性回归(预测连续值,如分数)
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X_train, y_train)
pred = model.predict(X_test)
逻辑回归(二分类,如是否升学、是否就业)
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X_train, y_train)
pred = model.predict(X_test)
pred_prob = model.predict_proba(X_test)[:,1]
决策树 / 随机森林(最常用、最强业务解释性)
- 能输出特征重要性 feature_importances_
- 不需要强特征标准化
- 擅长非线性关系
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_estimators=100)
rf.fit(X_train, y_train)
K-Means 聚类(用户分层、生源分层)
用途:
- 把学生分成:高分稳定型、潜力型、待关注型
- 做精细化运营 / 培养策略
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3, random_state=42)
labels = km.fit_predict(X_scaled)
PCA 降维
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
分类模型指标有哪些?
- 准确率 Accuracy:总体预测对的比例
- 精确率 Precision:预测为 1 的里面真的是 1
- 召回率 Recall:真实 1 里被预测出来的比例
- F1:精确率与召回率平衡
- AUC / ROC:二分类最常用、最靠谱
from sklearn.metrics import accuracy_score, precision_score, recall_score, roc_auc_score
回归模型指标有哪些?
- MAE 平均绝对误差
- MSE/RMSE 均方误差 / 平方根误差
- R² 解释程度(越接近 1 越好)
from sklearn.metrics import mean_absolute_error, r2_score
聚类评估有哪些?
- 轮廓系数
- Calinski-Harabasz 指数
过拟合是什么?怎么解决?
模型在训练集上表现特别好,但在测试集 / 新数据上表现很差。
原因:模型把噪声、偶然规律也学会了,没有学到真正通用的模式。
表现:
- 训练集准确率 / 误差很优秀
- 测试集准确率骤降、误差飙升
- 模型太复杂,泛化能力差
解决方案:
1. 增加数据
- 数据越多,噪声影响越小,模型越容易学到真实规律。
2. 降低模型复杂度
- 线性模型:减少特征
- 决策树:减小深度、减少叶子节点
- 少用复杂模型(如深度树、复杂神经网络)
3. 正则化(最常用)
- L1 正则:让部分特征系数变为 0,自动做特征选择
- L2 正则:压缩系数大小,防止极端权重
- 逻辑回归、线性回归常用:
LogisticRegression(C=0.1)C 越小,正则越强
4. 剪枝(树模型专用)
- 限制树深度
max_depth - 限制叶子节点最小样本数
min_samples_leaf - 提前停止生长
5. 特征选择 / 降维
- 删除无关、冗余、噪声特征
- 使用 PCA、方差筛选、相关性筛选
6. 早停 Early Stopping
- 训练时监控验证集误差,不再提升就停止,防止过度学习。
7. 集成模型(简单有效)
- 随机森林、XGBoost 自带抗过拟合能力,比单棵树稳得多。
总结
过拟合就是模型在训练集表现很好,但泛化能力差,在新数据上表现差。
解决方法主要有:增加数据、降低模型复杂度、使用正则化、特征选择、剪枝,以及用随机森林这类集成模型。
欠拟合是什么?怎么解决?
模型在训练集上表现就很差,测试集也很差
原因:模型太简单、学习能力不足,连数据里的基本规律都没学会
表现:
- 训练集误差大、准确率低
- 测试集同样差
- 模型偏差(Bias)过高
解决方案:
1. 增加更有用的特征
- 手工构造特征(组合特征、交叉特征、时间特征等)
- 引入更多维度信息
2. 提高模型复杂度
- 换成更复杂的模型:线性 → 树模型 / 集成模型
- 加深树深度、增加叶子节点
3. 减少正则化
- 减小正则化强度
- 线性模型调大
C值 - 树模型减少剪枝
4. 去掉不必要的降维、特征筛选
- 别把有用信息过滤掉了
5. 训练更充分
- 增加迭代次数
- 调整学习率
| 欠拟合 Underfitting | 过拟合 Overfitting | |
|---|---|---|
| 训练集效果 | 差 | 很好 |
| 测试集效果 | 差 | 很差 |
| 模型复杂度 | 太简单 | 太复杂 |
| 核心问题 | 高偏差,没学会规律 | 高方差,学了噪声 |
| 解决思路 | 加特征、加复杂度 | 降复杂度、正则、剪枝、加数据 |
总结
欠拟合是模型太简单,在训练集上都学不好,偏差过高。
解决方法主要是:增加有效特征、提高模型复杂度、减少正则化。
而过拟合是模型太复杂,学了噪声,泛化差,需要降复杂度、正则化、剪枝、增加数据等方式解决。
什么是交叉验证?
交叉验证(Cross Validation)就是:把数据集分成多份,轮流用其中一部分当验证集,多次训练评估,最后取平均结果,让模型评估更可靠。
为什么要用交叉验证?
- 只分一次 train/test,结果容易碰巧好 / 碰巧差,不稳定。
- 交叉验证能避免运气成分,更真实地反映模型泛化能力。
- 防止你因为单次划分运气好,高估或低估模型效果。
最常用的:K 折交叉验证(K-Fold)
流程(以 5 折 为例):
- 把数据平均分成 5 份
- 第 1 次:用 1,2,3,4 份训练,第 5 份验证
- 第 2 次:用 1,2,3,5 份训练,第 4 份验证
- …… 依次轮换
- 最后得到 5 个分数,取平均值 作为最终评估结果
from sklearn.model_selection import cross_val_score
# cv=5 就是 5 折交叉验证
scores = cross_val_score(model, X, y, cv=5)
# 平均分数
print(scores.mean())
总结
交叉验证是把数据集分成若干份,轮流作为验证集来多次评估模型,最后取平均结果。目的是让模型评估更稳定、可靠,避免单次划分带来的偶然性,更准确地反映模型的泛化能力。
扩展
- K 一般取多少?常用 5 折 或 10 折
- 什么时候用留一法(LOOCV)?数据特别少的时候
- 交叉验证的作用?
- 评估模型效果
- 调参(网格搜索)
- 对比不同模型
网格搜索 GridSearchCV
GridSearchCV = 穷举所有参数组合 + 交叉验证(CV)自动选出最好的参数。
- 模型里有很多超参数:比如随机森林的
max_depth、n_estimators逻辑回归的C(正则强度) - 人工一个个试太慢、不准
- GridSearchCV 自动遍历所有参数组合,用交叉验证打分,选出最优一组
基本流程
- 定义一个参数网格(字典):要试哪些值都列出来
- 遍历每一种参数组合
- 每组参数都做 K 折交叉验证
- 记录平均分数
- 最终输出:最优参数 + 最优分数
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
# 模型
rf = RandomForestClassifier()
# 定义要搜索的参数网格
param_grid = {
'max_depth': [3, 5, 7], # 树深度
'n_estimators': [50, 100, 200] # 树数量
}
# 网格搜索 + 5折交叉验证
grid = GridSearchCV(
estimator=rf,
param_grid=param_grid,
cv=5, # 5折交叉验证
scoring='roc_auc'
)
grid.fit(X_train, y_train)
# 结果
print("最优参数:", grid.best_params_)
print("最优分数:", grid.best_score_)
# 用最优模型预测
best_model = grid.best_estimator_
- 优点:结果客观、自动、可靠
- 缺点:参数多的时候非常慢(暴力穷举)
- 更快的替代:
RandomizedSearchCV(随机搜索,不遍历全部)
总结
GridSearchCV 是一种自动调参方法,它会穷举我们指定的所有参数组合,并通过交叉验证评估每组参数的效果,最终自动选出泛化能力最好的参数组合,让模型效果更稳定、避免人工调参的主观性。
特征重要性怎么看?
特征重要性 = 这个特征对模型预测贡献有多大,值越大,说明这个特征越关键。
1. 树模型(最常用)
- 随机森林 Random Forest
- 决策树
- GBDT / XGBoost / LightGBM
2. 线性模型
- 线性回归
- 逻辑回归(看系数绝对值大小,但要先标准化)
3. 不推荐
KNN、SVM、神经网络 → 很难直接看重要性
树模型代码
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor()
model.fit(X_train, y_train)
# 查看特征重要性
importances = model.feature_importances_
# 转成DataFrame方便看
import pandas as pd
fi = pd.DataFrame({
'feature': X_train.columns,
'importance': importances
}).sort_values('importance', ascending=False)
print(fi)
原理
- 树在分裂时,用这个特征减少了多少误差(基尼系数 / 方差)
- 减少越多 → 越重要
线性模型代码
- 必须先标准化,否则量纲不同不能比
- 系数绝对值越大 → 越重要
- 正负代表影响方向
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X_train_scaled, y_train) # 必须先标准化!
coef = pd.DataFrame({
'feature': X_train.columns,
'coef': model.coef_
}).sort_values('coef', key=abs, ascending=False)
总结
树模型(如随机森林)可以直接通过 feature_importances_ 查看特征重要性,值越大表示该特征对预测贡献越大。线性模型需要先做标准化,再看系数的绝对值大小。在分析中常用它来识别关键影响因素。
注意
- 特征重要性高 ≠ 因果关系,只是相关性
- 多重共线性会让重要性失真
- 树模型容易偏好高基数特征(如 ID、类别多的特征)
- 数据分析里最常用:随机森林看特征重要性,用来做因素分析
分类模型评估指标
4 个基础指标
- TP(真正例):真 1,预测 1
- FN(假反例):真 1,预测 0
- FP(假正例):真 0,预测 1
- TN(真反例):真 0,预测 0
5 大核心指标
1. 准确率 Accuracy
公式:(TP + TN) / 全部
- 所有样本里预测对的比例
- 缺点:类别不平衡时完全没用(比如 99% 是 0,瞎猜都能 99% 准确率)
2. 精确率 Precision(查准率)
预测为 1 的里面,有多少是真 1
- TP / (TP + FP)
- 场景:宁可少抓,不能抓错例如:垃圾邮件识别、推荐系统
3. 召回率 Recall(查全率)
真实为 1 的里面,有多少被找出来
- TP / (TP + FN)
- 场景:宁可错抓,不能漏掉例如:金融欺诈、疾病检测、风险识别
4. F1 分数
精确率与召回率的调和平均
- 2 * (Precision * Recall) / (Precision + Recall)
- 两者都要兼顾时用
5. AUC(最常用、最重要)
- 衡量模型整体区分能力
- 范围 0.5 ~ 1
- 0.5 = 瞎猜
- 0.7~0.8 = 可用
- 0.8~0.9 = 很好
- 0.9+ = 极强
- 优点:不受类别不平衡影响
- 数据分析面试必说 AUC
总结
分类模型常用的评估指标有:准确率、精确率、召回率、F1 和 AUC。准确率在不平衡数据下不可靠,所以我一般优先看 AUC。业务需要少误判时看精确率,需要不漏掉目标时看召回率,两者兼顾用 F1。
标准化和归一化区别?
| 归一化 MinMaxScaler | 标准化 StandardScaler | |
|---|---|---|
| 范围 | 0~1 | 均值 0,方差 1 |
| 受异常值影响 | 很大 | 较小 |
| 数据分布要求 | 无 | 近似正态更稳 |
| 适用场景 | 固定范围、距离模型 | 大多数机器学习模型 |
- StandardScaler:均值 0 方差 1 → 通用
- MinMaxScaler:0~1 → 距离类模型(KNN、KMeans、神经网络)
用 归一化 的场景:
- KNN、K-Means、SVM 等距离 - based 模型
- 神经网络
- 需要把值限定在 0~1 之间
用 标准化 的场景:
- 线性回归、逻辑回归
- 随机森林、树模型也可以用(不强制)
- 数据存在异常值时更稳
- 日常建模默认优先用标准化
树模型不需要标准化?树模型按大小排序分裂,和数值绝对大小无关。
总结
归一化是把数据缩放到 0~1,标准化是把数据变成 均值 0 方差 1。归一化受异常值影响大,适合距离类模型;标准化更稳健,日常建模一般优先用标准化。
类别特征用什么编码?
一、无序分类特征(性别、专业、城市、省份)
1. One-Hot 编码(独热编码)
最常用、默认首选
- 一列变多列,只有 0/1
- 不引入大小关系
- 类别不多时用
适用:性别、专业、省份、学历等
二、有序分类特征(低 / 中 / 高、小 / 大、差 / 良 / 优)
2. 标签编码 / 有序编码 LabelEncoder / OrdinalEncoder
- 按顺序映射成 0,1,2…
- 保留大小关系
适用:成绩等级、风险等级、教育阶段
三、高基数类别(用户 ID、手机号、学校编号、超多省份)
3. 频率编码(计数编码)
用出现次数代替类别
4. 目标编码(均值编码)
用该类别对应的目标均值编码(比如:某专业深造率 = 编码)
适用:类别特别多、one-hot 会爆炸的场景
四、树模型 vs 线性模型怎么选?
树模型(随机森林、XGBoost)
- 可以直接用标签编码
- 也可以用 目标编码
- 一般不用 one-hot(会影响效果)
线性模型 / 逻辑回归 / KNN / 神经网络
- 必须用 One-Hot
- 不能直接用标签编码(会引入虚假大小关系)
总结
类别不多的无序特征用 One-Hot 编码;有序特征用 标签编码或有序编码;类别特别多的高基数特征用 频率编码或目标编码;线性模型必须用 One-Hot,树模型可以直接用标签编码或目标编码。
用 sklearn 做过什么实际分析?
用 sklearn 做过学生升学概率预测模型,用随机森林看特征重要性,识别出影响深造的关键因素,并用聚类做学生分层,辅助制定针对性培养策略。
类别不平衡(比如很少人违约、很少人辍学)怎么办?
1. 更换评估指标(最简单、第一步必做)
- 不用 Accuracy 准确率(会骗人)
- 改用:
- AUC
- F1 分数
- Recall 召回率
2. 调整样本(最常用)
(1)过采样 Oversampling
- 对少数类重复采样、生成样本
- 优点:用足少量信息
- 缺点:容易过拟合
(2)欠采样 Undersampling
- 对多数类随机删掉一些
- 优点:快、简单
- 缺点:丢信息
(3)SMOTE 算法
- 在少数类样本之间插值生成新样本
- 比简单过采样更不容易过拟合
- 最常用、最推荐
3. 类别权重 Class Weight
直接在模型里给少数类更高的权重,惩罚错分。
代码示例:
# 逻辑回归
LogisticRegression(class_weight='balanced')
# 随机森林
RandomForestClassifier(class_weight='balanced')
不用改数据,效果往往很好。
4. 阈值移动 Threshold Moving
模型默认阈值 0.5,我们调低阈值(如 0.2、0.3),让模型更容易预测为少数类,提高召回率。
5. 集成方法
- 对多数类多次欠采样
- 训练多个模型
- 最后投票不容易过拟合,效果稳定。
总结
类别不平衡时,首先不使用准确率,改用 AUC、F1、召回率。然后可以用SMOTE 过采样或欠采样平衡数据。最简单有效的是直接设置 class_weight='balanced' 调整类别权重。也可以通过降低阈值提高少数类的识别率。
逻辑回归 / 决策树 / 随机森林怎么选?
- 逻辑回归:线性模型,可解释性极强,速度快
- 决策树:非线性,可解释,但容易过拟合
- 随机森林:多棵树集成,效果最好、最稳,泛化能力强
1. 逻辑回归 Logistic Regression
- 类型:线性模型
- 优点:
- 速度极快
- 可解释性最强(能看系数、显著性、概率)
- 不容易过拟合
- 输出是概率,方便业务理解
- 缺点:
- 只能学线性关系
- 无法处理复杂交互
- 适合场景:
- 需要清晰解释因素影响(如:哪些因素影响升学 / 违约)
- 数据量很大、要求快
- 线上简单预测
2. 决策树 Decision Tree
- 类型:非线性、规则模型
- 优点:
- 可解释(能画树、能读规则)
- 不用标准化
- 能处理非线性、特征交互
- 缺点:
- 非常容易过拟合
- 不稳定,数据一变树就大变
- 适合场景:
- 小数据、需要白盒规则
- 做初步探索分析
- 一般不直接用于最终建模
3. 随机森林 Random Forest
- 类型:集成模型(多棵决策树投票)
- 优点:
- 效果通常最好
- 抗过拟合、稳定性强
- 能处理非线性、异常值、多重共线性
- 能输出特征重要性(数据分析神器)
- 缺点:
- 比逻辑回归慢一点
- 可解释性比逻辑回归差(黑盒)
- 适合场景:
- 通用首选模型
- 效果优先、不知道用啥时就用它
- 找关键影响因素(特征重要性)
- 分类 / 回归都强
怎么选?
1. 优先选 逻辑回归
- 需要强可解释性
- 要报告系数、显著性、影响方向
- 数据大、要求速度
2. 优先选 随机森林
- 追求预测效果
- 存在非线性、特征交互
- 想看特征重要性
- 不知道用啥模型时的默认首选
3. 一般不单独用决策树
- 容易过拟合、不稳定
- 要用也用剪枝,或直接升级成随机森林
总结
逻辑回归线性、可解释性最强,适合需要清晰说明因素影响的场景;决策树能处理非线性,但容易过拟合;随机森林是集成模型,效果最稳定、泛化能力最强,还能输出特征重要性,是我做数据分析时的通用首选模型。
K-Means
K-Means 是什么?
K-means 属于 Scikit-learn,是无监督聚类算法,把相似的样本自动分成 K 个簇。不需要标签 y,只需要特征 X。
from sklearn.cluster import KMeans
K-Means 的执行过程?
- 随机选 K 个点作为初始质心
- 每个样本计算到质心距离,归到最近的簇
- 重新计算每个簇的新质心(均值)
- 重复 2、3,直到质心不再变化
归类 → 求中心 → 再归类
K 怎么确定?
- 手肘法(Elbow Method)看簇内误差平方和 SSE 下降趋势,拐弯点就是 K
- 轮廓系数(Silhouette Score)越大聚类效果越好
- 结合业务:比如分 3 类:高价值 / 中等 / 普通
K-Means 优缺点?
优点
- 简单、快、好用
- 大数据集也能跑
- 客户分群、用户分层神器
缺点
- 需要手动指定 K
- 对异常值极敏感
- 对量纲敏感(必须标准化)
- 只能发现凸、球形簇,不规则形状不行
- 初始质心影响结果(要设 random_state)
K-Means 为什么要标准化 / 归一化?
因为 K-Means 基于距离(欧氏距离)。如果特征量纲不一样(比如年龄 0-100,收入 0-100 万),收入会直接主导距离,结果失真。
必须用 StandardScaler 或 MinMaxScaler。
K-Means 遇到异常值怎么办?
- 先删异常值
- 或用 K-Medians(更稳,但 sklearn 没有)
- 或用 DBSCAN 密度聚类替代
用 K-Means 做过什么?
做用户 / 学生分层,比如根据成绩、活跃度、消费等特征聚类,分成高潜力、普通、待关注群体,用于精细化运营和策略分析。
A/B 测试 & 统计学
A/B 测试
什么是 A/B 测试?
将用户随机分成两组:
- A 组:对照组(原有版本)
- B 组:实验组(新策略 / 新功能)通过统计假设检验判断指标差异是否显著,从而决定是否上线新版本。
为什么要做 A/B 测试?
- 避免凭感觉决策
- 量化新策略真实效果
- 降低上线风险
- 用数据证明 “有效”
A/B 测试核心指标怎么选?
- 核心:转化率、点击率、留存、人均时长、收入
- 辅助:用户结构、设备分布、地域分布原则:少而精,优先业务北极星指标
完整 A/B 测试流程是什么?
- 明确实验目的与核心指标
- 计算所需样本量 & 实验天数
- 随机分流,保证两组用户同质
- 上线实验,埋点收集数据
- 数据清洗,检查分流均匀性
- 做假设检验(t 检验 / 卡方)
- 根据 p 值、置信区间判断是否显著
- 给出结论:上线 / 不上线 / 延长实验
统计学
原假设 H0 和备择假设 H1 是什么?
- H0:两组无差异
- H1:两组有差异只有拒绝 H0,才能说 B 版本更好。
p 值是什么?
在原假设成立(两组无差异)的情况下,观察到当前差异或更极端结果的概率。
- p < 0.05 → 差异显著,认为策略有效
- p > 0.05 → 差异不显著,不能认为有效
显著性水平 α 和功效 Power
- α(一类错误):假阳性,没效果说有效果,常用 0.05
- Power = 1−β:真有效果能检测出来的概率,常用 0.8
一类错误 & 二类错误
- 一类(α):误判有效(假阳性)
- 二类(β):漏判有效(假阴性)
什么是置信区间?
有 95% 把握认为真实提升幅度落在这个区间内。
中心极限定理 CLT
样本量足够大时,样本均值近似服从正态分布,因此可以用 t 检验。
不同指标对应什么检验?
- 转化率 / 点击率(0/1 数据)→ 卡方检验 / 双比例 z 检验
- 时长、分数、金额(连续值)→ 独立样本 t 检验
- 数据不正态、异常值多→ Mann-Whitney U 检验(非参数)
样本量由什么决定?
- 基线转化率
- 最小可检测提升 MDE
- 显著性水平 α=0.05
- 统计功效 Power=0.8
期望提升越小,需要样本量越大。
实验没到样本量能停吗?
不能。样本不足 → 检验功效低 → 容易得出错误不显著结论。
为什么必须随机分流?
保证两组用户性别、年龄、活跃度、设备等分布一致,排除干扰变量。
什么是辛普森悖论?
整体看 B 更好,按维度拆分后 A 更好。原因:用户结构分布不均。避免:随机分流 + 分层分析 + 检查组间一致性。
peeking 问题(多次偷看结果)
频繁看结果会提高一类错误,导致假显著。解决:
- 固定实验周期
- 不中途停止
- 使用多重检验校正
实验前要检查什么?
- 分流是否均匀(用户结构一致)
- 埋点是否正常
- 有无污染、交叉影响
- 有无系统波动(活动、节假日)
显著就一定上线吗?
不一定。还要看:
- 提升幅度是否业务显著
- 有无负向指标
- 实现成本、风险
- 长期影响
BI 工具
基础概念
什么是 BI?BI 工具用来干什么?
- BI = 商业智能
- 用途:连接数据 → 清洗加工 → 可视化 → 做仪表板 → 支撑业务决策
- 核心价值:让数据可看、可用、可自助分析
常用哪些 BI 功能?
- 连接数据库(MySQL、Hive、Excel)
- 数据建模、维度 / 指标区分
- 仪表板 Dashboard 制作
- 筛选器、参数、钻取
- 计算字段 / 聚合指标
- 定时刷新、分享权限
维度 vs 指标(度量)区别?
- 维度:看什么角度(时间、地区、产品、性别)
- 指标:看什么数值(金额、数量、转化率、次数)一句话:维度用来分组,指标用来计算
什么是钻取、下钻、联动?
- 下钻:从年→季→月→日,看更细粒度
- 钻取:点图表跳转到明细
- 联动:点一个图表,其他图表同步筛选
数据建模中,星型模型是什么?
- 一张事实表(订单、行为)
- 多张维度表(用户、商品、时间、地区)
- 优点:结构清晰、查询快、适合 BI
缓慢变化维 SCD 了解吗?
- 维度属性会变(用户改名、商品改类目)
- SCD1:直接覆盖
- SCD2:保留历史,加生效 / 失效时间面试常问:用户历史归属怎么处理
做过的典型仪表板有哪些?
通用高分回答:
- 业务概览大盘
- 用户增长 / 留存分析
- 销售 / 转化漏斗
- 渠道效果分析
- 地域 / 品类分析
Tableau
Tableau 中的维度和度量为什么颜色不同?
- 蓝色:离散(维度,分组用)
- 绿色:连续(数值 / 时间,坐标轴用)
Tableau 计算字段有哪些?
- 基础计算
- LOD 表达式(必考)
{INCLUDE}{EXCLUDE}{FIXED}
FIXED 是什么?举个例子
- 无视视图筛选,按指定维度固定聚合例:
{FIXED [用户ID]: SUM([金额])}
求每个用户的总金额,不受图表筛选影响。
Tableau 上下文筛选是什么?
优先执行的筛选,先算上下文,再算普通筛选用于优化大数据量性能。
Tableau 怎么做漏斗?
- 用条形图 / 漏斗图
- 步骤字段 + 指标
- 计算转化率
Tableau 与数据库怎么连接?
- 实时连接 Live
- 数据提取 Extract(更快)
Power BI
Power BI 三大组件
- Power Query(清洗)
- Power Pivot(建模 DAX)
- Power View(可视化)
什么是 DAX?常用函数?
- 建模与指标计算语言常用:
SUM / COUNT / AVERAGECALCULATE(核心)FILTERALL / ALLEXCEPTDIVIDERELATED
CALCULATE 作用?
修改筛选上下文,最常考
CALCULATE(SUM(金额), 渠道="APP")
Power BI 建模关系
一对多、一对一、多对多事实表多端,维度表一端
计算列 vs 度量(DAX)
- 计算列:行级别,刷新时算好
- 度量:查询时动态计算做指标用度量。
Power BI 增量刷新
大表只刷新新增部分,提升性能。
FineBI
FineBI 核心特点
- 国产 BI,自助分析强
- 大数据支持好(Hive、ClickHouse)
- 权限、企业级管控完善
- 可视化拖拽简单
FineBI 中的业务包 / 数据集
- 准备数据集 → 建立业务包
- 自助数据集自助 ETL
FineBI 计算字段
- 快速计算
- 聚合指标
- 表计算(同环比、占比、排名)
FineBI 权限体系
- 目录权限
- 数据权限(行权限、列权限)
- 部门 / 角色权限
FineBI 怎么做实时数据?
- 直连数据库
- 定时刷新
- 大屏监控
Tableau vs Power BI
- Tableau:可视化强、美观、适合复杂分析
- Power BI:便宜、和 Office 集成、DAX 强
- 企业选 Tableau;个人 / 微软生态选 Power BI
FineBI vs Tableau/Power BI
- FineBI:国产、大数据支持好、权限强、便宜
- 国内中大厂 / 传统行业用得非常多
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)