数据清洗模块模拟训练题1-参考答案

  • ~17.11K 字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
"""
参考答案脚本
包含4套题目的完整解答代码
"""
import pandas as pd
import numpy as np
import os
import glob
import re

# ============================================================
# 模拟题一:电商订单数据清洗 - 参考答案
# ============================================================
def clean_order_data():
"""模拟题一参考答案"""
print("=" * 60)
print("模拟题一:电商订单数据清洗 - 参考答案")
print("=" * 60)

# 读取数据
filepath = os.path.join(DATA_DIR, "order_data.csv")
df = pd.read_csv(filepath)
print(f"\n[1] 原始数据形状: {df.shape}")
print(f" 原始数据列名: {list(df.columns)}")
print(f" 缺失值统计:\n{df.isnull().sum()}")

# 任务1:列名修改
df.rename(columns={
"单价(元)": "商品单价",
"订单金额(元)": "订单总金额",
"下单时间": "交易时间"
}, inplace=True)
print(f"\n[2] 列名修改后: {list(df.columns)}")

# 任务2:缺失值处理 - 任何一条数据中只要有一个缺失值就删除
before = len(df)
df.dropna(inplace=True)
after = len(df)
print(f"\n[3] 缺失值处理: 删除了 {before - after} 条含缺失值的数据, 剩余 {after} 条")

# 任务3:数据类型转换
df["购买数量"] = df["购买数量"].astype("int64")
df["商品单价"] = df["商品单价"].astype("float64")
df["订单总金额"] = df["订单总金额"].astype("float64")
print(f"\n[4] 数据类型转换后:\n{df.dtypes}")

# 任务4:日期处理 - 只保留日期部分
# 提取"2023年XX月XX日"格式的日期
df["交易时间"] = df["交易时间"].str.extract(r"(\d{4}年\d{1,2}月\d{1,2}日)")
df["交易时间"] = pd.to_datetime(df["交易时间"], format="%Y年%m月%d日", errors="coerce")
print(f"\n[5] 日期处理后:\n{df['交易时间'].head()}")

# 任务5:删除日期转换后为空的行
before = len(df)
df.dropna(subset=["交易时间"], inplace=True)
after = len(df)
print(f"\n[6] 删除无效日期行: 删除了 {before - after} 条, 剩余 {after} 条")

# 任务6:按交易日期升序排列,重置索引
df.sort_values(by="交易时间", ascending=True, inplace=True)
df.reset_index(drop=True, inplace=True)
print(f"\n[7] 排序后前5条日期: {df['交易时间'].head().tolist()}")

# 任务7:异常值处理
before = len(df)
df = df[(df["购买数量"] > 0) & (df["购买数量"] <= 100)]
df = df[df["商品单价"] >= 0]
df = df[df["订单总金额"] >= 0]
after = len(df)
print(f"\n[8] 异常值处理: 删除了 {before - after} 条异常数据, 剩余 {after} 条")

# 任务8:重复值处理
before = len(df)
df.drop_duplicates(subset=["订单编号"], keep="first", inplace=True)
after = len(df)
print(f"\n[9] 重复值处理: 删除了 {before - after} 条重复数据, 剩余 {after} 条")

# 任务9:保存清洗后的数据
output_path = os.path.join(OUTPUT_DIR, "cleaned_order_data.csv")
df.to_csv(output_path, index=False, encoding="utf-8-sig")
print(f"\n[10] 清洗后数据已保存: {output_path}")
print(f" 最终数据形状: {df.shape}")
print(f" 最终数据预览:\n{df.head()}")

return df

# ============================================================
# 模拟题二:学生成绩数据清洗 - 参考答案
# ============================================================
def clean_student_score_data():
"""模拟题二参考答案"""
print("\n" + "=" * 60)
print("模拟题二:学生成绩数据清洗 - 参考答案")
print("=" * 60)

# 读取数据
filepath = os.path.join(DATA_DIR, "student_score.csv")
df = pd.read_csv(filepath)
print(f"\n[1] 原始数据形状: {df.shape}")
print(f" 原始数据列名: {list(df.columns)}")
print(f" 缺失值统计:\n{df.isnull().sum()}")

# 任务1:列名修改
df.rename(columns={
"考试日期": "考试时间",
"科目名称": "课程名称",
"等级": "成绩等级"
}, inplace=True)
print(f"\n[2] 列名修改后: {list(df.columns)}")

# 任务2:缺失值处理
before = len(df)
df.dropna(inplace=True)
after = len(df)
print(f"\n[3] 缺失值处理: 删除了 {before - after} 条含缺失值的数据, 剩余 {after} 条")

# 任务3:数据类型转换
df["平时成绩"] = df["平时成绩"].astype("int64")
df["期中成绩"] = df["期中成绩"].astype("int64")
df["期末成绩"] = df["期末成绩"].astype("int64")
df["总评成绩"] = df["总评成绩"].astype("float64")
print(f"\n[4] 数据类型转换后:\n{df[['平时成绩','期中成绩','期末成绩','总评成绩']].dtypes}")

# 任务4:日期处理
df["考试时间"] = df["考试时间"].str.extract(r"(\d{4}/\d{1,2}/\d{1,2})")
df["考试时间"] = pd.to_datetime(df["考试时间"], format="%Y/%m/%d", errors="coerce")
print(f"\n[5] 日期处理后:\n{df['考试时间'].head()}")

# 任务5:删除无效日期行
before = len(df)
df.dropna(subset=["考试时间"], inplace=True)
after = len(df)
print(f"\n[6] 删除无效日期行: 删除了 {before - after} 条, 剩余 {after} 条")

# 任务6:排序与索引重置
df.sort_values(by="考试时间", ascending=True, inplace=True)
df.reset_index(drop=True, inplace=True)
print(f"\n[7] 排序后前5条日期: {df['考试时间'].head().tolist()}")

# 任务7:异常值处理
before = len(df)
df = df[(df["平时成绩"] >= 0) & (df["平时成绩"] <= 100)]
df = df[(df["期中成绩"] >= 0) & (df["期中成绩"] <= 100)]
df = df[(df["期末成绩"] >= 0) & (df["期末成绩"] <= 100)]
df = df[df["总评成绩"] >= 0]
after = len(df)
print(f"\n[8] 异常值处理: 删除了 {before - after} 条异常数据, 剩余 {after} 条")

# 任务8:重复值处理
before = len(df)
df.drop_duplicates(subset=["学号", "课程名称"], keep="first", inplace=True)
after = len(df)
print(f"\n[9] 重复值处理: 删除了 {before - after} 条重复数据, 剩余 {after} 条")

# 任务9:保存
output_path = os.path.join(OUTPUT_DIR, "cleaned_student_score.csv")
df.to_csv(output_path, index=False, encoding="utf-8-sig")
print(f"\n[10] 清洗后数据已保存: {output_path}")
print(f" 最终数据形状: {df.shape}")
print(f" 最终数据预览:\n{df.head()}")

return df

# ============================================================
# 练习题三:多文件数据处理 - 参考答案
# ============================================================
def clean_multi_file_sales():
"""练习题三参考答案"""
print("\n" + "=" * 60)
print("练习题三:多文件数据处理 - 参考答案")
print("=" * 60)

# 任务1:使用glob获取所有CSV文件路径
pattern = os.path.join(DATA_DIR, "sales_2023_*.csv")
file_list = sorted(glob.glob(pattern))
print(f"\n[1] 找到 {len(file_list)} 个数据文件:")
for f in file_list:
print(f" - {os.path.basename(f)}")

# 任务2:循环读取并合并
dfs = []
for filepath in file_list:
df_temp = pd.read_csv(filepath)
print(f" 读取 {os.path.basename(filepath)}: {len(df_temp)} 条记录")
dfs.append(df_temp)

df = pd.concat(dfs, axis=0, ignore_index=True)
print(f"\n[2] 合并后数据形状: {df.shape}")

# 任务3:缺失值处理
before = len(df)
df.dropna(inplace=True)
after = len(df)
print(f"\n[3] 缺失值处理: 删除了 {before - after} 条, 剩余 {after} 条")

# 任务4:数据类型转换
df["销售数量"] = df["销售数量"].astype("int64")
df["销售金额"] = df["销售金额"].astype("float64")
df["日期"] = pd.to_datetime(df["日期"], errors="coerce")
print(f"\n[4] 数据类型转换后:\n{df.dtypes}")

# 任务5:异常值处理
before = len(df)
df = df[df["销售数量"] > 0]
df = df[df["销售金额"] > 0]
after = len(df)
print(f"\n[5] 异常值处理: 删除了 {before - after} 条, 剩余 {after} 条")

# 任务6:重复值处理
before = len(df)
df.drop_duplicates(keep="first", inplace=True)
after = len(df)
print(f"\n[6] 重复值处理: 删除了 {before - after} 条, 剩余 {after} 条")

# 任务7:排序与索引重置
df.sort_values(by="日期", ascending=True, inplace=True)
df.reset_index(drop=True, inplace=True)
print(f"\n[7] 排序后日期范围: {df['日期'].min()} ~ {df['日期'].max()}")

# 任务8:分组统计
store_summary = df.groupby("门店名称").agg(
总销售数量=("销售数量", "sum"),
总销售金额=("销售金额", "sum")
).reset_index()
store_summary = store_summary.sort_values(by="总销售金额", ascending=False)
print(f"\n[8] 门店销售统计:\n{store_summary}")

summary_path = os.path.join(OUTPUT_DIR, "store_summary.csv")
store_summary.to_csv(summary_path, index=False, encoding="utf-8-sig")
print(f"\n 门店统计已保存: {summary_path}")

# 任务9:保存完整数据
output_path = os.path.join(OUTPUT_DIR, "all_sales_data.csv")
df.to_csv(output_path, index=False, encoding="utf-8-sig")
print(f"\n[9] 合并清洗后数据已保存: {output_path}")
print(f" 最终数据形状: {df.shape}")

return df

# ============================================================
# 练习题四:中文数字转换为阿拉伯数字 - 参考答案
# ============================================================
def chinese_to_arabic(text):
"""
将中文数字转换为阿拉伯数字
支持基本数字和单位:零一二三四五六七八九十百千万亿
示例: "一百二十三" -> 123, "三千五百万" -> 35000000, "四十五" -> 45
"""
if pd.isna(text) or text is None:
return None

text = str(text).strip()

# 定义中文数字映射
cn_num = {
"零": 0, "〇": 0, "一": 1, "壹": 1,
"二": 2, "贰": 2, "两": 2,
"三": 3, "叁": 3,
"四": 4, "肆": 4,
"五": 5, "伍": 5,
"六": 6, "陆": 6,
"七": 7, "柒": 7,
"八": 8, "捌": 8,
"九": 9, "玖": 9,
}

cn_unit_simple = {
"十": 10, "拾": 10,
"百": 100, "佰": 100,
"千": 1000, "仟": 1000,
}

cn_unit_big = {
"万": 10000, "萬": 10000,
"亿": 100000000, "億": 100000000,
}

# 如果已经是纯数字,直接返回
if text.isdigit():
return int(text)

# 先按大单位(万、亿)拆分段
# 在每个大单位前插入分隔符,然后分段处理
segments = [] # 每段是一个小段(不含万/亿),以及其后的大单位值
current_segment = ""
i = 0
while i < len(text):
ch = text[i]
if ch in cn_unit_big:
# 当前段结束,记录段和大单位
segments.append((current_segment, cn_unit_big[ch]))
current_segment = ""
elif ch in cn_num or ch in cn_unit_simple:
current_segment += ch
# 其他字符跳过
i += 1
# 最后一段(没有大单位)
if current_segment:
segments.append((current_segment, 1))

total = 0
for seg_text, multiplier in segments:
# 解析一个小段(如 "三千五百万"中的"三千五")
seg_val = 0
j = 0
while j < len(seg_text):
ch = seg_text[j]
if ch in cn_num:
digit = cn_num[ch]
# 看后面是否有单位
if j + 1 < len(seg_text) and seg_text[j + 1] in cn_unit_simple:
unit = cn_unit_simple[seg_text[j + 1]]
seg_val += digit * unit
j += 2
else:
# 没有单位,就是个位数
# 但如果前面已经有值且当前位是0,跳过
seg_val += digit
j += 1
elif ch in cn_unit_simple:
# 前面没有数字,如"十二"中的"十"默认为1
seg_val += cn_unit_simple[ch]
j += 1
else:
j += 1
total += seg_val * multiplier

return total if total != 0 else 0

def clean_chinese_number_data():
"""练习题四参考答案"""
print("\n" + "=" * 60)
print("练习题四:中文数字转换为阿拉伯数字 - 参考答案")
print("=" * 60)

# 任务1:转换函数已定义(chinese_to_arabic)
# 测试转换函数
test_cases = [
("一百二十三", 123),
("三千五百万", 35000000),
("四十五", 45),
("十二", 12),
("三百五十万", 3500000),
("一千零二十", 1020),
("八", 8),
("一百", 100),
("二千零八十万", 20800000),
]
print("\n[1] 转换函数测试:")
for cn, expected in test_cases:
result = chinese_to_arabic(cn)
status = "✓" if result == expected else "✗"
print(f" {status} '{cn}' -> {result} (期望: {expected})")

# 任务2:读取数据并删除缺失值
filepath = os.path.join(DATA_DIR, "chinese_number_data.csv")
df = pd.read_csv(filepath)
print(f"\n[2] 原始数据形状: {df.shape}")
before = len(df)
df.dropna(inplace=True)
after = len(df)
print(f" 删除缺失值: 删除了 {before - after} 条, 剩余 {after} 条")

# 任务3:应用转换函数
# 定义辅助函数:去除单位后转换
def convert_and_strip(text, suffix):
"""去除后缀后转换中文数字"""
if pd.isna(text):
return None
text = str(text).replace(suffix, "")
return chinese_to_arabic(text)

df["合同金额(数字)"] = df["合同金额(中文)"].apply(lambda x: convert_and_strip(x, "万元"))
df["实际支出(数字)"] = df["实际支出(中文)"].apply(lambda x: convert_and_strip(x, "万元"))
df["工期(数字)"] = df["工期(中文)"].apply(lambda x: convert_and_strip(x, "天"))
df["参与人数(数字)"] = df["参与人数(中文)"].apply(lambda x: convert_and_strip(x, "人"))

print(f"\n[3] 转换结果预览:")
print(df[["项目名称", "合同金额(中文)", "合同金额(数字)", "实际支出(中文)", "实际支出(数字)"]].head())

# 任务4:数据类型转换
df["合同金额(数字)"] = df["合同金额(数字)"].astype("int64")
df["实际支出(数字)"] = df["实际支出(数字)"].astype("int64")
df["工期(数字)"] = df["工期(数字)"].astype("int64")
df["参与人数(数字)"] = df["参与人数(数字)"].astype("int64")
print(f"\n[4] 数据类型转换后:\n{df[['合同金额(数字)','实际支出(数字)','工期(数字)','参与人数(数字)']].dtypes}")

# 任务5:计算预算结余
df["预算结余(数字)"] = df["合同金额(数字)"] - df["实际支出(数字)"]
print(f"\n[5] 预算结余计算:")
print(df[["项目名称", "合同金额(数字)", "实际支出(数字)", "预算结余(数字)"]])

# 任务6:保存
output_path = os.path.join(OUTPUT_DIR, "cleaned_chinese_number_data.csv")
df.to_csv(output_path, index=False, encoding="utf-8-sig")
print(f"\n[6] 处理后数据已保存: {output_path}")
print(f" 最终数据形状: {df.shape}")

return df

# ============================================================
# 主函数
# ============================================================
if __name__ == "__main__":
print("=" * 60)
print("Python程序开发竞赛 - 数据清洗模块参考答案")
print("=" * 60)

clean_order_data()
clean_student_score_data()
clean_multi_file_sales()
clean_chinese_number_data()

print("\n" + "=" * 60)
print("所有参考答案执行完毕!")
print(f"结果保存目录: {OUTPUT_DIR}")
print("=" * 60)
分享