资讯动态

数据分析笔试源码包全拆解:从SQL取数到AB检验与建模避坑指南

发布时间:2026/10/20 1:19:18 来源:尧图企业网站定制
简介面向数据分析求职者与机器学习初学者的笔试源码包内容源自睿智合创北京科技有限公司真实笔试题目覆盖数据处理、缺失值填充、分组统计、数值分箱与统计建模等核心考点。资源共3个文件包含 inscode 源码、HTML 版答案解析页和 gitignore 配置文件压缩包仅7KB轻量便于按需查阅。源码中示范了 pandas 的 drop_duplicates() 去重、dropna() 与 fillna() 缺失值处理、groupby() 分组聚合以及 cut() 数值区间划分等操作同时用对比方式梳理了线性回归与逻辑回归的适用场景、有监督与无监督学习的区别与联系以及分类和回归模型的评估指标与建模流程。作者还整理了 HR 面试与技术面试的经历和答题思路帮助读者了解企业考察侧重点找准复习方向。目前已有263人学习适合笔试冲刺、专项强化和面试前系统回顾。1. 一份数据分析笔试源码包先拆清楚再动手手里拿到一份数据分析岗位的笔试源码包最忌讳的就是直接双击脚本、看完报错就扔一边。这份资料来自某公司为方便叙述下文统一用“某公司”代称数据岗笔试的完整复盘里面既有原始数据集也有按题型拆好的参考解答覆盖 SQL 取数、pandas 清洗、AB 实验显著性和分类建模四个环节。适合正在准备数据岗笔试的求职者也适合想用一份完整样例练手的数据分析师。它的价值不在“答案对不对”而在把笔试里最容易卡住的数据格式、统计口径和建模细节全部拉通。接下来我会从题面结构、代码实现、参数设定到踩坑点一层层拆开说。2. 看懂笔试结构四类高频考题与源码目录的对应关系2.1 题目文档先行SQL、清洗、AB 测试、建模各占多少拿到源码包后第一件事不是看代码而是先把题目说明通读一遍。这份资料里的题目文档把笔试拆成了四个独立部分每部分都对应一份独立的输入数据和输出约定。我在本地按题型统计了一下考察点分布如下题型考察点输入文件输出约定SQL 取数窗口函数、多表关联、去重逻辑orders表、users表输出结果表数据清洗缺失值、异常值、时间字段标准化raw_order.csv清洗后的clean_order.csvAB 实验分析显著性检验、置信区间、效应量ab_test.csv统计结论与业务建议分类建模特征工程、模型训练、指标评估clean_order.csv与label.csv模型 AUC 与特征重要度不要小看这个结构划分它在提示你一个关键信息笔试主观上想筛的不是“谁代码写得花”而是“谁能在有限时间给出可验证的结果”。所以源码包里的参考解答全部输出到output/目录每份输出都有明确的列名和行数约定。我在复现时习惯先建立一张“输出清单”把每一题预期生成的文件名、列名、行数都记下来再逐步验证。这一步能帮你省下大量排查时间尤其是在最后核对“我的结果和参考答案差在哪”的时候。2.2 目录组织把“参考解答”和“可复用地基”分开这份源码包把目录组织得很清晰整体结构大致是. ├── data │ ├── raw_order.csv │ ├── user_profile.csv │ ├── ab_test.csv │ └── label.csv ├── sql │ ├── question_1.sql │ └── question_2.sql ├── python │ ├── 01_data_cleaning.py │ ├── 02_ab_test.py │ └── 03_model.py ├── output │ └── README.md └── 笔试说明.md我一般会先看output/README.md它会写明每个输出文件的表头和数据量相当于一份“验收标准”。这些文件的组织有个很实用的点data/里是原始数据sql/和python/是独立可跑的脚本彼此之间不互相依赖这样你可以单独跑某一部分不用每次从清洗开始。有一点值得注意01_data_cleaning.py的输出会被03_model.py复用所以在跑建模前必须先把清洗脚本跑通。源码包里没有把中间结果缓存到data/而是直接写到output/这个习惯我后来也沿用了好处是原始数据永远不被污染重跑任何一步都很干净。3. 数据清洗与特征构建pandas 基线的完整拆解3.1 先跑通基线脚本读表、看类型、统计缺失数据清洗部分的参考脚本基本沿用了 pandas 的标准流程但有两个细节值得单独拎出来看。第一是读表时显式指定编码和 dtype第二是缺失值统计做了自定义封装。我把基线脚本的核心段摘出来import pandas as pd from pathlib import Path DATA_DIR Path(../data) OUT_DIR Path(../output) OUT_DIR.mkdir(exist_okTrue) dtype_dict { user_id: str, order_id: str, amount: float32, } orders pd.read_csv( DATA_DIR / raw_order.csv, encodingutf-8-sig, dtypedtype_dict, ) print(forders shape: {orders.shape}) print(orders.dtypes) missing_stats orders.isnull().sum() print(missing_stats[missing_stats 0])这段代码的逻辑并不复杂但每个参数都有讲究。encodingutf-8-sig是为了兼容带着 BOM 头的 CSV 文件避免列名出现莫名其妙的\ufeff前缀。dtype显式把user_id和order_id声明为字符串是为了防止长数字 ID 被 pandas 识别成 int64 后丢失精度——这个问题在笔试里特别容易翻车因为肉眼看不出来等 join 的时候才发现主键对不上。amount用float32而不是默认的float64在这里影响不大但对百万行以上数据有明显的内存收益。随后用isnull().sum()按列统计缺失值这一步的目的是快速确认缺失集中在哪些字段再决定下一步用什么策略填充。常见做法是先按缺失率排序缺失率低于 5% 的字段可以直接删除对应行缺失率高于 30% 的字段则需要考虑是否保留。拿到缺失分布后再结合业务含义去填充不能盲目用fillna(0)。3.2 时间字段、金额异常与特征构造清洗的下半场是时间字段标准化和异常值过滤。这套源码里给了一个很实用的模式先解析时间再过滤异常最后构造特征。核心代码大概是import datetime as dt orders[order_time] pd.to_datetime( orders[order_time], errorscoerce ) valid_mask ( orders[order_time].notna() orders[amount].between(0, 10000) orders[status].eq(paid) ) clean_orders orders.loc[valid_mask].copy() obs_date pd.Timestamp(2024-12-31) rfm clean_orders.groupby(user_id).agg( last_order(order_time, max), frequency(order_id, nunique), monetary(amount, sum), ).reset_index() rfm[recency_days] (obs_date - rfm[last_order]).dt.days rfm rfm.drop(columns[last_order])pd.to_datetime的errorscoerce会把无法解析的时间字符串转成NaT这一点很关键因为源数据里总混着几种时间格式比如2024/1/5和2024-01-05。解析失败的行会进入后续的notna()过滤保证进入建模的数据时间字段一定是合法的。amount.between(0, 10000)是一个业务经验值金额小于等于 0 的订单一般是退款或测试单大于 10000 的则可能是异常录入这个阈值可以根据实际业务调整但笔试场景下用分位数确认边界更稳妥我一般会先看amount.describe()再改阈值。RFM 特征构造部分groupby(user_id)后分别取最近一次下单时间、去重订单数、总消费金额。recency_days用观察日减去最近订单时间得到的是整数天数。这样构造出来的特征框才是后续建模真正喂给算法的东西。很多人在这一步容易忽略源数据里的order_time可能带时区偏移两个不同时区的订单在相减时会得到无法按天取整的结果所以逐行清洗时要先把时区统一成 UTC 或东八区。4. AB 测试显著性从 t 检验到可信区间4.1 先做描述统计与正态性校验AB 实验部分的源码把重点放在了“结论是否可靠”上而不是只输出一个 p 值。第一步是先看两组的样本量和均值再判断是否满足正态性假设。import pandas as pd from scipy import stats ab pd.read_csv(DATA_DIR / ab_test.csv) group_stats ab.groupby(group)[metric].agg( countcount, meanmean, stdstd ) print(group_stats) for g in ab[group].unique(): sample ab.loc[ab[group] g, metric] stat, p_value stats.shapiro(sample) print(fgroup{g}, shapiro_p{p_value:.4f})描述统计这一步没什么玄学groupby()后分别计算样本量、均值和标准差目的是快速判断两组样本量是否悬殊。如果一组只有几十个样本另一组有几万个t 检验仍然可以跑但结论的说服力会大打折扣笔试答到这一步往往会扣分。shapiro正态性检验在样本量小于 5000 时相对可靠如果 p 值小于 0.05说明该组数据不满足正态分布后续要考虑非参数检验。不过实际业务数据很少严格正态t 检验在大样本下对正态性偏离并不敏感所以这一步更多是“记录依据”而不是“非得通过不可”。4.2 主检验、效应量与结论表述正态性校验之后主检验部分参考解答用了独立样本 t 检验同时补了一个效应量指标。代码段如下control ab.loc[ab[group] control, metric] treatment ab.loc[ab[group] treatment, metric] t_stat, p_value stats.ttest_ind(treatment, control, equal_varFalse) print(ft_stat{t_stat:.4f}, p_value{p_value:.6f}) def cohen_d(a, b): na, nb len(a), len(b) var_a, var_b a.var(ddof1), b.var(ddof1) s_pooled (((na - 1) * var_a (nb - 1) * var_b) / (na nb - 2)) ** 0.5 return (a.mean() - b.mean()) / s_pooled d cohen_d(treatment, control) print(fcohen_d{d:.4f}) ci stats.t.interval( 0.95, dflen(treatment) len(control) - 2, loc(treatment.mean() - control.mean()), scale(treatment.std() / (len(treatment) ** 0.5)) ) print(f95% CI: {ci})equal_varFalse对应 Welch’s t-test不假设两组方差齐性这是比较稳健的默认选择。p 值小于 0.05 只说明“差异不太可能是随机波动”不能说明“这个差异有多大”所以补了 Cohen’s d。d 大于 0.8 通常视为大效应在业务解读里能直接回答“提升几个百分点”的问题。置信区间部分用stats.t.interval算的是均值差的可信范围这一步容易被忽略但它恰恰是面试官追问的细节p 值显著而置信区间包含 0 的情况虽然少见一旦出现就说明你的检验假设有问题。输出结论时参考解答一般不会只写“显著”而是把 p 值、效应量、样本量全部列出来再结合业务口径给一句话建议。这也是我在实际工作中复用度最高的输出模板。5. 避坑清单复现这套源码最容易翻车的五个点5.1 环境与路径类编码、相对路径、随机种子先看环境类问题。这类报错最常见也最让人头大因为它们跟代码逻辑没关系纯粹是环境差异引起的。第一read_csv报UnicodeDecodeError。现象是脚本在别人机器上跑得好好的到你这边一读 CSV 就报错。原因多半是文件编码不是 UTF-8而是 GBK 或带 BOM 的 UTF-8。解决办法是用encodingutf-8-sig它能同时兼容带 BOM 和不带 BOM 的 UTF-8。如果还报错就改成encodinggbk试一次但这种情况一般意味着文件本身是 GBK 编码后续写文件时也要保持一致。第二相对路径失效。现象是脚本单独运行时能跑但通过 IDE 的“运行当前文件”按钮跑就报FileNotFoundError。原因是 IDE 的工作目录不一定在python/目录下导致Path(../data)解析到错误位置。解决办法是在脚本顶部固定工作目录用BASE_DIR Path(__file__).resolve().parent.parent来定位项目根目录而不是用相对当前进程的路径。这个坑在多人协作时特别隐蔽因为每个人打开 IDE 的方式不一样。第三随机种子未固定。现象是同一份数据、同一个模型每次跑出来的 AUC 都不一样。原因是建模脚本里没有设置random_state数据划分和模型初始化时的随机过程不受控。解决办法是在train_test_split、RandomForestClassifier、XGBClassifier等所有涉及随机的地方都显式传random_state42。这一点在笔试场景里几乎是必查项因为面试官会反复跑你的脚本核对结果。5.2 数据与建模类时间字段、样本不均衡、特征泄漏第四时间字段类型不对。现象是 RFM 特征算出来的recency_days全是负数或者直接报TypeError: unsupported operand type(s)。原因是order_time在 CSV 里是字符串如果没做pd.to_datetime就直接相减pandas 会尝试把两个字符串做减法。解决办法是清洗阶段统一用errorscoerce解析并把解析后的列重新赋值而不是在原列上原地改。还有一个细节如果源数据里的时间是时间戳数字比如1735689600需要先判断单位是秒还是毫秒再决定用units还是unitms。第五样本不均衡导致 AUC 虚高或模型失效。现象是建模时直接训练分类报告显示准确率 95%但实际业务里几乎没有预测价值。原因是正负样本比例悬殊模型把所有样本都预测成多数类。解决办法是建模脚本里加class_weightbalanced或者用train_test_split(..., stratifyy)保住训练集和测试集的类别比例。源码里的参考解答用了class_weight方案因为它在不改变数据分布的前提下给少数类更高权重复现成本最低。我实际跑的时候还发现如果只调class_weight而不管特征模型对少数类的召回率依然有限所以更完整的做法是同时做下采样或合成样本但笔试场景下先把class_weight用对就足够拿分。最后提一个容易忽略的特征泄漏问题。现象是模型 AUC 到了 0.99明显好得不真实。原因是在特征构造时用了未来信息比如用整张表的金额均值去填充某一行缺失值或者把label本身参与聚合。解决的办法是特征构造的每一步都只依赖当前行之前的数据像groupby聚合要确认窗口不是全量。这一点在笔试里属于加分项主动写出来会显得你真正理解建模流程。6. 收尾一键复现与交付验证源码包的价值在于能复现而复现的底线是“换一台机器、换一个人也能跑出同样结果”。我习惯把三个脚本串成一条命令输出一个校验文件方便最终检查和交付。cd python python 01_data_cleaning.py \ python 02_ab_test.py \ python 03_model.py跑完之后不要急着看日志直接检查output/目录下的文件是否齐全再用一个小脚本核对关键结果。以下是常用验证点python -c import pandas as pd from pathlib import Path out Path(output) files sorted(p.name for p in out.glob(*.csv)) print(files) assert clean_order.csv in files assert model_metrics.csv in files 校验的核心是清洗文件存在、AB 检验输出存在、模型指标文件存在且行数符合题目约定。建议把每个输出文件的列名也核对一遍因为字段名拼写差异是笔试里最常见的扣分点比如recency_days写成recency_day。我一般会在跑完基线后把output目录里的文件做一个清单对照题目说明逐项打勾全部通过才算真正复现完成。现在说回我自己的习惯。以前我拿到任何一个源码包第一反应是跑起来看报错结果经常被环境问题耗掉大量时间反而忽略了代码本身。从那以后我每次都强制先花十分钟看目录结构、读输出说明、建一张验收清单再开始动手。这样即使脚本中途翻车我也能快速定位是哪一步出了问题而不是像无头苍蝇一样在报错信息里打转。这套方法帮我处理了不少数据相关的资料包也让我在笔试节奏里保持了稳定发挥希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑