第零步 : 明确任务
- 多元异常值检测(多元, 异常值检测)
- 可视化定性分析
- 定量分析与异常值处理
完成所有工作之后,进行了回溯,对上一步进行优化:
一. 明确一个大概:
- 明确是哪个方向的任务(有监督 / 无监督 / 半监督)
- 明确是什么样的任务(分类 / 回归)
- 大方向是什么( 预测 / 优化 )
二. 任务的初步探索
- 数据层
1.1 定性方面
1.2 定量方面 - 特征层
2.1 定性方面
2.2 定量方面 - 模型层:
3.1 模型选取
3.2 训练、验证及参数调整策略
3.3 优化方法与融合方式
第一步:查看数据
从这里看出,数据是按照一定规则进行分布的(一定规则 : 每个数据之间是用 空格隔开的 , 且每一行都有5列,每个行之间是靠换行符隔开的 )
并且这些数据后面都写上了 e-02, 或 e-03: 那这里是什么意思呢?
答: 这里是表示小数点后几位的意思,也就是小数点要移动几位的意思
举例: 1.22e-02 = 小数点往前移动两位 = 0.0122
/ 这里的 - 代表 缩小的意思
这里的分割方式是 sep = " "
这里不需要第一行作为列名,因此需要在后面的地方进行设置,header = None
解决完上面的所有之后,才是正式对数据进行探索和处理
第二步:数据探索
2.1 读取数据
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
path = r"F:/Python_Tensorflow_codes/006group_learning/team-learning-data-mining-master/AnomalyDetection/outlier.txt"
# df_txt1 = pd.read_csv(path, header=None)
# print(df_txt1)
df_txt = pd.read_table(path, sep=" ", header=None)
df_txt
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 0.036853 | 0.034390 | 0.091979 | -0.010263 | -0.008141 |
1 | -0.001152 | 0.021750 | -0.020401 | 0.009866 | -0.034471 |
2 | -0.012586 | 0.047364 | 0.011108 | -0.011569 | -0.023341 |
3 | -0.028378 | 0.043980 | 0.001264 | 0.023138 | 0.005426 |
4 | 0.022225 | 0.007152 | -0.037135 | -0.029387 | -0.099154 |
... | ... | ... | ... | ... | ... |
995 | 0.006989 | -0.039900 | 0.013303 | -0.035476 | 0.068688 |
996 | -0.004276 | -0.028075 | -0.000769 | -0.051646 | -0.057467 |
997 | -0.000544 | 0.059539 | -0.012897 | 0.032994 | -0.089494 |
998 | -0.039697 | 0.000563 | -0.032977 | 0.015441 | 0.007677 |
999 | -0.010866 | 0.022604 | 0.014564 | -0.014077 | -0.025783 |
1000 rows × 5 columns
2.2 查看数据的基本信息和常见的统计量
从上面的结果看出数据一共有5列columns,一共有1000行rows
我们要想检测异常值,先针对第一列进行,然后用循环的方式去遍历其他的类别
df_txt.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 0 1000 non-null float64
1 1 1000 non-null float64
2 2 1000 non-null float64
3 3 1000 non-null float64
4 4 1000 non-null float64
dtypes: float64(5)
memory usage: 39.2 KB
- 这个数据集一共有1000个样本, 且 数据样本是从 0 — 999
RangeIndex: 1000 entries, 0 to 999 ::: ‘RangeIndex = the range of index’
- 这个数据一共有 5 列, Data columns (total 5 columns), 接下来就是来对这些列进行说明
下面的 data columns : 表中写出了:
Non-null Count = 没有缺失值 统计, 这里表明没有缺失值,
数据类型是float64
df_txt.describe()
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
count | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 |
mean | -0.000405 | 0.000425 | -0.001732 | 0.000764 | -0.001384 |
std | 0.029394 | 0.031259 | 0.030364 | 0.030200 | 0.030905 |
min | -0.100500 | -0.100282 | -0.115040 | -0.101569 | -0.099154 |
25% | -0.021540 | -0.020828 | -0.022276 | -0.020037 | -0.022739 |
50% | 0.000150 | 0.000222 | -0.002650 | 0.000652 | -0.000494 |
75% | 0.019580 | 0.022964 | 0.018493 | 0.021632 | 0.019515 |
max | 0.094261 | 0.084309 | 0.091979 | 0.088520 | 0.090503 |
print(df_txt.head())
0 1 2 3 4
0 0.036853 0.034390 0.091979 -0.010263 -0.008141
1 -0.001152 0.021750 -0.020401 0.009866 -0.034471
2 -0.012586 0.047364 0.011108 -0.011569 -0.023341
3 -0.028378 0.043980 0.001264 0.023138 0.005426
4 0.022225 0.007152 -0.037135 -0.029387 -0.099154
2.3 数据的可视化分析(定性分析)
本次的任务是多元异常点的检测,既然没有缺失值,那么我们的着手就放在异常值的定性分析和定量分析上。
接下来,我们将进行异常值的定性分析(可视化判断)
那么可视化分析 = 画图 —> 画什么图呢?
答: 判断异常值常用的图形是—箱型图
首先绘制一个类别的箱型图
## 按照我们想要的情况取出数据
## 我们先要第一列,那么就通过类别的方式取出第一列
s = df_txt[0]
'''
## 这里有必要说明一下pandas的取数方法:
# df_txt[0][1] ### 代表df_txt[列][行] 先取出列,再取出第几个
# df_txt[3:5] ### 代表 df_txt[ 行数1 : 行数2 ] 这里取出的是从第4行到第6行数据(注意行数是从0开始计算的)
# df_txt[0:1, 2:3] ### 为什么这样就不对了呢??? 另外不对的还有 df_txt[: , 2] 这种类型
### 重点 : [num1 : num2 , num_a:num_b] 这是切片的方式,这种直接切片的方式不适用与pandas
### pandas 中有切片的方法 就是 .iloc[切片1,切片2]
### 直接切片方法可以用到哪里: 切片(slice)是对序列型对象(如list, string, tuple)的一种高级索引方法
'''
sns.boxplot(data=s, orient="v")
#### 这里是将类别进行了遍历
columns = df_txt.columns.tolist()[:5]
fig = plt.figure(figsize=(80, 60), dpi=80)
for i in range(5):
plt.subplot(5, 1, i+1)
sns.boxplot(df_txt[i], orient="h", width=0.5)
从上面的图片我们可以定性的看出异常值的位置的
而且对于这些类别而言,异常值存在的数量并没有超出我们不能接受的程度:
异常值过多 —> 过多的异常值对目标特征(标签类别)的影响程度较大 —> 因此删除此类特征
异常值可以接受 —> 保留特征,同时找出异常值并进行处理:
处理方法如下:
1.删除含有异常值的记录
2.将异常值视为缺失值,交给缺失值处理方法来处理
3.用平均值来修正
4.不处理
2.4 定量查找异常值,并进行处理(定量分析异常值)
2.4.1 通过统计量(箱型图中异常值的计算阈值)找出异常值
## 通过箱型图对异常值的判定方式来检测异常值,并进行相关操作
def outliers(data, col_name, scale=3): ## scale--尺度 一般取1.5-3 ## 定义异常值函数 = 寻找异常值 + 异常值处理
def box_plot_outliers(data_ser, box_scale): ## 定义 寻找异常值函数, 通过箱形图的异常值判断公式判断(这里会给出箱体的参数:类别和尺度)
##IQR = 尺度*(上四分位点-下四分位点) ## 这里的尺度可以根据经验选取,常选用 1.5 作为经验值,效果很好
IQR = box_scale * (data_ser.quantile(0.75) - data_ser.quantile(0.25)) # IQR = 箱形图的四分位数,内距 = (箱体的3/4 - 箱体的1/4)* 倍数
val_low = data_ser.quantile(0.25) - IQR #计算下边缘 ### 下边界阈值范围 ## 边界值
val_up = data_ser.quantile(0.75) + IQR #计算上边缘 ### 上边界阈值范围
rule_low = (data_ser < val_low) #小于下边缘的值 ### 异常值(下方的) ## rule_low, rule_up : 规则以下的值,规则以上的值。
rule_up = (data_ser > val_up) #大于上边缘的值 ### 异常值(上方的)
return (rule_low, rule_up), (val_low, val_up)
data_n = data.copy() # 对数据进行 copy() , 这里的 data_n = data_new
data_series = data_n[col_name] # 这里取出来的是 某一类别下的数据
rule, value = box_plot_outliers(data_series, box_scale=scale) # data_series数据的序列, box_scale是箱体的尺度
##对满足在rule_low以下或rule_up以上条件计数
index = np.arange(data_series.shape[0])[rule[0] | rule[1]] ### data_series.shape[0] 这个数据序列的长度是多少(有多少行) rule = (rule_low, rule_up)
print("删除了: {} 个数据".format(len(index))) ### 竖杠的意思:按位或运算 60|13 = 61
data_n.reset_index(drop=True, inplace=True) ##取删除离群点后的数据
print("剩余: {} 个数据".format(data_n.shape[0]))
index_low = np.arange(data_series.shape[0])[rule[0]]
outliers = data_series.iloc[index_low]#计在下边缘以下的点
print("小于下边缘线的数据详细:")
print(pd.Series(outliers).describe())
index_up = np.arange(data_series.shape[0])[rule[1]]#计在上边缘以上的点
outliers = data_series.iloc[index_up]
print("大于上边缘线的数据详细:")
print(pd.Series(outliers).describe())
##可视化数据
fig, ax = plt.subplots(1, 2, figsize=(5, 3))
sns.boxplot(y=data[col_name], data=data,ax=ax[0])
sns.boxplot(y=data_n[col_name], data=data_n,ax=ax[1])
return data_n
这里要注意: scale的选择 :
### 对第一组数据进行使用
outliers(df_txt, 0, scale=1)
# outliers(df_txt, 1, scale=1.5)
### 阈值选取的值不同,那它对于计算的阈值设定也就不同
# outliers(df_txt, 1, scale=3)
删除了: 35 个数据
剩余: 1000 个数据
小于下边缘线的数据详细:
count 15.000000
mean -0.072242
std 0.010630
min -0.100500
25% -0.077237
50% -0.070234
75% -0.063716
max -0.062705
Name: 0, dtype: float64
大于上边缘线的数据详细:
count 20.000000
mean 0.070432
std 0.008490
min 0.061253
25% 0.063830
50% 0.068014
75% 0.076154
max 0.094261
Name: 0, dtype: float64
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 0.036853 | 0.034390 | 0.091979 | -0.010263 | -0.008141 |
1 | -0.001152 | 0.021750 | -0.020401 | 0.009866 | -0.034471 |
2 | -0.012586 | 0.047364 | 0.011108 | -0.011569 | -0.023341 |
3 | -0.028378 | 0.043980 | 0.001264 | 0.023138 | 0.005426 |
4 | 0.022225 | 0.007152 | -0.037135 | -0.029387 | -0.099154 |
... | ... | ... | ... | ... | ... |
995 | 0.006989 | -0.039900 | 0.013303 | -0.035476 | 0.068688 |
996 | -0.004276 | -0.028075 | -0.000769 | -0.051646 | -0.057467 |
997 | -0.000544 | 0.059539 | -0.012897 | 0.032994 | -0.089494 |
998 | -0.039697 | 0.000563 | -0.032977 | 0.015441 | 0.007677 |
999 | -0.010866 | 0.022604 | 0.014564 | -0.014077 | -0.025783 |
1000 rows × 5 columns
## 接下来通过多组循环的方式,对数据进行定量分析
# col_name = df_txt.columns ### 试验
columns = df_txt.columns.tolist()[:5]
for i in range(df_txt.shape[1]):
print("category", i)
outliers(df_txt, columns[i], scale=1)
category 0
删除了: 35 个数据
剩余: 1000 个数据
小于下边缘线的数据详细:
count 15.000000
mean -0.072242
std 0.010630
min -0.100500
25% -0.077237
50% -0.070234
75% -0.063716
max -0.062705
Name: 0, dtype: float64
大于上边缘线的数据详细:
count 20.000000
mean 0.070432
std 0.008490
min 0.061253
25% 0.063830
50% 0.068014
75% 0.076154
max 0.094261
Name: 0, dtype: float64
category 1
删除了: 35 个数据
剩余: 1000 个数据
小于下边缘线的数据详细:
count 16.000000
mean -0.078005
std 0.009898
min -0.100282
25% -0.085692
50% -0.076782
75% -0.069292
max -0.066077
Name: 1, dtype: float64
大于上边缘线的数据详细:
count 19.000000
mean 0.073268
std 0.005226
min 0.066924
25% 0.068971
50% 0.071675
75% 0.076447
max 0.084309
Name: 1, dtype: float64
category 2
删除了: 48 个数据
剩余: 1000 个数据
小于下边缘线的数据详细:
count 21.000000
mean -0.075440
std 0.014680
min -0.115040
25% -0.078099
50% -0.070667
75% -0.064982
max -0.063122
Name: 2, dtype: float64
大于上边缘线的数据详细:
count 27.000000
mean 0.067204
std 0.007891
min 0.059763
25% 0.061541
50% 0.065973
75% 0.069225
max 0.091979
Name: 2, dtype: float64
category 3
删除了: 29 个数据
剩余: 1000 个数据
小于下边缘线的数据详细:
count 14.000000
mean -0.074974
std 0.012584
min -0.101569
25% -0.078545
50% -0.068299
75% -0.067407
max -0.062018
Name: 3, dtype: float64
大于上边缘线的数据详细:
count 15.000000
mean 0.073013
std 0.008230
min 0.063926
25% 0.065803
50% 0.070959
75% 0.077719
max 0.088520
Name: 3, dtype: float64
category 4
删除了: 37 个数据
剩余: 1000 个数据
小于下边缘线的数据详细:
count 19.000000
mean -0.080624
std 0.008203
min -0.099154
25% -0.084477
50% -0.078274
75% -0.074661
max -0.069339
Name: 4, dtype: float64
大于上边缘线的数据详细:
count 18.000000
mean 0.073144
std 0.007860
min 0.062123
25% 0.068213
50% 0.071288
75% 0.078326
max 0.090503
Name: 4, dtype: float64
我想的是让每组数据统计和相应的表进行对应, 但无法实现,判断理由:
执行:
outliers(df_txt, 0, scale=1)
outliers(df_txt, 1, scale=1.5)
执行完成之后,是同样的结果,因此不能对应显示
2.4.2 通过模型找出异常值,并处理
同样的,我们要先定义模型寻找异常值函数
# 量化定义寻找异常值的函数find_outlier()
def find_outlier(model, X, y, sigma=3):
# 找出 y_pred
try: ### 用 try: 尝试执行,如果已经训练好了模型,那么就用 try后面的语句
y_pred = pd.Series(model.predict(X), index=y.index)
except: ### 用except: 如果try后面有误(没有训练),那么执行 except后面的语句
model.fit(X, y) ### model.fit(X, y) 固定格式:因为有监督问题--模型训练的过程中一定是:既有样本,又有标签。
y_pred = pd.Series(model.predict(X), index=y.index) ### 模型预测中,model.predict() 是对样本的预测,不能写标签, predict() 方法都是写样本,有的会包含一些参数
resid = y - y_pred
mean_resid = resid.mean()
std_resid = resid.std()
z = (resid - mean_resid)/std_resid ### 这里是基于分步的异常值检测方法中的 Grubbs检验
outliers = z[abs(z) > sigma].index ### 这一步是异常值的检测方法。自行搜索异常值的检测方法
### 模型性能的评估
print("R2= ", model.score(X, y)) ### 模型的得分 model.score() Return the mean accuracy on the given test data and labels. X 为测试样本, Y为真实标签
print("mse= ", mean_squared_error(y, y_pred))
# 这里的mse可以换成: sk.metrics.mean_squared_error(y, y_pred)
# from sklearn.metrics import mean_squared_error --> mean_squared_error(y, y_pred)
# tf.keras.metrics.mean_squared_error(y, y_pred)
print("-"*30)
### 残差的统计量
print("mean of residuals: ", mean_resid)
print("std of residuals: ", std_resid)
print("-"*30)
### 残差的个数以及残差的值
print(len(outliers), "outliers: ")
print(outliers.tolist()) ### 将所有的值进行 tolist(),转换成 列表形式
### 可视化操作
###
plt.figure(figsize=(15, 5))
ax_131 = plt.subplot(1, 3, 1)
plt.plot(y, y_pred)
plt.plot(y.loc[outliers], y_pred.loc[outliers], "ro")
plt.legend(["Accepted", "Outliers"])
plt.xlabel("y")
plt.ylabel("y_pred")
###
ax_132 = plt.subplot(1, 3, 2)
plt.plot(y, y_pred, ".")
plt.plot(y.loc[outliers], y_pred.loc[outliers] - y_pred.loc[outliers], "ro")
plt.legend(["Accepted", "Outliers"])
plt.xlabel("y")
plt.ylabel("y_pred")
###
ax_133 = plt.subplot(1, 3, 3)
z.plot.hist(bins=50, ax=ax_133)
z.loc[outliers].plot.hist(color="r", bins=50, ax=ax_133)
plt.legend(["Accepted", "Outliers"])
plt.xlabel("z")
plt.savefig("outliers.png")
return outliers
# 通过岭回归模型找出异常值
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
X_train = df_txt.iloc[:, 0:-1]
y_train = df_txt.iloc[:, -1]
outlier = find_outlier(Ridge(), X_train, y_train)
R2= 0.003460438433444235
mse= 0.0009508699922869707
------------------------------
mean of residuals: -9.020562075079397e-19
std of residuals: 0.030851609586876844
------------------------------
2 outliers:
[4, 137]
到这里我发现了这个方法的一些问题
我们在 find_outlier() 中使用了:
model.fit(X, y)
model.predict(X)
model.score(X, y)
mean_square_error(y, y_pred)
pd.Series(model.predict(X), index=y.index)
写完之后,我才察觉到这个方法存在极大的问题,因为我们没有标签,我们没有 y , 我们无法使用 上面的 方法。
因此在没有标签值的情况下,我们不能使用基于模型的异常值检测方法
不知道自己的理解是否正确(这里先存疑),原因:
无监督学习中也是没有标签的,那无监督学习就不能进行异常值检测了吗(显然是可以的)。因为无监督学习也有自己的检测方法,那有没有基于模型的检测方法呢?这一点我相信是有的
下面我们对 基于模型检测异常值的方法进行知识点梳理:
- model.fit(X, y)
这条语句(训练模型)是直接书写的,因为语句是为了寻找:拟合模型,找到那个函数(拟合函数)
- y_pred = model.predict(X)
model.predict(X) 这是用已经找到(已经完成训练的)的拟合模型进行预测操作,并且将这种预测值(y_pred)放到规定的格式里:
y_pred = pd.Series(model.predict(X), index=y.index)
- model.score(X, y)
其中 self代表本体(对象), X, y 为数据集,score() 是这个对象的属性(方法)
这里的X是训练样本, y是X是真实标签;
评估的得分的标准是用了一些 metric 中的函数,用 y_true = y 和 y_predict = model.predict(X) 进行衡量
举例说明:这里仅仅其中一个模型( ClassifierMixin() )的评估方式,模型不同,评估方法和评估指标也是不同的
评估衡量( accuracy_score )的标准 :
返回值是: return accuracy_score(y, self.predict(X), sample_weight=sample_weight)
我们进一步深挖:
因此进一步确定了 基于模型的异常值检测方法无法使用到本例子中,
在有监督学习任务中,基于模型的异常检测方法可以使用
而这个任务是检测一群数据中偏离严重的值,没有标签,
以后在处理问题过程中,要先判断:
-
这个任务是什么任务?
-
有没有标签?如果有,那哪个特征代表标签。
-
对个标签特征影响程度较高的又是哪个?这才需要对大量数据进行筛选和处理
这些都是这一次的感悟,因此要进行回溯,回到最初(第0步)