Python大数据分析2:数据分析过程的完善(企业欺诈识别)

我们继续来完善上述分析过程。上次我们谈到的方法,其实是一种最为直观简化的方法,主要是方便大家理解如何进行利用机器学习方法来进行数据分类。但是,真正在应用中,我们还需要注意很多问题,我们仍然按照处理流程分别来看下:

首先看下数据预处理。上次我们已经说明了数据存在一定的错误,这个其实很正常,我们获取的数据不仅可能存在的一些诸如遗漏、误写等明显错误,而且还可能存在着一些与应用分析相关的错误,比如对于分类算法,通常还需要对非数值型数据进行数值型的转换。

比如上次企业审计数据存在部分非数值型数据,我们来看看如何使用Python来处理。

import numpy as np

import pandas as pd

frame = pd.read_csv(‘C:\\temp\\audit_risk.csv’)

results = frame.applymap(np.isreal)

print(results)

这里,我们使用了applymap函数,它可以将整个DataFrame所有元素都应用下参数中指定的函数。其中在判断是否为数值时,我们利用了numpy库中的isreal函数,这个需要我们仍然导入numpy库。applymap最终返回一个和原始DataFrame一样大小的新DataFrame,只是其中的每个数值都被处理了下。

由于DataFrame在转换列类型时,通常都是以列为单位的,也就是说,一旦有一列含有非数值元素,它就会将此列全部设置为非数值型,如字符型。所以我们能看到这里有一个列LOCATION_ID,就是这种情况。

显然,此时为了效果,可以只显示列名即可,0表示列,1表示行:

results = frame.applymap(np.isreal).all(0)

print(results)

此时all为0的设置就表示只以列显示结果,并且列中所有元素都满足,该列才命中。

我们已经知道了LOCATION_ID列存在非数值元素。

如果列很多,更有效的方法是只显示有问题的列,即isreal为假的列:

print(results[(results == False)])

接下来如何处理这些异常数据呢?这里介绍一种简单常见的方法:

frame[‘LOCATION_ID’] = pd.to_numeric(frame[‘LOCATION_ID’], errors=’coerce’)

results = frame.applymap(np.isreal).all(0)

print(results[(results == False)])

这里使用to_numeric将其强制转换为数值,同时对于异常数据,则使用空值来替换,这个是通过errors参数为强制来实现的。

从结果来看,现在已经全部都是数值了!

对于空值,也是常见的问题。

我们先查找下空值:

import numpy as np

import pandas as pd

frame = pd.read_csv(‘C:\\temp\\audit_risk.csv’)

frame[‘LOCATION_ID’] = pd.to_numeric(frame[‘LOCATION_ID’], errors=’coerce’)

results = frame.isnull()

print(results)

和刚才一样,也返回一个和原始DataFrame一样大小的新DataFrame,只是其中的每个元素都是真假表示是否为空值。

显然,需要再次查询下定位:

results = frame.isnull().any(0)

print(results[results == True])

这里any表示该列只要有任意一个为空则命中。从结果来看,存在两列,一列是我们替换非数值的LOCATION_ID,还有一个是新的Money_Value。

究竟是那几行呢?

results = frame.isnull().any(1)

print(results[results == True])

只要把any的0换为1,表示行,就能看到。此时凭借着行列就可以手工去修改下数据。

我们这里对于空值都采取了使用0来填充。

frame = frame.fillna(0)

results = frame.isnull().any(1)

print(results[results == True])

从结果来看,已经全部替换完毕。

这是完整代码:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import numpy as np

frame = pd.read_csv('C:\\temp\\audit_risk.csv')
frame['LOCATION_ID'] = pd.to_numeric(frame['LOCATION_ID'], errors='coerce')
frame = frame.fillna(0)
y = frame[frame.columns[len(frame.columns) - 1]]
frame.drop(frame.columns[len(frame.columns) - 1], axis=1, inplace=True)
X = frame
X_train, X_test, y_train, y_test = train_test_split(X, y)
estimator = KNeighborsClassifier()
estimator.fit(X_train, y_train)
y_predicted = estimator.predict(X_test)
print(np.mean(y_test == y_predicted))

当然,除了使用这种0值填充方法外,我们也可以使用更为灵活的填充方法,比如就拿空值所在列的平均值来填充,方法其实很简单:

from sklearn.impute import SimpleImputer

imp = SimpleImputer(strategy=’mean’)

X = imp.fit_transform(X)

只需将刚才填充X的语句换掉即可,这里看得出是使用mean平均值来填充。

当然效果可能会因数据、算法和填充策略而各不一样。这里就需要大家多比较。

除了这些数据问题外,还有一种常见的数据问题,称之为数据规范化问题。也就是说,不同列的数据呈现出不同的波动幅度和取值范围,因此在进行各种算法比较中,这些数据本身的数值差异会对算法产生一些不利影响。因此,我们常常还需要进行必要的规范化。

做法其实很简单:

from sklearn.preprocessing import StandardScaler

X = StandardScaler().fit_transform(X)

只需在模型数据划分前,利用合适的规范化工具处理下整体特征数据处理下即可。

这里结果变优了。当然,需要注意的是,盲目使用未必一定最优。

事实上,有时我们还需针对不同的场景和数据,灵活选择更多的规范化方法。

大家有没有注意到,即使对于同样的代码,每次运行效果都并不完全一样。

这里其实反映了一个重要的问题,那就是数据的选择。由于算法自动切分训练集和测试集,数据的划分不一样,结果就可能不一样,因此更为合理的方法是进行多次验证,这种也被称为交叉验证。比如我们将训练集和测试集交换:

estimator = KNeighborsClassifier()

estimator.fit(X_test, y_test)

y_predicted = estimator.predict(X_train)

print(np.mean(y_train == y_predicted))

效果要差些,因为默认的测试集数量少于训练集,训练效果不好。

当然无需我们自己来手动的分组来试,我们可以利用sklearn中的一些非常方便的方法。

from sklearn.model_selection import cross_val_score

estimator = KNeighborsClassifier()

scores = cross_val_score(estimator, X, y, scoring=’accuracy’)

print(np.mean(scores))

此时,默认分为5组,挨次把其中的一组作为测试集。代码反而更简单了,创建完分类器后,直接调用交叉验证方法,给出分类器模型、训练集、测试集和评价指标,直接就可以输出最终的结果。

事实上,这个结果没有这么高,大约为91%左右。

也可以设置交叉分组的次数:

scores = cross_val_score(estimator, X, y, scoring=’accuracy’, cv=10)

print(np.mean(scores))

这里最为极端的一个例子是自己验证自己,显然这并没有意义:

estimator = KNeighborsClassifier(n_neighbors=1)

estimator.fit(X_train, y_train)

y_predicted = estimator.predict(X_train)

print(np.mean(y_train == y_predicted))

这也说明训练集和测试集分开的必要性。

对于同样的数据,这些模型都是固定的算法,那么究竟有哪些影响结果的因素呢?刚才我们说的都是训练集和测试集的划分和选择问题。其实还有一种可能,即模型参数,不同的参数选择,效果也会不一样,因此称之为参数调优。这些参数也被称为超参数。

比如这里的分类算法而言,究竟邻居数量选择几个是大有学问的,我们大胆的试一试使用一个1个邻居参与运算,看看效果。

estimator = KNeighborsClassifier(n_neighbors=1)

scores = cross_val_score(estimator, X, y, scoring=’accuracy’)

print(np.mean(scores))

你会发现结果变高了一点点。

事实上,默认邻居数量为5。这里是将邻居从1到20全部设置的最终准确度展示结果。可以看出结果呈现出以1和10为最高点,其余数量都依次递减的特点。

这种调参的过程显然也比较麻烦,因此可以考虑使用一些更为简单有效的模型,比如高斯朴素贝叶斯分类,速度很快,而且不需要选择超参数,所以通常很适合作为初步分类手段。

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
import numpy as np
from sklearn.model_selection import cross_val_score

frame = pd.read_csv('C:\\temp\\audit_risk.csv')
frame['LOCATION_ID'] = pd.to_numeric(frame['LOCATION_ID'], errors='coerce')
frame = frame.fillna(0)
y = frame[frame.columns[len(frame.columns) - 1]]
frame.drop(frame.columns[len(frame.columns) - 1], axis=1, inplace=True)
X = frame
X_train, X_test, y_train, y_test = train_test_split(X, y)
estimator = GaussianNB()
scores = cross_val_score(estimator, X, y, scoring='accuracy')
print(np.mean(scores))

其中我们仅仅替换了模型,其他没有做任何改变,结果却获得了很好的提升,而且无需设置超参数。

因此,整个机器学习的分析方法可以总结下:

第一,获取数据;

第二,划分特征数据和预测数据;

第三,数据预处理,包括必要的数据替换、填充和规范化(一般不对预测数据做处理);

第四,划分训练集和测试集;

第五,选择合适的模型并训练模型;

第六,预测结果和评估。

有时候,这个过程虽然简单,但是写法需要一步一步,很繁琐,因此可以通过一种称之为管道的方式组装起来,更为方便:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

frame = pd.read_csv('C:\\temp\\audit_risk.csv')
frame['LOCATION_ID'] = pd.to_numeric(frame['LOCATION_ID'], errors='coerce')
frame = frame.fillna(0)
y = frame[frame.columns[len(frame.columns) - 1]]
frame.drop(frame.columns[len(frame.columns) - 1], axis=1, inplace=True)
X = frame
X_train, X_test, y_train, y_test = train_test_split(X, y)
pipeline = Pipeline([('scale', StandardScaler()),
                     ('predict', KNeighborsClassifier())])
scores = cross_val_score(pipeline, X, y, scoring='accuracy')
print(scores)

这里的pipeline封装了规范化方法、模型方法等,直接使用非常方便。

发表评论

电子邮件地址不会被公开。 必填项已用*标注