Data-Science-For-Beginners 表单数据评估实战:用 Pandas 诊断与清洗客户表单采集数据
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
本篇文章围绕微软开源课程 Data-Science-For-Beginners 第 8 课配套练习「Evaluating Data from a Form(评估表单数据)」展开:一位客户用一个小型 HTML 表单采集了客户基础信息,交付你验证数据质量、定位可视化结果异常的原因,并给出表单改进建议。读完本文,你将掌握用 pandas 检查缺失值、统一格式、去重等清洗手段,并能够把「数据入口设计」与「数据清洗」结合起来,从源头提升采集数据的准确性与一致性。
任务背景:客户表单与待验证的数据
表单结构
客户部署的表单是一个极简静态页面 index.html,浏览器直接打开即可查看。从源码看,它只包含三个采集字段:
<h1>Please Fill out the Form (* required)</h1> *<label>Birth Month</label> <input type="text"> <br> <label>State</label> <input type="text"> <br> <label for="pets">Dogs or Cats?</label> <select name="pets" id="pets"> <option value="dogs">Dog</option> <option value="cats">Cats</option> </select> <br> <button>Submit</button>值得注意的三个细节:
- **Birth Month(出生月份)**用自由文本框输入,虽然标了
*(必填),但没有任何格式约束; - **State(州)**同样是自由文本,且未标记必填;
- Dogs or Cats?是下拉框,选项
value为小写复数"dogs"/"cats",而页面显示文本为"Dog"/"Cats",值与标签的约定并不一致。
这些「看起来无关紧要」的设计差异,正是后续数据问题的源头,也是本次评估任务的核心线索。
待验证的数据集
客户提供了表单记录的 form.csv 数据集(10 行 3 列):
birth_month,state,pet January,,Cats JAN,CA,Cats Sept,Hawaii,Dog january,AK,Dog July,RI,Cats September,California,Cats April,CA,Dog January,California,Cats November,FL,Dog December,Florida,Cats肉眼扫一遍即可发现大量「脏数据」特征:月份大小写混用(January/JAN/january)、缩写与全称混用(SeptvsSeptember)、州名缩写与全称混用(CAvsCalifornia、FLvsFlorida)、空值(第一条记录的 state 为空)等。
练习要求与评估标准
原练习文档要求如下(原文来自 translations/fr/2-Working-With-Data/08-data-preparation/assignment.md,英文原版见 assignment.md):
Instructions(任务要求):运用本课(Data Preparation)讲授的技术,就如何改进表单、使其能够采集到准确且一致的信息,提出你的建议。
练习配套的评估表格(Rubric)给出了三个维度:
| Exemplary(优秀) | Adequate(合格) | Needs Improvement(需改进) |
|---|---|---|
| 完整识别出数据质量问题并逐一给出代码级修复方案,同时能指出表单设计层面的根源并提出可落地的改进建议 | 识别出大部分质量问题并给出部分处理建议 | 仅指出少量问题,或缺乏可操作的修复建议 |
结合练习说明,交付物应包含:① 对数据集质量问题的诊断;② 解释可视化为何「看起来不正确」;③ 基于 pandas 的清洗/标准化方案;④ 对表单本身的改进建议。
第一步:加载数据并探查
练习提供的 assignment.ipynb 已经给出了加载数据与绘制可视化的骨架。核心代码为:
import pandas as pd import matplotlib.pyplot as plt # 加载数据集(相对于仓库根目录的路径) path = 'data/form.csv' form_df = pd.read_csv(path) print(form_df)运行后输出与 notebook 内记录的结果一致:
birth_month state pet 0 January NaN Cats 1 JAN CA Cats 2 Sept Hawaii Dog 3 january AK Dog 4 July RI Cats 5 September California Cats 6 April CA Dog 7 January California Cats 8 November FL Dog 9 December Florida Cats课程 README(2-Working-With-Data/08-data-preparation/README.md)中强调,面对任何数据集,第一步都是探查:通过元信息快速建立对数据规模、结构与内容的整体认知,再做进一步判断。常用手段包括:
form_df.shape # (10, 3),10 行 3 列 form_df.info() # 各列类型、非空计数与内存占用 form_df.head() # 前 5 行 form_df.tail() # 后 5 行 form_df.describe() # 数值列的统计摘要(本例全为文本列,作用有限)对这份数据来说,info()会立即暴露问题:state列存在非空计数少于总行数的情况(因为 CSV 中的空字段被 pandas 自动解析为NaN),这正是「缺失值」的第一信号。
第二步:诊断数据质量问题的根源
对照课程 README 归纳的常见清洗目标(格式统一、重复值、缺失值、探索性观察),本数据集存在的问题可逐项归类。
月份字段:大小写与缩写不一致
birth_month里January出现了三次,但写法分别是January、JAN、january;Sept与September实为同一个月。也就是说,同一月份在数据集中被拆成了多个互不相同的字符串。
州字段:缺失值 + 缩写/全称混用
state列第一条记录为空(NaN),同时CA与California指向同一个州,FL与Florida同理。若按原始字符串做统计,同一个州会被拆成两个类别。
宠物字段:值与标签约定不一致
pet列只出现Cats与Dog两种值,但它们既不是表单value中的小写复数(dogs/cats),也不是统一的大小写/单复数约定——Cats是复数而Dog是单数。若不同批次的数据分别按「标签」和「值」落库,后续合并必然产生新类别。
可视化为什么「看起来不正确」
练习中客户报告「部分可视化看起来不对」。原因并不在绘图本身,而在数据:value_counts()是按字符串精确相等分组的。课程 Notebook(2-Working-With-Data/08-data-preparation/notebook.ipynb)中的实践也印证了这一点——对脏的分类列直接做计数,等价于把同一实体的不同拼写当成不同类别:
form_df['state'].value_counts().plot(kind='bar') plt.show() form_df['birth_month'].value_counts().plot(kind='bar') plt.show()于是birth_month的柱状图上会出现三个并排的January/JAN/january柱子,state图上会出现两个并排的CA/California柱子——客户眼中「不对」的图,根源是数据未标准化,而不是代码有 bug。
第三步:用 Pandas 清洗表单数据
以下操作全部对应课程 README 与 Notebook 中讲解的核心 API,可直接在 assignment.ipynb 或独立 Python 环境中复现。
3.1 检测缺失值:isnull/notnull
课程指出,pandas 用NaN(IEEE 浮点规范中的特殊值)表示缺失浮点值,用 Python 的None表示其余类型的缺失,而isnull()/notnull()是检测两者的主要手段,返回布尔掩码:
form_df.isnull() # 逐元素布尔掩码 form_df.isnull().sum() # 每列缺失数量:state 列有 1 个 form_df.notnull() # 与 isnull 互补需要注意(课程 Notebook 中的例子):0是合法整数而非缺失;空字符串''在 pandas 看来同样是合法的字符串对象,不是缺失值。本数据集中state的空单元格之所以被识别为NaN,是因为read_csv在解析时就把空字段转成了缺失值。
3.2 处理缺失值:dropna/fillna
课程提供了两种策略。一是删除,用dropna():
form_df.dropna() # 默认 how='any':删除含任一缺失值的行 form_df.dropna(axis='columns') # 按列方向删除 form_df.dropna(how='all') # 仅当整行全为缺失时才删除 form_df.dropna(thresh=3) # 保留非空值数量 >= 3 的行 form_df.dropna(subset=['state']) # 仅依据指定列判断缺失二是填充,用fillna(),可以填固定值、前向填充或后向填充:
form_df.fillna('Unknown') # 用固定值填充 form_df.fillna(method='ffill') # 前向填充:用上一个有效值填补 form_df.fillna(method='bfill') # 后向填充:用下一个有效值填补兼容性提示:课程材料沿用
fillna(method='ffill')的写法;在较新版本的 pandas 中该方法已被弃用,推荐改用等价的df.ffill()与df.bfill()。
3.3 标准化分类值:映射字典 + 字符串方法
针对大小写与缩写混用,课程 Notebook 给出的做法是「先统一字符串形式,再用映射字典标准化」——这也是本练习最核心的修复步骤。以下代码针对birth_month、state、pet三列分别处理:
# 月份:统一为「首字母大写的完整月份名」 month_mapping = { 'jan': 'January', 'feb': 'February', 'mar': 'March', 'apr': 'April', 'may': 'May', 'jun': 'June', 'jul': 'July', 'aug': 'August', 'sep': 'September', 'oct': 'October', 'nov': 'November', 'dec': 'December', } def normalize_month(v): if pd.isnull(v): return v s = str(v).strip().title() # 'JAN' -> 'Jan','sept' -> 'Sept' return month_mapping.get(s[:3].lower(), s) form_df['birth_month_clean'] = form_df['birth_month'].apply(normalize_month) # 州:缩写与全称统一为两字母大写缩写(示例节选,可按需补全 50 州) state_mapping = { 'alaska': 'AK', 'california': 'CA', 'florida': 'FL', 'hawaii': 'HI', 'rhode island': 'RI', } def normalize_state(v): if pd.isnull(v): return v s = str(v).strip().lower() if len(s) == 2: # 已是缩写,统一大写 return s.upper() return state_mapping.get(s, v) form_df['state_clean'] = form_df['state'].apply(normalize_state) # 宠物:统一为小写复数,与表单 <select> 的 value 约定保持一致 def normalize_pet(v): if pd.isnull(v): return v s = str(v).strip().lower() return {'dog': 'dogs', 'cat': 'cats'}.get(s, s) form_df['pet_clean'] = form_df['pet'].apply(normalize_pet)标准化之后再统计,柱子数量才会与真实类别数一致:
form_df['birth_month_clean'].value_counts() # January 现在只有一根柱子 form_df['state_clean'].value_counts() # CA 与 California 合并为一类对于更复杂的拼写差异,课程 Notebook 还演示了用rapidfuzz库做模糊匹配(相似度高于 70% 即提示为近义名称),适合拼写变体多、难以穷举映射表的场景,可参考 notebook.ipynb 中的「Detecting Inconsistent Categorical Values」一节。
3.4 检测与删除重复值:duplicated/drop_duplicates
课程指出,重复数据会扭曲统计结果、产生不准确的分析,通常应删除;但在多数据集合并时,部分「重复」行可能携带补充信息,需具体判断。检测与删除接口如下:
form_df.duplicated() # 布尔掩码,标记与更早行重复的行 form_df.duplicated().sum() # 完全重复的行数 form_df.drop_duplicates() # 删除完全重复行 form_df.drop_duplicates(subset=['pet']) # 仅依据指定列判断重复本数据集中虽然没有完全相同的行,但存在近重复行:第 0 行(January,缺州,Cats)与第 7 行(January,California,Cats)在月份与宠物上完全一致,仅州字段一缺一全——这正是课程 Notebook「Detecting Near-Duplicate Rows」一节针对的场景。可以先做 3.3 的标准化,再按关键列检测近重复:
form_df[form_df.duplicated(subset=['birth_month_clean', 'pet_clean'], keep=False)]对这类近重复行,建议与客户确认是否同一用户重复提交,再决定去重或保留。
3.5 参考:完整的清洗流水线
课程 Notebook 在末尾提供了一个可直接复用的「脏数据 → 干净数据」流水线示例(创建示例脏数据集后依次执行三步):1. 检测不一致的分类值并标准化(映射字典 / 模糊匹配);2. 检测异常数值(离群点);3. 检测近重复行。本练习的数据集为纯分类字段,暂不涉及数值离群点,但若后续表单增加年龄、金额等数值字段,可直接套用该流水线的离群点检测逻辑,详见 notebook.ipynb 末尾的「Creating a Sample 'Dirty' Dataset」一节。
第四步:从源头改进表单设计
课程 README 强调,清洗的最终目标不仅是修好一份数据,更是保证「易用与复用、跨数据集的一致性、模型准确性」。因此本练习的落点应是建议客户改进表单本身,从源头避免脏数据。结合 index.html 的现状,可给出如下可落地建议:
- Birth Month:将自由文本框替换为 12 个固定月份的
<select>下拉框(或用日期选择器),彻底消除大小写与缩写差异;required属性保持必填语义。 - State:替换为包含 50 个两字母州缩写(
CA、FL等)的下拉框,禁止自由文本,从机制上杜绝「缩写 vs 全称」混用。 - Pets:统一
value与显示标签的约定(例如value="dogs"配标签Dogs),并保证入库值使用value而非显示文本,避免单复数/大小写漂移。 - 校验兜底:为文本类字段补充 HTML5
required、pattern、maxlength等约束;后端入库前再做一次字符串strip()、title()/upper()归一化,作为第二道防线。 - 口径文档化:在表单页或数据字典中明确每个字段的取值标准,方便后续团队跨数据集合并时保持一致性(对应课程强调的「Consistency」目标)。
评分建议与自查清单
- 优秀(Exemplary):能同时给出 ① 数据质量诊断(缺失、格式、重复、近重复);② 可视化异常的解释(
value_counts按精确字符串分组);③ 可运行的 pandas 清洗代码;④ 指向表单源码的具体改进建议。 - 合格(Adequate):识别出大部分问题并给出代码或表单层面的处理建议。
- 需改进(Needs Improvement):仅罗列现象、缺乏根因分析与可操作方案。
延伸学习
- 课程主文档:2-Working-With-Data/08-data-preparation/README.md(清洗的意义、缺失值/重复值处理的完整讲解)
- 配套 Notebook:2-Working-With-Data/08-data-preparation/notebook.ipynb(含脏数据流水线示例与课后练习)
- 数据探查与后续分析:4-Data-Science-Lifecycle/15-analyzing/README.md
- pandas 基础(DataFrame 概览):2-Working-With-Data/07-python/README.md
- 练习文件:assignment.ipynb、表单页、数据集
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考