机器学习如何处理分类变量 [附python code]

适合阅读人群:想初步了解分类变量的处理。

什么叫categorical data分类变量?

分类变量(categorical variable)是说明事物类别的一个名称。 比如“性别”就是一个分类变量,他的值为“男”或“女”;“品牌”也是一个分类变量,值可以是“LV”、”爱马仕”、“Gucci”等; 衣服尺寸也是分类变量:比如,XS、S、M、L。

那这种在建模型的时候,要怎么处理那?

我们先看看,变量有几种类型,然后我们再看看怎么处理分类变量的类型。

image

生活中的变量:

我们在日常生活中,我们通常归类成2大类变量。

一个是数值类的,比如年龄,身高,交过女朋友数量,通常由阿拉伯数字组成。

第二种分类变量,比如性别,爱好,喜欢的明星,通常是语言文字表示。

统计中变量

但是在统计中,我们却严谨的多。

在统计中,我们分成4大类:

Nominal;Ordinal;Interval;Ratio

来一张图总结一下:

image

这4种类型和我们生活中理解的类型有什么区别吗?

当然有。

(1) Norminal Data 定类变量:变量的不同取值仅仅代表了不同类的事物,这样的变量叫定类变量。问卷的人口特征中最常使用的问题,而调查被访对象的“性别”,就是 定类变量。对于定类变量,加减乘除等运算是没有实际意义的。

(2) Ordinal Data定序变量:变量的值不仅能够代表事物的分类,还能代表事物按某种特性的排序,这样的变量叫定序变量。问卷的人口特征中最常使用的问题“教育程度“,以及态度量表题目等都是定序变量,定序变量的值之间可以比较大小,或者有强弱顺序,但两个值的差一般没有什么实际意义。

(3)Interval Data 定距变量:变量的值之间可以比较大小,两个值的差有实际意义,这样的变量叫定距变量。有时问卷在调查被访者的“年龄”和“每月平均收入”,都是定距变量。

(4) Ratio Data 定比变量, 有绝对0点,如质量,高度。定比变量与定距变量在市场调查中一般不加以区分,它们的差别在于,定距变量取值为“0”时,不表示“没有”,仅仅是取值为0。定比变量取值为“0”时,则表示“没有”。

这4种类型在machine learning 中,需要区分吗?

当然需要。

由于我们这篇讲categorical data 的处理,那我们讲一下nominal 和 ordinal的区别和实际python code。

一张总结一下:

image

一、如果我们想处理这类变量,达到下面的效果:

image

1)用pandas 自带的map:

Sex

map

import pandas as pd
df = pd.read_csv('data/titanic.csv')
df = df.dropna(how = 'any').reset_index(drop = True)
df['Sex_map'] = df['Sex'].map({'male':1,'female':0})
df.head()

image

2)用Pandas 自带的astype(注:仅适用于该变量只有2个unique value)

Sex

astype int

import pandas as pd
df = pd.read_csv('data/titanic.csv')
df = df.dropna(how = 'any').reset_index(drop = True)
df['Sex_astype'] = pd.Series(df['Sex'] == 'male').astype(int)
df.head()

image

3)用Sklearn的LabelEncoder

  • train dataset

Sex

LabelEncoder

Train dataset

import pandas as pd
df = pd.read_csv('data/titanic.csv')
df = df.dropna(how = 'any').reset_index(drop = True)

from sklearn.preprocessing import LabelEncoder
gle = LabelEncoder()
sex_labels = gle.fit_transform(df['Sex'])
df['Sex_labelencoder'] = sex_labels
df.head()

image

-test dataset

Sex

LabelEncoder

Test dataset

import pandas as pd
df_test = pd.read_csv('data/titanic_test.csv')
df_test = df_test.dropna(how = 'any').reset_index(drop = True)

from sklearn.preprocessing import LabelEncoder

sex_labels = gle.transform(df_test['Sex'])
df_test['Sex_labelencoder'] = sex_labels
df_test.head()

image

二、如果我们想处理这类变量,达到下面的效果:

image

1)用pandas 自带的 pd.get.dummies()

Sex

get dummies

import pandas as pd
df = pd.read_csv('data/titanic.csv')
df = df.dropna(how = 'any').reset_index(drop = True)

import pandas as pd
df = pd.read_csv('data/titanic.csv')
df = df.dropna(how = 'any').reset_index(drop = True)
pd.concat([df,pd.get_dummies(df['Sex'])],axis = 1).head()

image

注意:这里可以pd.get_dummie(drop= True) 将其中一列移除。

2)用Sklearn自带的 OneHotEncoder()

-Train dataset

Sex

get OneHotEncoder

Train dataset

import pandas as pd
df = pd.read_csv('data/titanic.csv')
df = df.dropna(how = 'any').reset_index(drop = True)

from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder()
sex_ohe = ohe.fit_transform(df[['Sex']]).toarray()
sex_ohe_df = pd.DataFrame(sex_ohe, columns= ohe.get_feature_names())
pd.concat([df,sex_ohe_df],axis = 1).head()

image

-Test dataset

Sex

get OneHotEncoder

Test dataset

import pandas as pd
df_test = pd.read_csv('data/titanic_test.csv')
df_test = df.dropna(how = 'any').reset_index(drop = True)

from sklearn.preprocessing import OneHotEncoder

sex_ohe = ohe.transform(df_test[['Sex']]).toarray()
sex_ohe_df = pd.DataFrame(sex_ohe, columns= ohe.get_feature_names())
pd.concat([df_test,sex_ohe_df],axis = 1)

image

三、下面看看如果变量值比较多的情况:

image

这种情况,用pandas 的map和astype 显然不合适。

因为写map的时候,比如把所有情况,手动输入{上海:1, 北京:2,深圳:3....}

1)这时候比较可行的是 Sklearn 的LabelEncoder

-Train dataset

Cabin

LabelEncoder

Train dataset

import pandas as pd
df = pd.read_csv('data/titanic.csv')
df = df.dropna(how = 'any').reset_index(drop = True)

from sklearn.preprocessing import LabelEncoder
gle = LabelEncoder()
cabin_labels = gle.fit_transform(df['Cabin'])
df['Cabin_labelencoder'] = cabin_labels
df.head()

image

-Test dataset

Cabin

LabelEncoder

Test

import pandas as pd
df_test = pd.read_csv('data/titanic_test.csv')
df_test = df_test.dropna(how = 'any').reset_index(drop = True)

from sklearn.preprocessing import LabelEncoder

cabin_labels = gle.transform(df_test['Cabin'])
df_test['Cabin_labelencoder'] = cabin_labels
df_test.head()

image

四、如果想这样变化:

image

1)pandas 自带的pd.get_dummies()

Train dataset

Cabin

get_dummies

Train

import pandas as pd
df = pd.read_csv('data/titanic.csv')
df = df.dropna(how = 'any').reset_index(drop = True)

pd.concat([df,pd.get_dummies(df['Cabin'])],axis = 1).head()

image
  • Test dataset

Cabin

get_dummies

Test

import pandas as pd
df_test = pd.read_csv('data/titanic_test.csv')
df_test = df_test.dropna(how = 'any').reset_index(drop = True)

pd.concat([df_test,pd.get_dummies(df_test['Cabin'])],axis = 1).head()

image

我们比较发现,适用dummy的时候,test dataset 并不会把所有的train dataset值都变成column的title,dummy只会显示test dataset里面的值。所以在build model时候,并不适用dummy来处理。

2)Pandas自带的OnehotEncoder

  • Train dataset

Cabin

Onehot

Train

import pandas as pd
df = pd.read_csv('data/titanic.csv')
df = df.dropna(how = 'any').reset_index(drop = True)

from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder()
cabin_ohe = ohe.fit_transform(df[['Cabin']]).toarray()
cabin_ohe_df = pd.DataFrame(cabin_ohe, columns= ohe.get_feature_names())
pd.concat([df,cabin_ohe_df],axis = 1).head()

image
  • Test Dataset

Cabin

Onehot

Test

import pandas as pd
df_test = pd.read_csv('data/titanic_test.csv')
df_test = df_test.dropna(how = 'any').reset_index(drop = True)

from sklearn.preprocessing import OneHotEncoder

ohe = OneHotEncoder()

cabin_ohe = ohe.transform(df_test[['Cabin']]).toarray()
cabin_ohe_df = pd.DataFrame(cabin_ohe, columns= ohe.get_feature_names())
pd.concat([df_test,cabin_ohe_df],axis = 1).head()

image

我们发现OnehotEncoder 能很好的解决dummy不能达到的要求。

所以,

通常情况下,我们会用OneHot 来处理Nominal 类型, LabelEncoder 来处理Ordinal类型

image

五、一些特殊处理:

我们想想,如果我有300个城市,如果都用Onehot做,是不是变量太多了?

那怎么解决?

我们可以用bin counting

bin_counting

game = pd.read_csv('data/vgsales.csv')
game[['Name', 'Genre']].head()

image

game.Genre.unique()

image

train

from sklearn.feature_extraction import FeatureHasher

fh = FeatureHasher(n_features=6, input_type='string')
hashed_features = fh.fit_transform(game['Genre'])
hashed_features = hashed_features.toarray()
pd.concat([game[['Name', 'Genre']], pd.DataFrame(hashed_features)], axis=1).head()

image

test

game_test = pd.read_csv('data/vgsales_test.csv')

from sklearn.feature_extraction import FeatureHasher

fh = FeatureHasher(n_features=6, input_type='string')

hashed_features = fh.transform(game_test['Genre'])
hashed_features = hashed_features.toarray()
pd.concat([game_test[['Name', 'Genre']], pd.DataFrame(hashed_features)], axis=1).head()

image

我们用5个变量代表了原来要用onehot代表的12个变量。

六、我们再想想,如果我有300个城市,还有没有其他的方法可以来描述这300个城市,让他们显得更有意义。

比如,我想预测某个城市的房价,我有几房几厅,是不是在市中心,交通是不是方便,哪个城市;

如果我把城市换成其他更有意义的变量是不是更好?比如:

Target encoding

pip install category_encoders

import pandas as pd
import category_encoders as ce

data = pd.DataFrame({'City' : ['Shanghai', 'Beijing', 'Shanghai', 'ShengZheng', 'Shengzheng', 'Beijing', "Beijing"],
'Income' : [70000, 64000, 72000, 59000, 57000, 67000, 62000]})
data

image

train

encoder = ce.target_encoder.TargetEncoder(cols=['City'])
encoder.fit(data['City'], data['Income'])
data['City_encoder'] = encoder.transform(data['City'])
data

image

test

data_test = pd.DataFrame({'City' : ['Shanghai', 'Beijing', 'Shanghai', 'Shengzheng']})
data_test['City_encoder'] = encoder.transform(data_test['City'])
data_test

image

完。

你可能感兴趣的:(机器学习如何处理分类变量 [附python code])