资讯动态

Python数据分析岗位全流程解析:招聘数据清洗、技能透视与薪资建模

发布时间:2026/9/11 5:27:34 来源:尧图企业网站定制
简介《python数据分析岗位分析》是一份面向数据分析入门者与高校学生的实战项目包围绕拉勾网“数据分析师”岗位数据完整演示数据采集、数据清洗、探索性分析与可视化全流程。压缩包共包含14个文件包括5个 Jupyter Notebook 分析脚本、7个 HTML 可视化结果页、1份 CSV 岗位数据源和1份 doc 大作业任务书整体大小仅3.18MB。其中 HTML 页面覆盖技能标签词云、城市地理分布热力图、地理位置饼状图、公司行业与工作经验等分析视角Notebook 脚本则展示了利用 pandas 进行数据整理、NumPy 完成数值计算、Matplotlib 绘制图表的完整思路。整套资源已有673人学习适合课程期末大作业参考、求职作品集练习或快速掌握 Python 数据分析基础流程。配套任务书与运行脚本、可视化图表相互呼应可复现一份完整的岗位分析报告并在此基础上扩展分析维度。1. 拿到「python数据分析岗位分析.rar」之后先想清楚这三件事这类压缩包在求职季很常见里面的内容通常是招聘平台导出或二手的岗位数据职位名称、城市、薪资、经验要求、学历门槛、公司信息外加一段 JD 文本。真正的分析难点不是「用 Python 画两张图」而是三个容易忽略的问题字段口径是什么、职位名称要不要归一化、JD 里的技能词怎么提取才不被切碎。下面按一套可复现流程把这个压缩包变成岗位画像先解压校验再做职位归一化与技能词频透视薪资和城市分布输出可交互报告最后用 sklearn 验证技能权重。适合正在准备数据分析求职的人也适合需要拿招聘信息做课程设计或业务判断的工程师。2. 解压与数据探查把 .rar 里的 python 数据分析岗位数据变成 DataFrame2.1 解压 rar 的正确姿势先看清单再释放文件拿到 .rar 之后第一件事不是解压而是先看压缩包里有什么。很多导出的数据包里既有「最终版」「最终版2」又有去重前的原始表如果直接全部解压后面很可能分析错文件。rarfile 可以只读清单不落盘先确认文件名再决定释放哪些。import rarfile rar rarfile.RarFile(python数据分析岗位分析.rar) for info in rar.infolist(): print(info.filename, info.file_size, bytes) # 只释放 CSV 数据文件避免把无关脚本也解出来 for info in rar.infolist(): if info.filename.endswith(.csv): rar.extract(info.filename, pathdata/)代码逻辑infolist()返回压缩包内的文件信息列表每个元素有filename和file_size两个常用属性第二次循环用endswith(.csv)做后缀过滤只释放需要的数据文件。extract()的path参数指定解压目录缺省是当前目录。常见报错是RarCannotExec说明rarfile找不到外部的 unrar 后端。Windows 上装 WinRAR 并把安装目录加入 PATHLinux 上装unrarmacOS 上用 Homebrew 装unrar装完重开终端即可。如果压缩包带密码RarFile支持password参数但不建议把密码硬编码进脚本可以改用getpass交互输入安全性更好。提示在共享服务器上解压未知来源的 rar 之前先执行病毒扫描解压后优先看 README 或字段字典多数岗位包的字段名和真实含义并不完全一致。2.2 读入 CSV 与字段体检前三步是 shape、dtypes、head数据量不大时read_csv后依次做三个动作看行数列数、看字段类型、打印前 10 行人工过目。这里的顺序不是走形式而是判断「字段口径到底是什么」。比如experience字段写的是「3-5年」「3~5年」还是「3-5年经验」只有看过取值集合之后才能决定用什么正则去解析。import pandas as pd df pd.read_csv(data/jobs.csv, encodingutf-8-sig) print(数据集形状:, df.shape) print(df.dtypes) print(df.head(10).to_string()) # 经验、学历这类文本字段看分布比看样例更可靠 print(df[experience].value_counts().head(10)) print(df[education].value_counts().head(10))utf-8-sig是带 BOM 的 UTF-8 编码导出的 CSV 经常带 BOM用普通utf-8读取时第一列列名会变成\ufeffjob_title排查起来费时utf-8-sig可以一次性规避。dtypes能快速暴露「城市字段意外读成了数字」「薪资字段变成 object」这类问题。head后肉眼看一遍主要检查列名是否对得上、有没有明显错位行。如果拿到的是.xlsx可以换成pd.read_excel(data/jobs.xlsx, engineopenpyxl)。但导出的岗位表多数是 CSV且 xlsx 在行数过万时读取速度明显更慢优先 CSV 没有实际问题。2.3 清洗薪资、经验、城市三个脏字段并保持口径可追溯字段清洗中最容易出错的是单位。薪资常见写法有「15-25K」「8千-1.2万」「薪资面议」如果只提取数字不区分单位「8千-1.2万」会被算成 4.6K整个城市薪资中位数都会偏离。这里用一段「数字单位」成对解析的代码import re def parse_salary(s): if not isinstance(s, str): return None s s.strip().lower() if 面议 in s or not s: return None # 匹配 “数字单位”单位可能为空也可能带 千/万/k parts re.findall(r(\d(?:\.\d)?)\s*([千万元k万]*), s) nums [] for num, unit in parts: v float(num) if unit.startswith(万): v * 10 elif unit.startswith(亿): v * 100000 nums.append(v) if not nums: return None if len(nums) 1: return nums[0] return sum(nums[:2]) / len(nums[:2]) df[salary_k] df[salary].apply(parse_salary)逻辑拆解正则\d(?:\.\d)?负责数字部分[千万元k万]*负责单位「万」前面的数字要乘以 10 换算成 K「亿」则乘 100000没有单位时默认就是 K。区间有两个端点时取均值写死nums[:2]是为了防止「15-25K·14薪」这类字符串被第三个数字污染。这个函数处理的是月薪口径「200-300/天」这类非月薪样本会被当成 250K 误入分析清洗后应该把明显超出常识的薪酬值筛掉比如salary_k 5或大于200的结合业务范围另行处理。经验字段用同样的数字提取思路但映射的是下限值。把「3-5年」解析为 3 年「5-10年」解析为 5 年方便后续分组和建模def parse_exp(e): if not isinstance(e, str): return 0 nums re.findall(r\d, e) if not nums: return 0 return int(nums[0]) df[exp_year_floor] df[experience].apply(parse_exp)城市字段的坑主要在「北京·朝阳区」「北京望京」这类写法。统一按市级粒度清洗时用分隔符取第一段即可但要注意「全国」「异地招聘」这类特殊值先看value_counts再决定归到「不限」还是剔除。清洗完成的标志是一张字段字典原始字段、清洗后字段、单位、缺失策略写进报告里可追溯。原始薪资正则切出的部分输出(K)15-25K[(15,), (25,K)]208千-1.2万[(8,千), (1.2,万)]10薪资面议[]None3. 岗位画像与技能图谱用 jieba 给 python 数据分析岗位的 JD 提取关键词3.1 职位名称归一化先分清楚「数据分析」里藏着多少种岗位「数据分析师」「数据分析运营」「商业分析」「BI 报表工程师」在招聘平台上经常被归进同一个「数据分析」类目但技能要求、薪资区间差异很大。分析的第一步是把职位名称做归一化否则后面所有统计都会被混着算。归一化的原则是去掉括号里的地点或团队信息去掉资深/初级这类职级前缀再按关键词映射到岗位类型。def normalize_title(t): if not isinstance(t, str): return 未知 t re.sub(r[(].*?[)], , t) t re.sub(r高级|资深|初级|中级|助理|实习, , t) return t.strip() df[title_clean] df[job_title].apply(normalize_title) def map_role(title): if 运营 in title: return 运营型数据岗位 if 商业 in title or 经营 in title: return 商业分析 if 挖掘 in title or 算法 in title or 科学家 in title: return 算法/挖掘 if 分析 in title or 报表 in title or title.lower().startswith(bi): return 数据分析 if 开发 in title or 工程 in title: return 数据开发 return 其他 df[role] df[title_clean].apply(map_role)把括号内容先删掉的目的是避免「数据分析北京」这类噪音影响运营 in title的判断。职级词删除后资深/高级这类信息不会参与岗位归类如果后续想分析「资深数据分析师」的薪资溢价不要删职级另建一列单独提取。映射顺序也值得说明运营判断必须放在分析前面因为「数据运营分析」这类标题二者都含先命中运营更符合岗位的实际工作内容。之后的统计按df[df[role] 数据分析]取子集也可以保留全量做各岗位薪资对比。「python数据分析岗位」在招聘标题里多数不会写全称JD 里的技术栈才是真正的筛选条件。3.2 jieba 自定义词典不让「数据挖掘」被切错是词频统计的前提直接用 jieba 对 JD 分词结果里会出现「数据」「分析」「挖掘」被拆开、「Tableau」被切成单个字母的问题。解决办法是准备一份技能自定义词典让 jieba 优先按行业词切分。自定义词典的格式是三列词、词频、词性词频建议写 10 以上词性可以写 n 或 nz。import jieba from collections import Counter jieba.load_userdict(skill_words.txt) stopwords set([以及, 负责, 相关, 工作, 公司, 我们, 要求, 能力, 职责, 经验, 招聘, 职位, 岗位]) words [] for jd in df[df[role] 数据分析][jd].dropna(): tokens jieba.lcut(jd.lower()) words [w.strip() for w in tokens if w.strip() and w not in stopwords and len(w) 1] counter Counter(words) print(counter.most_common(30))skill_words.txt中至少要有这些条目数据分析、数据挖掘、机器学习、深度学习、可视化、数据仓库、Tableau、Power BI、Excel 函数。load_userdict在lcut前调用一次即可多行 JD 重复调用没有意义。过滤条件里的len(w) 1会丢掉「SQL」这类全大写缩写所以统计前统一lower()再把 SQL、BI、ETL 等缩写通过自定义词典加固。词频能反映 JD 文本层面的热度但它有一个容易忽略的偏差同一份 JD 里反复出现「Python」三遍只说明这一条岗位对 Python 要求高并不说明市场整体热度高。统计「出现该技能的职位数占分析岗位总数的比例」比统计词频更有业务意义skills [python, sql, excel, tableau, powerbi, spss, 机器学习, 深度学习, hive, spark, flink] jd_sub df[df[role] 数据分析][jd].fillna().str.lower() ratio {} for skill in skills: ratio[skill] jd_sub.str.contains(skill, regexFalse).mean() skill_df pd.Series(ratio).sort_values(ascendingFalse).round(3) print(skill_df)str.contains(skill, regexFalse)是精确子串匹配regexFalse避免技能词里出现正则元字符时的意外行为。mean()在布尔序列上等价于「包含该技能的职位占比」输出如python 0.72表示 72% 的数据分析岗位在 JD 里提到了 python。词云在这种场景下只适合做汇报装饰判断技能热度时不如这张比例表可靠。3.3 技能与薪资交叉验证哪些技能写进 JD 但没被市场买账技能词频只能回答「岗位要什么」回答不了「值不值」。把每个技能转成布尔特征再按技能有无分别算薪资中位数就能看出哪些技能是基础门槛、哪些是真正的加分项skills [python, sql, excel, tableau, spss, 机器学习, hive] for skill in skills: df[fhas_{skill}] df[jd].fillna().str.lower().str.contains(skill, regexFalse) rows {} for skill in skills: has df.loc[df[fhas_{skill}], salary_k] not_has df.loc[~df[fhas_{skill}], salary_k] rows[skill] { 含技能薪资中位数: has.median(), 不含技能薪资中位数: not_has.median(), 含技能样本数: has.notna().sum(), } cross pd.DataFrame(rows).T.round(1) print(cross)表格输出结构大致如下具体数值以实际数据为准技能含技能薪资中位数不含技能薪资中位数含技能样本数python18.516.2860sql17.817.1920tableau20.217.4260机器学习22.617.0180解读时注意三点。第一样本数过小的技能不下结论比如只出现几十条的 Flink中位数会被少数大厂样本干扰。第二Python 这类 JD 覆盖率接近七成的技能两组薪资差异不大说明它已经是行业默认要求不是加分项。第三Tableau、机器学习这类细分技能如果切分后薪资差异明显才适合写进「技能决定薪资」的结论。判断时不要引用均值招聘薪资长尾分布明显均值容易被大厂样例带偏。4. 城市、经验与学历的 python 数据分析透视从 pandas 到可视化的参数取舍4.1 用 pivot_table 做「城市 x 经验」的薪资中位数透视单维度groupby只能回答「哪个城市贵」或「几年经验贵」但招聘市场真正的问题是「在特定城市、特定经验段薪资大概在什么水位」。「3-5年经验在北京能开到多少」这类查询对应的是一个二维透视表pivot_table是标准解法。df[exp_label] pd.cut(df[exp_year_floor], bins[-1, 1, 3, 5, 10, 100], labels[0-1年, 1-3年, 3-5年, 5-10年, 10年以上]) pivot df.pivot_table(indexcity, columnsexp_label, valuessalary_k, aggfuncmedian) print(pivot.head(10).round(1))indexcity决定行是城市columnsexp_label决定列是经验档aggfuncmedian填入的是该格子内的薪资中位数。选median的核心原因是薪资数据右偏明显少数高薪样本会把mean拉高 20% 以上。格子中出现 NaN 表示该组合没有足够样本不要填充成 0分析时直接忽略。只看全部城市的透视表很难读出重点先按城市薪资中位数选出 Top 10 再透视输出更接近业务层直接会看的那张表city_order (df.groupby(city)[salary_k] .median().sort_values(ascendingFalse) .head(10).index) pivot_top df[df[city].isin(city_order)].pivot_table( indexcity, columnsexp_label, valuessalary_k, aggfuncmedian) print(pivot_top.round(1))sort_values(ascendingFalse).head(10).index的顺序在后面绘图时同样有用。把它传给条形图的order参数可以保证图表的排序和透视表完全一致避免每次手动维护坐标轴顺序。4.2 学历与公司规模要求「本科及以上」的岗位薪资为什么不一定高于「大专」学历字段常见写法有「本科」「本科及以上」「硕士」「大专」「学历不限」。「及以上」这类后缀如果不处理「本科」和「本科及以上」会被分成两个类别透视表变得破碎。按关键词映射是更稳的做法def parse_edu(e): if not isinstance(e, str): return 不限 if 硕士 in e or 研究生 in e: return 硕士及以上 if 博士 in e: return 博士 if 本科 in e: return 本科 if 大专 in e or 专科 in e: return 大专 return 不限 df[edu_label] df[education].apply(parse_edu) edu_salary (df.groupby(edu_label)[salary_k] .agg([median, count]) .sort_values(median, ascendingFalse)) print(edu_salary.round(1))agg([median, count])同时拿到薪资中位数和样本量两个指标要配合看。经常出现的现象是「硕士及以上」的薪资中位数比「本科」高不少但样本量只有后者的五分之一这并不能说明「读硕士一定涨薪」只能说「要求硕士的岗位整体薪资水位更高」因为这类岗位往往集中在一线城市的大厂。报告里这两句结论的口径不能混。公司规模字段如果有人数区间如「1000-9999人」清洗时与经验字段同样用数字提取。如果压缩包里没有公司规模字段可以直接跳过这一维不要因为缺失而强行造列。4.3 中文可视化避坑字体、坐标轴顺序和 dpi 三个参数matplotlib 在中文场景下的第一个坑是汉字变方框。解决方案是显式指定中文字体同时关闭负号显示问题import matplotlib.pyplot as plt import seaborn as sns plt.rcParams[font.sans-serif] [Microsoft YaHei, SimHei] plt.rcParams[axes.unicode_minus] False sns.set_theme(stylewhitegrid) top df.groupby(city)[salary_k].median().sort_values(ascendingFalse).head(10) sns.barplot(xtop.values, ytop.index, estimatormedian, errorbarNone) plt.xlabel(月薪中位数(K)) plt.title(python数据分析岗位薪资 Top10 城市) plt.savefig(output/city_salary.png, dpi200, bbox_inchestight)plt.rcParams[font.sans-serif]接受一个字体列表Windows 上非必须但 Linux 服务器没有微软雅黑时必须换用SimHei或系统已安装的其他中文字体axes.unicode_minus设为False是为了让坐标轴的负号在部分字体下正常显示。sns.barplot的ytop.index直接传索引顺序errorbarNone表示不显示置信区间因为这里展示的是中位数显示置信区间在统计口径上反而奇怪。dpi200是为 PPT 或报告印刷准备的bbox_inchestight保证保存出来的图片四周不被裁掉。图表的结论要落在数据上如果 Top10 城市里一线城市包揽前五其余是杭州、成都、武汉这类强二线报告里就写「python数据分析岗位的薪资城市梯度明显」不要写「这几个城市值得去」那是过度外推。可视化只是把透视表的结果换了一种表达结论不能超过数据的范围。5. 把 python 数据分析岗位结论交付成可交互 HTML 与 PPT 报告5.1 用 pyecharts 出一份可交互 HTML业务方可以自己点选维度静态 matplotlib 图适合论文和 PPT但业务方在开会时往往想自己悬停看数值、点击图例看某个城市。这时 pyecharts 的交互能力更合适。它的接口和 pandas 的结构天然对齐图表配置保存在同一个 Python 脚本里改数据后重新运行即可from pyecharts.charts import Bar from pyecharts import options as opts top df.groupby(city)[salary_k].median().round(1).sort_values(ascendingFalse).head(10) bar Bar() bar.add_xaxis(top.index.tolist()) bar.add_yaxis(月薪中位数(K), top.values.tolist()) bar.set_global_opts( title_optsopts.TitleOpts(titlepython数据分析岗位薪资 Top10 城市), ) bar.render(output/salary_top10.html)render(xxx.html)生成一个自带 JS 依赖的独立 HTML 文件双击即可在浏览器打开不需要启动任何服务。add_yaxis的系列名「月薪中位数(K)」会出现在图表右上角的图例中也是导出图片时的系列名。pyecharts目前常见的是 1.x 系列options模块被拆到了独立的位置pip install pyecharts后会自动带上不需要额外安装。HTML 交付的一个问题是不能直接嵌进 PPT。常见做法是 HTML 文件随报告一起发PPT 里放同数据的 matplotlib 静态备份图两端数据口径保持一致即可。5.2 用 python-pptx 批量生成 PPT把图表固定进版式从分析结果到 PPT最容易被手工排版消耗时间的不是配图而是图表标题、数据口径、结论三点对齐。python-pptx 可以自动化这个流程把上一节生成的图片按固定版式放入from pptx import Presentation from pptx.util import Inches prs Presentation() slide prs.slides.add_slide(prs.slide_layouts[5]) # 5 是空白版式 slide.shapes.add_picture(output/city_salary.png, Inches(0.6), Inches(1.0), widthInches(8)) prs.save(output/python数据分析岗位报告.pptx)add_picture的三个位置参数分别是左边距、上边距和宽度单位用Inches转换。layout 5在默认模板里是空白版式如果想用标题版式可以改用prs.slide_layouts[0]再通过slide.shapes.title.text ...写入标题。PPT 文件命名和标题保持一致后续用搜索找文件时不会被系统忽略。代码只是骨架更关键的是每张 PPT 必须统一携带三行信息样本量、薪资口径、时间范围。比如「基于 1280 条数据分析岗位样本薪资为月薪中位数数据采集时间为 2024 年」缺了任何一行这份报告在半个月后回看时就无法确认数字含义。5.3 交付物清单脚本、字段字典和结论摘要缺一不可岗位分析这种偏交付型的项目只给一个 HTML 文件远远不够。完整交付物一般包含四部分清洗与分析脚本、字段字典、图表文件、结论摘要。脚本可以用ydata-profiling生成一份 EDA 报告作为附件一行profile.to_file(report.html)就能把缺失率、分布、相关性全部导出省去重复写探查代码。注意ydata-profiling在数据量超过十万行时会明显变慢岗位数据通常只有几千行可以放心用。如果数据量大先抽样或只对关键列做探查。结论摘要用纯文本 Markdown 写包含三条左右可验证的结论每条后面附对应图表文件名。这样后续复现时对着脚本能跑出同一组数字对着字段字典能看懂每个 column 的来源对着结论摘要能直接汇报整个流程不再依赖某一个人脑中的上下文。6. 进阶用同一份 rar 数据训练岗位薪资预测模型反向验证技能权重岗位分析做到透视表和词频这步基本回答完了「what」如果要再跨一步回答「哪些因素真正决定薪资」可以把这个任务转成回归问题。特征用前几章清洗好的字段经验下限、学历标签、城市薪资基准、技能布尔列。城市不要直接用LabelEncoder那会强加一个不存在的顺序关系这里用城市薪资中位数做替换与前面的透视口径保持一致from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error city_salary_map df.groupby(city)[salary_k].median() df[city_salary_base] df[city].map(city_salary_map) edu_map {大专: 1, 本科: 2, 硕士及以上: 3, 博士: 4, 不限: 2} df[edu_code] df[edu_label].map(edu_map) features [exp_year_floor, city_salary_base, edu_code, has_sql, has_python, has_excel, has_spark] X df[features].fillna(-1) y df[salary_k].dropna() X X.loc[y.index] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42) model RandomForestRegressor(n_estimators300, max_depth6, random_state42, n_jobs-1) model.fit(X_train, y_train) pred model.predict(X_test) print(MAE:, round(mean_absolute_error(y_test, pred), 2)) print(pd.Series(model.feature_importances_, indexfeatures) .sort_values(ascendingFalse).round(4))city_salary_map用城市全量薪资中位数做替换逻辑和上一章的透视口径一致但注意这样会把城市维度的大部分信息提前用掉后续看特征重要度时会略微低估城市本身的作用。fillna(-1)处理技能列里的空 JD-1 对树模型的分裂逻辑不会产生顺序干扰。MAE落在 4~6 意味着中位数薪资 18K 的情况下平均误差在 4K 左右说明薪资里还有大量公司因素和行业因素不在当前特征里不要试图把模型精度当成主要结论。特征重要度的用法是把数值和上一章的技能占比对齐看技能占比高但重要度低说明它是入行门槛而不是溢价因素技能占比一般但重要度高才是值得写进简历和报告的关键词。把model.feature_importances_拉回上一章的skill_df做一次内连接就能得到一份「热度与薪资权重」对比表这也是整个岗位分析流程里最后一步真正有用的产出。本文还有配套的精品资源点击获取

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

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

免费获取报价