目录

SQL语法

基础语法

WHERE 和 HAVING 的区别

DISTINCT 和 GROUP BY 的区别

LIMIT 和 OFFSET

NULL 判断

聚合函数

COUNT (*)、COUNT (1)、COUNT(列)

SUM/AVG 遇到 NULL

聚合函数能否嵌套

分组统计数量、总和、平均值

分组后平均值 > 100 的组

多表 JOIN

四种 JOIN 区别

LEFT JOIN 左表一定全保留吗

JOIN 数据膨胀原因

ON 与 WHERE 在 LEFT JOIN 中区别

子查询与 WITH

相关子查询 vs 非相关子查询

EXISTS 与 IN

查询最大值所在整行

大于本组平均值

CASE WHEN

顺序匹配

行转列统计数量

CASE 可放位置

MySQL IF 与 CASE WHEN

窗口函数

三个排名函数区别

窗口函数结构

窗口函数能否放 WHERE

每组 TopN

累计和

LAG、LEAD

窗口 SUM 与 GROUP BY 区别

去重、合并、排序

UNION 与 UNION ALL

删除重复保留一条

多字段排序

NULL 排序位置

SQL 执行顺序

标准执行顺序

为什么别名不能用在 WHERE

SQL 优化

定位慢查询

索引作用

索引失效场景

不推荐 SELECT *

大表 JOIN 优化

高频手写综合题(带注释)

分组统计 + 过滤

行转列

每组 Top3

连续 N 天登录(通用模板)

环比计算

SQL应用案例

统一表结构

基础查询

查询 2024 级学生核心信息

查询未报到新生(按分数降序)

河南生源 500 分以上学生

查询含 “工程” 的专业及招生人数

去重统计生源省份数量

GROUP BY 分组聚合

各学院录取人数、报到人数、报到率

各专业录取分数统计

各省生源人数排序

男女学生人数及占比

各专业一志愿率统计

空值、去重、COUNT

统计录取分数为空的学生数

统计省份非空的有效生源数

查询重复学号的学生

删除重复学号,保留一条

多表 JOIN

各专业报到率统计

所有录取学生 + 报到状态

各专业学位授予率

三表关联:各省生源深造人数

子查询 & HAVING

分数高于全校平均分的学生

报到率低于 85% 的专业

高于本专业平均分的学生

近三年录取人数逐年上升的专业

报到 + 毕业 + 深造的学生总数

CASE WHEN

各专业总深造率

分数分段 + 深造率统计

标记高分生源(高于校平均分)

考研报名率、上岸率

毕业结果分类统计

窗口函数

专业内分数 Top10

同专业分数分位数

专业人数占学院比例

深造 / 毕业 / 报到 专项 SQL

各专业毕业率、延期毕业人数

各专业深造明细 + 总深造率

各省生源深造率

升学院校类型占比

高频简答题

Python

Pandas

Pandas作用?

如何读取 csv、excel?如何指定编码、索引?

拿到一个 DataFrame,你第一步会看什么?

如何按条件筛选行?如何选取列?

如何查看重复行、删除重复?

如何把字符串转时间?转整数?

按专业分组,统计分数的均值、最大值、人数

多表合并用什么?和 SQL 的 JOIN 对应什么?

用 Pandas 做 Excel 透视表

根据分数新增等级列(优秀 / 良好 / 及格)

如何提取年、月、日?如何按月求和?

如何识别并剔除异常值?

行转列、列转行(melt /pivot)

apply/map/applymap 区别

为什么会出现SettingWithCopyWarning这个警告?怎么解决?

如何实现 SQL 的 group by having?

导出 Excel / CSV

NumPy

NumPy作用?

NumPy 数组(ndarray)和 Python 列表 list 的区别?

创建数组的常用方式有哪些?

查看数组属性

数组索引与切片

什么是广播(broadcasting)?为什么重要?

数据归一化 / 标准化

用 NumPy 实现 3σ 异常值筛选

数组形状修改

拼接与分割

np.where 用法

去重、唯一值

空值(NaN)相关问题

为什么要设置 dtype?

随机数生成

Matplotlib + Seaborn

Matplotlib 是什么?Seaborn 是什么?关系?

数据分析常用哪些图表?分别用于什么场景?

Matplotlib 两张图:figure /axes 是什么?

Matplotlib-绘制最简单的折线图

Matplotlib-画柱状图

Matplotlib-画直方图(看分布)

Matplotlib-画散点图(看相关性)

Matplotlib-画子图(多图并排)

Matplotlib-如何保存图片?

Matplotlib-中文乱码怎么解决?

Seaborn-绘制柱状图(带误差棒 / 分组)

Seaborn-箱线图(识别异常值)

Matplotlib vs Seaborn 区别?

箱线图、直方图、散点图分别用来干什么?

什么时候用热力图?

如何让图表更专业、更适合汇报?

做数据分析时可视化流程是什么?

SciPy

SciPy 是什么,在数据分析里用来干什么?

什么是 p 值?

常见统计检验怎么选?

导入scipy

正态性检验,如何检验一组数据是否符合正态分布?

独立样本 t 检验

配对样本 t 检验

单样本 t 检验

方差分析 ANOVA(多组比较)

卡方检验(分类变量关联性)

皮尔逊相关系数(线性相关)

斯皮尔曼等级相关(非参数,不要求正态)

秩和检验(非参数替代 t 检验)

t 检验和方差分析的区别?

什么时候用非参数检验?

相关系数 ≠ 因果关系?

p 值很小代表什么?

数据分析完整统计检验流程?

Statsmodels

Statsmodels 是什么?和 Sklearn 有什么区别?

线性回归用来干什么?

最小二乘线性回归代码

如何看回归结果 summary?

逻辑回归(分类问题)

线性回归有哪些基本假设?

多重共线性怎么判断、怎么处理?

异常值 / 强影响点怎么检测?

Statsmodels 时间序列能干什么?

ADF 平稳性检验

什么时候用 Statsmodels,什么时候用 Sklearn?

回归系数(coef)代表什么?

p < 0.05 代表什么?

R² 是什么意思?太低怎么办?

用 Statsmodels 做过什么业务分析?

Scikit-learn(sklearn)

Scikit-learn 是什么,在数据分析里用来干什么?

机器学习四大类

模型构建标准流程

分类特征编码:OneHot、LabelEncoder

数值特征归一化 / 标准化

缺失值处理 SimpleImputer

数据集划分

线性回归(预测连续值,如分数)

逻辑回归(二分类,如是否升学、是否就业)

决策树 / 随机森林(最常用、最强业务解释性)

K-Means 聚类(用户分层、生源分层)

PCA 降维

分类模型指标有哪些?

回归模型指标有哪些?

聚类评估有哪些?

过拟合是什么?怎么解决?

欠拟合是什么?怎么解决?

什么是交叉验证?

网格搜索 GridSearchCV

特征重要性怎么看?

分类模型评估指标

标准化和归一化区别?

类别特征用什么编码?

用 sklearn 做过什么实际分析?

类别不平衡(比如很少人违约、很少人辍学)怎么办?

逻辑回归 / 决策树 / 随机森林怎么选?

K-Means

K-Means 是什么?

K-Means 的执行过程?

K 怎么确定?

K-Means 优缺点?

K-Means 为什么要标准化 / 归一化?

K-Means 遇到异常值怎么办?

用 K-Means 做过什么?

A/B 测试 & 统计学

A/B 测试

什么是 A/B 测试?

为什么要做 A/B 测试?

A/B 测试核心指标怎么选?

完整 A/B 测试流程是什么?

统计学

原假设 H0 和备择假设 H1 是什么?

p 值是什么?

显著性水平 α 和功效 Power

一类错误 & 二类错误

什么是置信区间?

中心极限定理 CLT

不同指标对应什么检验?

样本量由什么决定?

实验没到样本量能停吗?

为什么必须随机分流?

什么是辛普森悖论?

peeking 问题(多次偷看结果)

实验前要检查什么?

显著就一定上线吗?

BI 工具

基础概念

什么是 BI?BI 工具用来干什么?

常用哪些 BI 功能?

维度 vs 指标(度量)区别?

什么是钻取、下钻、联动?

数据建模中,星型模型是什么?

缓慢变化维 SCD 了解吗?

做过的典型仪表板有哪些?

Tableau

Tableau 中的维度和度量为什么颜色不同?

Tableau 计算字段有哪些?

FIXED 是什么?举个例子

Tableau 上下文筛选是什么?

Tableau 怎么做漏斗?

Tableau 与数据库怎么连接?

Power BI

Power BI 三大组件

什么是 DAX?常用函数?

CALCULATE 作用?

Power BI 建模关系

计算列 vs 度量(DAX)

Power BI 增量刷新

FineBI

FineBI 核心特点

FineBI 中的业务包 / 数据集

FineBI 计算字段

FineBI 权限体系

FineBI 怎么做实时数据?

Tableau vs Power BI

FineBI vs Tableau/Power BI


SQL语法

基础语法

WHERE 和 HAVING 的区别

  1. WHERE:对【原始数据行】过滤,发生在分组之前
  2. HAVING:对【分组后的结果】过滤,必须跟在 GROUP BY 后,可使用聚合函数
  3. 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 优化

定位慢查询

  1. 开启慢查询日志
  2. 使用 EXPLAIN 查看执行计划
  3. 看是否走索引、是否全表扫描、是否出现 filesort

索引作用

索引是排序结构,类似目录

作用:避免全表扫描,加速 WHERE / ORDER BY / GROUP BY

索引失效场景

  1. 字段使用函数:WHERE YEAR(date) = 2024
  2. 隐式类型转换
  3. LIKE '%xxx%' 前模糊
  4. OR 连接无索引字段
  5. 优化器认为全表更快(数据量小)

不推荐 SELECT *

  1. 查询无用字段,增加 IO
  2. 无法使用覆盖索引
  3. 表结构变化后易出错

大表 JOIN 优化

  1. 小表驱动大表
  2. 关联字段建索引
  3. 先过滤再 JOIN
  4. 避免一对多导致数据膨胀
  5. 必要时分批查询

高频手写综合题(带注释)

分组统计 + 过滤

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 的区别?

  1. 数组元素类型必须统一,列表可以混放任意类型。
  2. 数组支持矢量化运算,不用循环,速度极快。
  3. 数组支持多维结构(矩阵),更适合数值计算。
  4. 数组占用内存更小,底层连续存储。
  5. 提供大量数学 / 统计函数,适合数据分析、建模。

创建数组的常用方式有哪些?

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)

做数据分析时可视化流程是什么?

  1. 先看数据整体:直方图 / 核密度图看分布
  2. 看异常值:箱线图
  3. 看对比:柱状图 / 条形图
  4. 看趋势:折线图
  5. 看相关性:散点图 + 热力图
  6. 最后用 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 值很小代表什么?

代表两组差异极不可能是随机误差造成的,可以认为存在真实差异。

数据分析完整统计检验流程?

  1. 看数据分布 → 正态检验
  2. 正态 → t 检验 / ANOVA
  3. 非正态 → 秩和检验
  4. 分类变量 → 卡方检验
  5. 看相关性 → 皮尔逊 / 斯皮尔曼
  6. 看 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)

线性回归有哪些基本假设?

  1. 线性:X 与 Y 线性关系
  2. 独立:样本相互独立
  3. 正态:残差近似正态分布
  4. 同方差:残差方差恒定
  5. 无多重共线性:自变量之间不高度相关

多重共线性怎么判断、怎么处理?

判断

  • 看系数符号反常、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 等,简化特征、可视化

模型构建标准流程

  1. 数据读取与探索
  2.  特征工程(缺失值、编码、归一化)
  3. 划分训练集/测试集 train_test_split
  4. 模型初始化
  5. 训练 model.fit(X_train, y_train)
  6. 预测 model.predict(X_test)
  7. 评估指标(准确率、MAE、AUC 等)
  8. 模型优化与特征重要性

分类特征编码: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 均方误差 / 平方根误差
  • 解释程度(越接近 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 折 为例):

  1. 把数据平均分成 5 份
  2. 第 1 次:用 1,2,3,4 份训练,第 5 份验证
  3. 第 2 次:用 1,2,3,5 份训练,第 4 份验证
  4. …… 依次轮换
  5. 最后得到 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)?数据特别少的时候
  • 交叉验证的作用?
    1. 评估模型效果
    2. 调参(网格搜索)
    3. 对比不同模型

网格搜索 GridSearchCV

GridSearchCV = 穷举所有参数组合 + 交叉验证(CV)自动选出最好的参数。

  • 模型里有很多超参数:比如随机森林的 max_depthn_estimators逻辑回归的 C(正则强度)
  • 人工一个个试太慢、不准
  • GridSearchCV 自动遍历所有参数组合,用交叉验证打分,选出最优一组

基本流程

  1. 定义一个参数网格(字典):要试哪些值都列出来
  2. 遍历每一种参数组合
  3. 每组参数都做 K 折交叉验证
  4. 记录平均分数
  5. 最终输出:最优参数 + 最优分数
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 的执行过程?

  1. 随机选 K 个点作为初始质心
  2. 每个样本计算到质心距离,归到最近的簇
  3. 重新计算每个簇的新质心(均值)
  4. 重复 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 测试流程是什么?

  1. 明确实验目的核心指标
  2. 计算所需样本量 & 实验天数
  3. 随机分流,保证两组用户同质
  4. 上线实验,埋点收集数据
  5. 数据清洗,检查分流均匀性
  6. 假设检验(t 检验 / 卡方)
  7. 根据 p 值、置信区间判断是否显著
  8. 给出结论:上线 / 不上线 / 延长实验

统计学

原假设 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 检验(非参数)

样本量由什么决定?

  1. 基线转化率
  2. 最小可检测提升 MDE
  3. 显著性水平 α=0.05
  4. 统计功效 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 / AVERAGE
  • CALCULATE(核心)
  • FILTER
  • ALL / ALLEXCEPT
  • DIVIDE
  • RELATED

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:国产、大数据支持好、权限强、便宜
  • 国内中大厂 / 传统行业用得非常多
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐