数据分析笔试题:从SQL到业务结论的完整拆解与避坑指南 简介这份数据分析笔试题资源包面向准备数据分析岗位笔试的求职者与在校学生覆盖从统计基础到机器学习、大数据处理的完整知识链路帮助读者在真实题目中检验数据清洗、建模与业务解读能力。包内共41个文件以png图表、xlsx与xls数据表、docx文档为主辅以gif动图、pptx演示、mp4录屏及htm说明压缩包约54.78MB涵盖SQL查询、分析报告、岗前测试与多套笔试真题等模块。资源中既有前海数据等企业的岗位笔试题也有分年度、分行业的制造与批发数据图表可对照练习缺失值处理、异常值识别、假设检验、可视化选型及线性回归、决策树等算法应用。目前已有818人学习下载适合需要系统刷题、复盘解题思路并积累业务分析经验的中级学习者。1. 数据分析笔试题为什么 SQL 写对了业务结论还是错的很多人第一次做数据分析笔试题都会掉进同一个坑SQL 跑通了、Python 脚本没报错、图表也画出来了但面试官看完只回一句“结论呢”。问题不在工具在于笔试题考的不是语法而是你能不能把一句模糊的业务问题翻译成可验证的分析路径。比如“最近三个月复购率下降的原因”它背后藏着指标定义、口径对齐、维度拆解、异常归因四层动作缺一层结论就站不住。这类题在数据运营、商业分析、增长策略岗的笔试里出现频率极高适合已经会写基础查询、但一遇到开放题就不知道从哪下手的从业者。下面按我实际做题和带人的顺序把整套流程拆开讲。2. 拆解笔试题干从一句话需求到可执行分析框架2.1 先分清题目要的是描述、诊断还是预测数据分析笔试题干通常分三类。描述型问“是什么”比如“统计各渠道月度 GMV”诊断型问“为什么”比如“找出转化率下滑的主要环节”预测型问“会怎样”比如“预估下季度留存”。三类题的答题结构完全不同描述型重口径和完整性诊断型重假设和验证预测型重特征和误差说明。拿到题先花三十秒判断类型再决定后面写不写模型。我见过不少人一上来就上回归结果题目只想要一个分组对比反而显得不会抓重点。判断方法很简单题干里出现“下降/上升/异常/原因”就是诊断型出现“预计/预估/趋势”就是预测型出现“统计/汇总/分布”就是描述型。混合型题目按主问题定类型次要问题用附注处理。2.2 把业务词翻译成字段和口径题干里的“活跃用户”“有效订单”“高价值客户”都不是天然存在的字段需要你给出可执行定义。这一步是笔试拉开差距的地方。常见做法是先列一张口径对照表把每个业务词映射到表、字段、过滤条件和时间窗口。业务词可执行定义涉及字段注意点活跃用户统计周期内至少一次登录user_id, login_time去重排除内部账号有效订单支付成功且未全额退款order_id, pay_status, refund_amount部分退款是否剔除要写明高价值客户近90天消费额前20%user_id, pay_amount分位数按全量还是分渠道这张表不用写进最终答案但一定要在草稿里过一遍。口径不清后面所有数字都是空中楼阁。2.3 用假设树把诊断型题目拆到可验证诊断型题目最怕东查一下西查一下。我的习惯是先画假设树根节点是现象第一层按“量、价、频、人”拆第二层按渠道、地区、品类、新老客拆叶子节点必须是能直接用 SQL 验证的粒度。-- 假设复购率下降主要由新客首购后30天未复购导致 -- 按注册月份和新老客拆分复购率 SELECT DATE_TRUNC(month, u.register_time) AS register_month, CASE WHEN o.order_seq 1 THEN 首购 ELSE 复购 END AS order_type, COUNT(DISTINCT o.user_id) AS buyer_cnt, COUNT(DISTINCT o.order_id) AS order_cnt FROM orders o JOIN users u ON o.user_id u.user_id WHERE o.pay_status success AND o.pay_time 2024-01-01 GROUP BY 1, 2 ORDER BY 1, 2;这段查询的作用是把复购率按注册月份和订单序号拆开看下降是集中在某一批新客还是全体用户。order_seq是订单序号需要在子查询里用窗口函数生成DATE_TRUNC的粒度按题目要求换成周或日。跑完先看绝对值再看环比最后看结构占比三步下来基本能定位到具体人群。3. SQL 与 Python 双线作答笔试现场怎么分配时间3.1 SQL 部分先保证口径正确再追求写法优雅笔试里的 SQL 题通常限时我的策略是先用最直白的写法把结果跑出来再考虑优化。窗口函数、CTE、子查询都能解题但现场优先选自己不容易写错的。比如算留存用自连接比用窗口函数更稳虽然代码长一点但逻辑一眼能看懂。-- 计算次日留存先取每日新增再关联次日活跃 WITH new_users AS ( SELECT user_id, MIN(DATE(login_time)) AS first_day FROM login_log GROUP BY user_id ), next_day_active AS ( SELECT DISTINCT user_id, DATE(login_time) AS active_day FROM login_log ) SELECT n.first_day, COUNT(DISTINCT n.user_id) AS new_cnt, COUNT(DISTINCT a.user_id) AS retained_cnt, ROUND(COUNT(DISTINCT a.user_id) * 1.0 / COUNT(DISTINCT n.user_id), 4) AS retention_rate FROM new_users n LEFT JOIN next_day_active a ON n.user_id a.user_id AND a.active_day DATE_ADD(n.first_day, INTERVAL 1 DAY) GROUP BY n.first_day ORDER BY n.first_day;MIN(DATE(login_time))取每个用户首次登录日LEFT JOIN保证没有次日活跃的用户也计入分母。DATE_ADD的间隔按题目要求改成 1、3、7、30。注意COUNT(DISTINCT)在关联后可能重复计数所以分子分母都要去重。3.2 Python 部分pandas 处理脏数据的三板斧笔试给的数据集往往有缺失、重复、格式混乱。pandas 里我固定用三步先看形状和类型再处理缺失和重复最后做分组聚合。不要一上来就dropna()先统计缺失比例超过 30% 的列要考虑是否保留。import pandas as pd df pd.read_csv(orders.csv) # 第一步体检 print(df.shape) print(df.dtypes) print(df.isnull().mean().sort_values(ascendingFalse).head(10)) # 第二步清洗 df df.drop_duplicates(subset[order_id]) df[pay_amount] pd.to_numeric(df[pay_amount], errorscoerce) df df[df[pay_amount].notna() (df[pay_amount] 0)] # 第三步聚合 result (df.groupby([channel, pd.Grouper(keypay_time, freqM)]) .agg(order_cnt(order_id, nunique), gmv(pay_amount, sum), buyer_cnt(user_id, nunique)) .reset_index()) result[arpu] result[gmv] / result[buyer_cnt] print(result.head())errorscoerce把无法转换的值变成 NaN避免整列报错。pd.Grouper按月聚合时要求pay_time是 datetime 类型读入后要先用pd.to_datetime转换。nunique去重计数sum求和最后算 ARPU 时注意分母是买家数不是订单数。3.3 图表只画能支撑结论的那一张笔试如果要求出图不要堆图表。诊断型题目画一张趋势图加一张结构拆解图就够了。趋势图看整体走向结构图看哪个维度贡献了变化。用 matplotlib 时把标题、轴标签、单位写全面试官不用猜。import matplotlib.pyplot as plt fig, axes plt.subplots(1, 2, figsize(12, 4)) result.groupby(pay_time)[gmv].sum().plot(axaxes[0], title月度GMV趋势) result.groupby(channel)[gmv].sum().plot(kindbar, axaxes[1], title渠道GMV分布) axes[0].set_ylabel(GMV) axes[1].set_ylabel(GMV) plt.tight_layout() plt.show()左图看趋势是否下降右图看是哪个渠道拖累。如果右图某一根柱子明显变短结论就落到那个渠道上再回去查该渠道的转化漏斗。4. 避坑数据分析笔试题里最容易翻车的五个地方现象SQL 跑出来行数对不上。原因多表关联时一对多导致膨胀或者WHERE里对右表字段过滤把LEFT JOIN变成了INNER JOIN。解决关联前先确认粒度右表过滤条件写到ON里而不是WHERE里聚合前用COUNT(DISTINCT)验证。现象留存率算出来大于 1。原因分子分母口径不一致分子用了全量活跃用户分母只用了新增用户。解决分子必须从分母同一批用户里取用LEFT JOIN或IN子查询限定用户范围。现象Python 分组结果和 SQL 对不上。原因时间字段时区不一致或者 pandas 读入时把日期解析成了字符串。解决统一用 UTC 或业务时区读入时显式指定parse_dates分组前打印dtypes确认。现象结论写了“建议加强运营”这种空话。原因没有落到具体维度和动作。解决结论必须包含“哪个群体、哪个环节、预期影响多少”比如“新客首购后 7 天内未复购占比上升 12%建议对这批人做首购后第 3 天触达”。现象时间不够最后一题没写完。原因在前面的清洗和探索上花太久。解决笔试先扫一遍所有题按分值分配时间SQL 题控制在 40% 以内Python 和结论各占 30%留 10% 检查口径。5. 从笔试到实战把一次作答变成可复用的分析模板笔试和实际工作的差别在于笔试有标准答案工作没有。但两者共用同一套底层能力定义问题、拆解假设、验证结论、给出动作。我后来把笔试里反复用到的结构固化成一个模板遇到新问题直接往里填。模板分四块第一块是问题定义写清楚现象、时间范围、涉及指标第二块是口径表把每个业务词映射到字段和过滤条件第三块是验证路径按假设树列出要跑的查询和预期结果第四块是结论与建议每条结论必须附上数据支撑和下一步动作。# 可复用的分析模板骨架 analysis { problem: 复购率下降, period: 2024-01 至 2024-03, metrics: { repurchase_rate: 近90天复购用户数 / 近90天购买用户数, new_buyer_retention: 首购后30天内再次购买用户数 / 首购用户数 }, hypotheses: [ 新客首购后触达不足, 某渠道用户质量下降, 促销活动导致一次性购买增加 ], validation: [], conclusion: [] }这个骨架的好处是每填一个字段都在逼自己把模糊想法变具体。hypotheses列表里的每一条都要能对应到一个 SQL 或 pandas 查询跑完把结果填进validation最后从验证结果里提炼conclusion。我现在的习惯是任何分析任务先花十分钟把这个字典写出来再动手写代码。写不出来说明问题还没想清楚这时候敲键盘只是浪费时间。还有一个技巧是反向验证结论出来后假设这个结论是错的去找一个能推翻它的查询。如果找不到结论才站得住。比如你说是渠道问题那就把该渠道剔除后重算整体指标看下降是否消失。这个动作在笔试里能加分在实际工作里能救命。最后说一个我自己的教训。早期做笔试题总想展示技术窗口函数、复杂 CTE、机器学习模型全往上堆结果面试官只关心“所以呢”。后来我改成先写一句结论再用数据证明反而通过率高了。分析的价值不在代码多漂亮在于能不能让看的人做决定。希望帮到你。本文还有配套的精品资源点击获取