本篇文章分享数据分析中最重要的一个步骤:数据预处理
。我们在做数据分析之前,都需要采集很多数据,这些数据可能是从官网下载的,可能是从各个大型数据库网站付费购买的,也有可能是通过爬虫获取数据的(爬取数据时要注意规范,要遵守每个网址给的爬虫君子协议,政府官网尽量别去爬,不能非法爬取公民身份资料等敏感信息!!!)。
【补充】什么是网址给的爬虫君子协议?
- 进入你要爬取网址的根目录,在其后面加上:
/robots.txt
,就可以看到这个网址允许你爬取哪些分支下的数据,或者不允许你爬取哪些分支下的数据。
- 比如你要爬取知乎的信息,那么其君子协议看:
https://www.zhihu.com/robots.txt
,可以看到知乎不允许未授权用户爬取任何信息,即我们是不能去爬取知乎数据的。只允许了Googlebot
,Googlebot-Image
,Baiduspider-news
,Baiduspider
,Baiduspider-render
,Baiduspider-image
,bingbot
(截止于2024年1月16日只允许上述指定爬虫爬取数据) 爬取指定目录下的数据~- 又比如看看
CSDN
平台的君子协议:https://www.csdn.net/robots.txt
,它不允许任何人爬取的目录如下:
- User-agent: *
- Disallow: /scripts
- Disallow: /public
- Disallow: /css/
- Disallow: /images/
- Disallow: /content/
- Disallow: /ui/
- Disallow: /js/
- Disallow: /scripts/
- Disallow: /article_preview.html*
- Disallow: /tag/
- Disallow: /*?*
- Disallow: /link/
- Disallow: /tags/
- Disallow: /news/
- Disallow: /xuexi/
不论是从什么方式获取的数据,其数据内容或多或少会存在缺失值
、数据量纲跨度大
(就是同一列数据中,有的数据只是个位数,但有的数据范围到了几十万的级别),所以在做数据分析前,数据预处理是必须要做的,哪怕你的数据可能都很正常(其实只有做了这一步才知道你获得的数据正不正常)。
那么本篇文章将分享最常用的两种数据处理方法:z-score 标准化、min-max 归一化,以及数据缺失值处理 和 数据重复处理。
z-score 标准化是基于给定数据(data
)的 均值(mean
) 和 标准差(std
) 进行数据标准化的,其计算公式为:
n
e
w
D
a
t
a
=
d
a
t
a
?
m
e
a
n
s
t
d
newData = \frac{data - mean}{std}
newData=stddata?mean?
对应的 Python 代码示例:
import numpy as np
data = np.array([[ 1.1, 2, 3 ],
[ 9, 10, 11 ],
[10.9, 5, 0.1]])
# 计算均值
mean = np.mean(data, axis=0)
# 计算标准差
std = np.std(data, axis=0)
print(f"每一列的均值:{mean}\n")
print(f"每一列的标准差:{std}\n")
# 减去均值,除以标准差
newData = (data - mean) / std
print(f"z-score标准化:\n{newData}")
# 查看z-score标准化后的每一列均值 和 标准差
print(f"z-score标准化后的每一列均值:{np.mean(newData, axis=0)}\n")
print(f"z-score标准化后的每一列标准差:{np.std(newData, axis=0)}\n")
输出:
每一列的均值:[7. 5.66666667 4.7 ]
每一列的标准差:[4.24342629 3.29983165 4.60941066]
z-score标准化:
[[-1.39038588 -1.1111678 -0.36881071]
[ 0.47131725 1.31319831 1.36676909]
[ 0.91906863 -0.20203051 -0.99795838]]
z-score标准化后的每一列均值:[ 0.00000000e+00 -1.01770444e-16 -7.40148683e-17]
z-score标准化后的每一列标准差:[1. 1. 1.]
经过z-score标准化后的数据均值都是0,标准差都是1,当然由于计算机计算精度问题,所以现在看到的均值并不都是0,不过也是非常接近于0了!
看看标准化后的数据是什么样的:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(2024)
data = np.random.rand(100, 2) * 10
x = data[:, 0]
y = data[:, 1]
# 新建figure对象
plt.subplot(121)
plt.scatter(x, y, s=50, alpha=0.7, color='g')
# 设置标题
plt.title("原始数据", fontdict={'fontname': 'FangSong', 'fontsize': 'xx-large', 'fontweight': 'bold'})
plt.xlabel('x轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
plt.ylabel('y轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
plt.xlim(-5, 20)
plt.ylim(-5, 20)
plt.grid()
# 计算均值
mean = np.mean(data, axis=0)
# 计算标准差
std = np.std(data, axis=0)
# 减去均值,除以标准差
newData = (data - mean) / std
x = newData[:, 0]
y = newData[:, 1]
# 新建figure对象
plt.subplot(122)
plt.scatter(x, y, s=50, alpha=0.7, color='g')
# 设置标题
plt.title("标准化后的数据", fontdict={'fontname': 'FangSong', 'fontsize': 'xx-large', 'fontweight': 'bold'})
plt.xlabel('x轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
# plt.ylabel('y轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
plt.xlim(-5, 20)
plt.ylim(-5, 20)
plt.grid()
plt.show()
输出:
min-max 归一化是对给定数据进行线性变换,它是基于给定数据(data
)的极大值(max_d
)和极小值(min_d
)进行数据归一化操作的,计算公式如下:
n e w D a t a = d a t a ? m i n _ d m a x _ d ? m i n _ d newData = \frac{data - min\_d}{max\_d - min\_d} newData=max_d?min_ddata?min_d?
对应的 Python 代码示例:
import numpy as np
data = np.array([[ 1.1, 2, 3 ],
[ 9, 10, 11 ],
[10.9, 5, 0.1]])
# 计算极大值
max_d = np.max(data, axis=0)
# 计算极小值
min_d = np.min(data, axis=0)
print(f"每一列的极大值:{max_d}\n")
print(f"每一列的极小值:{min_d}\n")
# 减去极小值,除以 (极大值 - 极小值)
print(f"min-max标准化:\n{(data - min_d) / (max_d - min_d)}")
输出:
每一列的极大值:[10.9 10. 11. ]
每一列的极小值:[1.1 2. 0.1]
min-max标准化:
[[0. 0. 0.26605505]
[0.80612245 1. 1. ]
[1. 0.375 0. ]]
看看归一化的数据图像是什么样的:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(2024)
data = np.random.rand(100, 2) * 10
x = data[:, 0]
y = data[:, 1]
# 新建figure对象
plt.subplot(131)
plt.scatter(x, y, s=50, alpha=0.7, color='g')
# 设置标题
plt.title("原始数据", fontdict={'fontname': 'FangSong', 'fontsize': 'xx-large', 'fontweight': 'bold'})
plt.xlabel('x轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
plt.ylabel('y轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
plt.xlim(-5, 20)
plt.ylim(-5, 20)
plt.grid()
# 计算极大值
max_d = np.max(data, axis=0)
# 计算极小值
min_d = np.min(data, axis=0)
# 减去均值,除以标准差
newData = (data - min_d) / (max_d - min_d)
x = newData[:, 0]
y = newData[:, 1]
# 新建figure对象
plt.subplot(132)
plt.scatter(x, y, s=50, alpha=0.7, color='g')
# 设置标题
plt.title("归一化后的数据", fontdict={'fontname': 'FangSong', 'fontsize': 'xx-large', 'fontweight': 'bold'})
plt.xlabel('x轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
# plt.ylabel('y轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
plt.xlim(-5, 20)
plt.ylim(-5, 20)
plt.grid()
# 新建figure对象
plt.subplot(133)
plt.scatter(x, y, s=50, alpha=0.7, color='g')
# 设置标题
plt.title(f"归一化后的数据\n(放大版)", fontdict={'fontname': 'FangSong', 'fontsize': 'xx-large', 'fontweight': 'bold'})
plt.xlabel('x轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
# plt.ylabel('y轴', fontdict={'fontname': 'FangSong', 'fontsize': 20})
plt.xlim(-0.5, 1.5)
plt.ylim(-0.5, 1.5)
plt.grid()
# wspace 控制子图列间距, hspace 控制子图横间距
plt.subplots_adjust(wspace=0.5, hspace=0)
plt.show()
输出:
可以看见所有数据都缩放到 [0, 1] 之间了~
现在要用到一个新的第三方库:pandas
,这是数据分析中常用的库。
关键方法:.fillna(value, inplace=True)
,参数含义如下:
参数 | 含义 |
---|---|
value | 你要将缺失值替换成的值 |
inplace | 是否覆盖数据框(默认是:False ,不覆盖,生成一个新的数据框) |
自行创建一个文件:test.csv
,数据如下:
示例:
import pandas as pd
df = pd.read_csv("test.csv", encoding="utf-8")
print(df)
print()
df['age'].fillna(20, inplace=True) # 更新age列的NaN为20
df['score'].fillna(0, inplace=True) # 更新score列的NaN为0
print(df)
输出:
Unnamed: 0 name age score
0 0 senlin NaN NaN
1 1 Tom NaN 100.0
2 2 Mary 18.0 60.0
3 2 Mary 18.0 60.0
Unnamed: 0 name age score
0 0 senlin 20.0 0.0
1 1 Tom 20.0 100.0
2 2 Mary 18.0 60.0
3 2 Mary 18.0 60.0
关键方法:.drop_duplicates(keep=keep, inplace=True)
,各参数含义如下:
参数 | 含义 |
---|---|
keep | 保留第一个重复值还是最后一个重复值,可以选:first , last |
inplace | 是否覆盖数据框(默认是:False ,不覆盖,生成一个新的数据框) |
示例(保留第一个重复值):
import pandas as pd
df = pd.read_csv("test.csv", encoding="utf-8")
print(df)
print()
df.drop_duplicates(keep='first', inplace=True)
print(df)
输出:
Unnamed: 0 name age score
0 0 senlin NaN NaN
1 1 Tom NaN 100.0
2 2 Mary 18.0 60.0
3 2 Mary 18.0 60.0
Unnamed: 0 name age score
0 0 senlin NaN NaN
1 1 Tom NaN 100.0
2 2 Mary 18.0 60.0
示例(保留最后一个重复值):
import pandas as pd
df = pd.read_csv("test.csv", encoding="utf-8")
print(df)
print()
df.drop_duplicates(keep='last', inplace=True)
print(df)
输出:
Unnamed: 0 name age score
0 0 senlin NaN NaN
1 1 Tom NaN 100.0
2 2 Mary 18.0 60.0
3 2 Mary 18.0 60.0
Unnamed: 0 name age score
0 0 senlin NaN NaN
1 1 Tom NaN 100.0
3 2 Mary 18.0 60.0