Kaggle旧金山犯罪分类预测----非数值型特征问题

Kaggle是一个数据分析建模的应用竞赛平台,有点类似KDD-CUP(国际知识发现和数据挖掘竞赛),企业或者研究者可以将问题背景、数据、期望指标等发布到Kaggle上,以竞赛的形式向广大的数据科学家征集解决方案。而热爱数据挖掘的小伙伴们可以下载/分析数据,使用统计/机器学习/数据挖掘等知识,建立算法模型,得出结果并提交,排名top的可能会有奖金.
应用机器学习,千万不要一上来就试图做到完美,先撸一个baseline的model出来,再进行后续的分析步骤,一步步提高,所谓后续步骤可能包括『分析model现在的状态(欠/过拟合),分析我们使用的feature的作用大小,进行feature selection,以及我们模型下的bad case和产生的原因』等等。
1. 要对数据有自己的认识
2. 对数据中的特殊点/离群点进行分析和处理
3. 特征工程(feature engineering)很重要了!
4. 尝试模型融合(model ensemble)

  1. 问题介绍

在旧金山这个地方,一度犯罪率还挺高的,很多人都经历过大到暴力案件,小到东西被偷,车被划的事情。当地警方努力地去总结和想办法降低犯罪率,一个挑战是在给出犯罪的地点和时间的之后,要第一时间确定这可能是一个什么样的犯罪类型,以确定警力等等。犯罪报告里面包括日期,描述,星期几,所属警区,处理结果,地址,GPS定位等信息。

2.查看数据
数据可以在Kaggle比赛数据页面下载到.大家也可以在博主提供的百度网盘地址中下载到。
我们依旧用pandas载入数据,先看看数据内容。

import pandas as pd
import numpy as np

#用pandas载入csv训练数据,并解析第一列为日期格式
train=pd.read_csv('/Users/Hanxiaoyang/sf_crime_data/train.csv', parse_dates = ['Dates'])
test=pd.read_csv('/Users/Hanxiaoyang/sf_crime_data/test.csv', parse_dates = ['Dates'])
train

可以用train.info()和train.describe()获取行列信息

我们依次解释一下每一列的含义:
Date: 日期
Category: 犯罪类型,比如 Larceny/盗窃罪 等.
Descript: 对于犯罪更详细的描述
DayOfWeek: 星期几
PdDistrict: 所属警区
Resolution: 处理结果,比如说『逮捕』『逃了』
Address: 发生街区位置
X and Y: GPS坐标
train.csv中的数据时间跨度为12年,包含了90w+的记录。另外,这部分数据,大家从上图上也可以看出来,大部分都是『类别』型,比如犯罪类型,比如星期几。

  1. 特征预处理
    数据中类别和文本型非常多,我们要进行特征预处理,对于类别特征,我们用最常见的因子化操作将其转成数值型,比如我们把犯罪类型用因子化进行encode,也就是说生成如下的向量:
    星期一/Monday = 1,0,0,0,…
    星期二/Tuesday = 0,1,0,0,…
    星期三/Wednesday = 0,0,1,0,…

    用pandas的get_dummies()可以直接拿到这样的一个二值化的01向量。Pandas里面还有一个很有用的方法LabelEncoder可以用于对类别编号。对于已有的数据特征,我们打算做下面的粗略变换:
    1.LabelEncoder**对犯罪类型做编号**
    2.处理时间,在我看来,也许犯罪发生的时间点(小时)是非常重要的,因此我们会用Pandas把这部分数据抽出来;
    3.对街区,星期几,时间点用get_dummies()因子化;
    4.做一些组合特征,比如把上述三个feature拼在一起,再因子化一下;
    具体的数据和特征处理如下:
import pandas as pd
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn import preprocessing
#用LabelEncoder对不同的犯罪类型编号
leCrime = preprocessing.LabelEncoder()
crime = leCrime.fit_transform(train.Category)
#因子化星期几,街区,小时等特征
days = pd.get_dummies(train.DayOfWeek)
district = pd.get_dummies(train.PdDistrict)
hour = train.Dates.dt.hour
hour = pd.get_dummies(hour) 
#组合特征
trainData = pd.concat([hour, days, district], axis=1)
trainData['crime']=crime
#对于测试数据做同样的处理
days = pd.get_dummies(test.DayOfWeek)
district = pd.get_dummies(test.PdDistrict)
hour = test.Dates.dt.hour
hour = pd.get_dummies(hour) 
testData = pd.concat([hour, days, district], axis=1)
trainData

4.进行建模
这个多分类问题中,Kaggle的评定标准是multi-class log_loss,这个值越小,表示最后的效果越好。
-log P(yt|yp) = -(yt log(yp) + (1 - yt) log(1 - yp))

我们可以快速地筛出一部分重要的特征,搭建一个baseline系统,再考虑步步优化。比如我们这里简单一点,取小时时间点,星期几和街区作为分类器输入特征,我们用scikit-learn中的train_test_split函数拿到训练集和交叉验证集。代码如下:

import pandas as pd
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn import preprocessing
from sklearn.metrics import log_loss
from sklearn.naive_bayes import BernoulliNB
import time
#用pandas载入csv训练数据,并解析第一列为日期格式
train=pd.read_csv(r'C:\Users\lujinyu\Desktop\atae-lstm\atae-lstm\data\train.csv', parse_dates = ['Dates'])
test=pd.read_csv(r'C:\Users\lujinyu\Desktop\atae-lstm\atae-lstm\data\test.csv', parse_dates = ['Dates'])
#print(train.info(),train.describe())
#用LabelEncoder对不同的犯罪类型编号
leCrime = preprocessing.LabelEncoder()
crime = leCrime.fit_transform(train.Category)
#因子化星期几,街区,小时等特征
days = pd.get_dummies(train.DayOfWeek)
district = pd.get_dummies(train.PdDistrict)
hour = train.Dates.dt.hour
hour = pd.get_dummies(hour)
#组合特征
trainData = pd.concat([hour, days, district], axis=1)
trainData['crime']=crime
#对于测试数据做同样的处理
days = pd.get_dummies(test.DayOfWeek)
district = pd.get_dummies(test.PdDistrict)
hour = test.Dates.dt.hour
hour = pd.get_dummies(hour)
testData = pd.concat([hour, days, district], axis=1)
features = ['Friday', 'Monday', 'Saturday', 'Sunday', 'Thursday', 'Tuesday',
'Wednesday', 'BAYVIEW', 'CENTRAL', 'INGLESIDE', 'MISSION',
'NORTHERN', 'PARK', 'RICHMOND', 'SOUTHERN', 'TARAVAL', 'TENDERLOIN']
hourFea = [x for x in range(0,24)]
features = features + hourFea
# 分割训练集(3/5)和测试集(2/5)
training, validation = train_test_split(trainData, test_size=0.6)
'''
X_train,X_test, y_train, y_test =
cross_validation.train_test_split(train_data,train_target,test_size=0.4, random_state=0)
train_data:所要划分的样本特征集
train_target:所要划分的样本结果
test_size:样本占比,如果是整数的话就是样本的数量
random_state:是随机数的种子。
随机数种子:其实就是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的随机数。比如你每次都填1,其他参数一样的情况下你得到的随机数组是一样的。但填0或不填,每次都会不一样。
随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:
种子不同,产生不同的随机数;种子相同,即使实例不同也产生相同的随机数。
'''
# 朴素贝叶斯建模,计算log_loss
model = BernoulliNB()
'''ernoulliNB假设特征的先验概率为二元伯努利分布,即如下式:
P(Xj=xjl|Y=Ck)=P(j|Y=Ck)xjl+(1−P(j|Y=Ck)(1−xjl)
此时l只有两种取值。xjl只能取值0或者1。
BernoulliNB一共有4个参数,其中3个参数的名字和意义和MultinomialNB完全相同。
唯一增加的一个参数是binarize。这个参数主要是用来帮BernoulliNB处理二项分布的,
可以是数值或者不输入。如果不输入,则BernoulliNB认为每个数据特征都已经是二元的。
否则的话,小于binarize的会归为一类,大于binarize的会归为另外一类。
在使用BernoulliNB的fit或者partial_fit方法拟合数据后,我们可以进行预测。
此时预测有三种方法,包括predict,predict_log_proba和predict_proba。 
predict方法就是我们最常用的预测方法,直接给出测试集的预测类别输出。
predict_proba则不同,它会给出测试集样本在各个类别上预测的概率。
容易理解,predict_proba预测出的各个类别概率里的最大值对应的类别,也就是predict方法得到类别。
predict_log_proba和predict_proba类似,它会给出测试集样本在各个类别上预测的概率的一个对数转化。
转化后predict_log_proba预测出的各个类别对数概率里的最大值对应的类别,也就是predict方法得到类别。。'''
nbStart = time.time()
model.fit(training[features], training['crime'])
'''partial_fit说明:增量的训练一批样本 
这种方法被称为连续几次在不同的数据集,从而实现核心和在线学习,这是特别有用的,当数据集很大的时候,不适合在内存中运算 
该方法具有一定的性能和数值稳定性的开销,因此最好是作用在尽可能大的数据块(只要符合内存的预算开销) '''
nbCostTime = time.time() - nbStart#消耗的时间
predicted = np.array(model.predict_proba(validation[features]))
print("朴素贝叶斯建模耗时 %f 秒" %(nbCostTime))
print("朴素贝叶斯log损失为 %f" %(log_loss(validation['crime'], predicted)))

结果如下:

D:\Anaconda3\python.exe C:/Users/lujinyu/Desktop/atae-lstm/atae-lstm/TEST.py
朴素贝叶斯建模耗时 11.314548 秒
朴素贝叶斯log损失为 2.584893
Process finished with exit code 0

你可能感兴趣的:(Kaggle旧金山犯罪分类预测----非数值型特征问题)