机器学习:使用One-Hot-Encoding和Python来分析收入数据
在这篇文章中,我将说明如何使用逻辑回归,结合“one-hot-encoding”技术来揭示某些有趣的事实与成人收入UCI数据集。可以从这里下载此数据集(https://www.kaggle.com/flyingwombat/logistic-regression-with-uci-adult-income),以及数据描述和一些基本分析。
我的Jupyter笔记本可以在这里找到,分析建立在Valentin Mihov的一篇文章之上。我再实现one-hot-encoding技术作进一步的数据解释。
第一步是将csv数据加载到Pandas DataFrame中:
income_df = pd.read_csv("data/income_data.csv")
print list(income_df)
print income_df.shape
Output:['age', 'workclass', 'fnlwgt', 'education', 'educational-num', 'marital-status', 'occupation', 'relationship', 'race', 'gender', 'capital-gain', 'capital-loss', 'hours-per-week', 'native-country', 'income']
(48842, 15)
特性名称很容易解释(唷!)人生苦短,读不懂数据描述,不是吗?
我们可以立即看到的另一件事是,这个dataframe包含15个特性和48842行(数据点)。
一个小技巧是使用“incom_df.sample()”快速浏览原始数据。与“head()”或“tail(tail)()”相比,sample()函数在csv文件中选择并打印serveral随机行:
一个快速发现是这个数据集包含数字(例如“年龄”)和分类数据(例如“教育”)。我写了一个小函数来更好地概述数据:
# get some basic ideas about the data
def summerize_data(df):
for column in df.columns:
print column
if df.dtypes[column] == np.object: # Categorical data
print df[column].value_counts()
else:
print df[column].describe()
print ''
summerize_data(income_df)
对于数字特征,此函数打印整列中数据的平均值和标准偏差。对于分类特征,它只是对每个子类别中的样本数进行计数。例如:
让我们跳过冗长的数据清理、拆分和扩展过程,并查看我们的逻辑回归模型的输出。与线性回归相比,逻辑回归是一种二元分类器算法。例如,在目前的情况下,它可以根据年龄、职业、婚姻状况等因素预测一个人的年收入是否在50K以上,但不能预测一个连续的数字,比如75636美元。
关于如何将分类变量转换为机器能够更好地理解的东西,例如编码,有一些深入的讨论。最简单的方法是为每个子类别分配一个不同的整数。例如,在我们的“workclass”类别中,我们可以将“1”分配给“Private”,“2”分配给“sel- emp-non -inc”,“3”分配给“Local-gov”等等。sciki - learning为我们提供了一个叫preprocessing.LabelEncoder的工具。然后我们可以使用编码数据作为训练和测试数据的一部分。
cls = linear_model.LogisticRegression()
cls.fit(X_train, y_train)
y_pred = cls.predict(X_test)
print accuracy_score(y_test, y_pred)
Output:0.7992793883
精度在0.80左右,每次你可能会得到一个稍微不同的数字因为这是一个随机过程。如果你的目标是你的目标,那么你可以做很多事情来改善你的预测。但是我们关注的是,使用coeff_属性、逻辑回归函数中特征的系数,对预测因子的重要性进行排序。
在绘制coeff_属性后,得到如下图和表:
UCI成人收入数据集中的特征,按逻辑回归系数排列
注意,此时“educational-num”列已经从dataframe中删除,因为它包含与“education”基本相同的信息。这个排名有些用处。它告诉我们,在预测一个人的收入时,资本收益、年龄、工作时间、社会地位等因素比其他因素如职业、种族和国籍更重要,如果我们相信抽样是无偏见的,并且我们的模型是正确的。然而,它并没有告诉我们这些特征在预测中的作用。而相对较低的职业排名似乎违反直觉。此外,婚姻关系和婚姻状况对收入水平的“负面”影响是什么意思?
另一个问题是将分类特征编码到序数变量的方法本身是有缺陷的。当特性具有二进制性质时,例如性别(在简单的场景中),它可以正常工作。但是,当特性有两个以上的子类时,机器将会在不相关的值之间建立一些任意的连接。例如,如果我们将“1”分配给“Private”,“2”分配给“Sel-emp-not-inc”,“3”分配给“Local-gov”,机器会认为“Private”更类似于“Sel-emp-not-inc”,而不是“Local-gov”,因为“1”比“3”更接近“2”。但这不是我们的意图。
我们可以使用one-hot-encoding来解决这个问题——转换特性[0,0,1],功能B(0,1,0)和特性C[0,0,1]。建议分别处理数值特性和类别特性,并在稍后将它们连接起来。
numeric_subset = income_df.select_dtypes('number')
categorical_subset = income_df.select_dtypes('object')
# One hot encode
categorical_subset = pd.get_dummies(categorical_subset[categorical_subset.columns.drop("income")])
转换之后,新的数据框将被展开,每个子类别都将成为一个新列
使用One-Hot-Endoding,“workclass”特性现在扩展到一个具有许多“0”和“1”
使用新的编码数据,我们可以重新生成我们的训练和测试集,并重新拟合我们的逻辑模型。准确度在一定程度上提高了。更重要的是,来自新编码数据的系数告诉我们关于数据的更完整的说明:
标题中承诺的单身人士的坏消息是:你处在收入阶梯的最底层!如果你想发财,获得一些资本,请结婚:d
这似乎很有趣,但我们可能想进一步问,年龄和工作时间如何影响收入?为了回答这个问题,我写了一个函数来把样本人口分成不同的年龄组:
# Further group numerical predictors such as "age" or "hours-per-week" to gain more insight!
# Group the "age" column
age_group = []
for age in income_df["age"]:
if age < 25:
age_group.append("<25")
elif 25 <= age <= 34:
age_group.append("25-34")
elif 34 < age <= 44:
age_group.append("35-44")
elif 44 < age <= 54:
age_group.append("45-54")
elif 54 < age <= 65:
age_group.append("55-64")
else:
age_group.append("65 and over")
new_income_df = income_df.copy()
new_income_df["age_group"] = age_group
del new_income_df["age"]
使用新的dataframe,我们现在可以得到更详细的数据分析输出:
嗯,现在看来你得每周工作48到60个小时,对于单身人士来说,也许一到二十年才会变得富有(对不起!)不过你仍然有足够的空间来改善你的收入;)