脏数据
不完美的数据
Numpy 的一切都是和数据打交道,但是在世纪情况下数据其实是不完整,不完美的。比如下面这张图里面, 你会发现,这份数据显然有些不完整的地方,city有数据缺失,duration 虽然是时间上的数据,但是时间单位不统一,时间格式不统一, 这都给后面我们让机器使用这份数据增加难度。
脏数据类型
其实如果你真有做过数据分析,当你拿到一份数据的时候,是十分期望这份数据没什么问题的,但是事与愿违,通常这份数据都多多少少有些问题。 常见的脏数据种类有:
raw_data = [
[“Name”, “StudentID”, “Age”, “AttendClass”, “Score”],
[“小明”, 20131, 10, 1, 67],
[“小花”, 20132, 11, 1, 88],
[“小菜”, 20133, None, 1, “98”],
[“小七”, 20134, 8, 1, 110],
[“花菜”, 20134, 98, 0, None],
[“刘欣”, 20136, 12, 0, 12]
]
data = np.array(raw_data) data array([[‘Name’, ‘StudentID’, ‘Age’, ‘AttendClass’, ‘Score’], [‘小明’, 20131, 10, 1, 67], [‘小花’, 20132, 11, 1, 88], [‘小菜’, 20133, None, 1, ‘98’], [‘小七’, 20134, 8, 1, 110], [‘花菜’, 20134, 98, 0, None], [‘刘欣’, 20136, 12, 0, 12]], dtype=object)
这时的 array 输出的结果,结尾处有一个标识。dtype=object,这种 dtype 会对后续数据处理带来很多麻烦,Python list 直接转换过来的 data 是无法参与诸多 Numpy 计算的。 而只有 dtype 为 int,float 这样的数值形式,才能参与运算。
<a name="goFBZ"></a>
## 数据清洗流程
<a name="xTMZW"></a>
### 1 数据预处理
去掉行首列首,筛选过滤string
```python
data_process = []
for i in range(len(raw_data)):
if i == 0:
continue # 不要首行字符串
# 去掉首列名字
data_process.append(raw_data[i][1:])
data = np.array(data_process, dtype=np.float) # 字符转化为数字,缺失值转化为nan,因为这份数据中存在 None,而只有 np.float 能转换 None
print("data.dtype", data.dtype)
print(data)
data.dtype float64
[[2.0131e+04 1.0000e+01 1.0000e+00 6.7000e+01]
[2.0132e+04 1.1000e+01 1.0000e+00 8.8000e+01]
[2.0133e+04 nan 1.0000e+00 9.8000e+01]
[2.0134e+04 8.0000e+00 1.0000e+00 1.1000e+02]
[2.0134e+04 9.8000e+01 0.0000e+00 nan]
[2.0136e+04 1.2000e+01 0.0000e+00 1.2000e+01]]
2 清洗数据
1,索引号重复检查和修改
我们要看看有没有什么数据是不合逻辑的。比如我发现学号有重复,可能是在输入学生信息的时候手误输错了。
sid = data[:, 0]
unique, counts = np.unique(sid, return_counts=True)
print(unique)
print(counts)
[20131. 20132. 20133. 20134. 20136.]
[1 1 1 2 1]
我们可以看到有一个数据 20134 重复出现了 2 次。然后综合判断,我们的数据中少了一个 20135,可能就是把某个同学的学号输错了,我们将错误的同学修改过来。
data[4, 0] = 20135
print(data)
[[2.0131e+04 1.0000e+01 1.0000e+00 6.7000e+01]
[2.0132e+04 1.1000e+01 1.0000e+00 8.8000e+01]
[2.0133e+04 nan 1.0000e+00 9.8000e+01]
[2.0134e+04 8.0000e+00 1.0000e+00 1.1000e+02]
[2.0135e+04 9.8000e+01 0.0000e+00 nan]
[2.0136e+04 1.2000e+01 0.0000e+00 1.2000e+01]]
2,缺失的信息补全
查看缺失值,计算有数据的平均数
is_nan = np.isnan(data[:,1])
print("is_nan:", is_nan)
nan_idx = np.argwhere(is_nan) # 查看缺失值
is_nan: [False False True False False False]
# 计算有数据的平均年龄,用 ~ 符号可以 True/False 对调
mean_age = data[~np.isnan(data[:,1]), 1].mean()
print("有数据的平均年龄:", mean_age)
有数据的平均年龄: 27.8
3, 处理异常值
我们可以看到有一个 98 岁的学生,这不太正常,所以把这个 98 岁的当异常数据看待。然后用平均值代替。根据项目调整
# ~ 表示 True/False 对调,& 就是逐个做 Python and 的运算
normal_age_mask = ~np.isnan(data[:,1]) & (data[:,1] < 20)
print("normal_age_mask:", normal_age_mask)
normal_age_mask: [ True True False True False True]
normal_age_mean = data[normal_age_mask, 1].mean()
print("normal_age_mean:", normal_age_mean)
normal_age_mean: 10.25
data[~normal_age_mask, 1] = normal_age_mean
print("ages:", data[:, 1])
ages: [10. 11. 10.25 8. 10.25 12. ]
4, 数据值异常大或小
因为没上课,就没成绩,但是倒数第一行,没上课,怎么还有成绩?还有倒数第三行,成绩居然超出了满分 100 分。这些情况都是我们要处理的情况。
# 没上课的转成 nan
data[data[:,2] == 0, 3] = np.nan
# 超过 100 分和低于 0 分的都处理一下
data[:, 3] = np.clip(data[:, 3], 0, 100)
print(data[:, 2:])
[[ 1. 67.]
[ 1. 88.]
[ 1. 98.]
[ 1. 100.]
[ 0. nan]
[ 0. nan]]
print(data)
[[2.0131e+04 1.0000e+01 1.0000e+00 6.7000e+01]
[2.0132e+04 1.1000e+01 1.0000e+00 8.8000e+01]
[2.0133e+04 1.0250e+01 1.0000e+00 9.8000e+01]
[2.0134e+04 8.0000e+00 1.0000e+00 1.0000e+02]
[2.0135e+04 1.0250e+01 0.0000e+00 nan]
[2.0136e+04 1.2000e+01 0.0000e+00 nan]]