赞
踩
参考
- import pandas as pd
-
- # 读取 Excel 文件
- data = pd.read_excel("D:\\数据科学导论\\income-dataset.xlsx")
-
- # 显示读取的数据
- data
可以看到共有32516条数据,每条数据有15个特征值,其中5个特征值为数值型的,9个特征值为离散型的。
对获取到的数据进行预处理,包括去除重复值、缺失值处理、异常值处理等;
- #查看数据是否存在缺失
- data.apply(lambda x:np.sum(x.isnull()))
可以看到数据集中有3个变量存在数值缺失,分别是居民的工作类型(离散型)缺1836、职业(离散型)缺1843和国籍(离散型)缺583。
缺失值的存在一般都会影响分析或建模的结果,所以需要对缺失数值做相应的处理。
缺失值的处理一般采用三种方法:
pandas中fillna()方法,能够使用指定的方法填充NA/NaN值。
函数形式:
fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
参数:
value:用于填充的空值的值。(该处为字典)
method: {'backfill', 'bfill', 'pad', 'ffill', None}, default None。定义了填充空值的方法, pad/ffill表示用前面行/列的值,填充当前行/列的空值, backfill / bfill表示用后面行/列的值,填充当前行/列的空值。
axis:轴。0或'index',表示按行删除;1或'columns',表示按列删除。
inplace:是否原地替换。布尔值,默认为False。如果为True,则在原DataFrame上进行操作,返回值为None。
limit:int,default None。如果method被指定,对于连续的空值,这段连续区域,最多填充前 limit 个空值(如果存在多段连续区域,每段最多填充前 limit 个空值)。如果method未被指定, 在该axis下,最多填充前 limit 个空值(不论空值连续区间是否间断)
downcast:dict, default is None,字典中的项,为类型向下转换规则。或者为字符串“infer”,此时会在合适的等价类型之间进行向下转换,比如float64 to int64 if possible。
在这里我们使用:
- # 缺失值处理,采用众数替换法(mode()方法取众数)
- data.fillna(value={'workclass':data['workclass'].mode()[0],
- 'ouccupation':data['occupation'].mode()[0],
- 'native-country':data['native-country'].mode()[0]},
- inplace = True)
通过
.mode()[0]
方法找到了每列中的众数(出现频率最高的值)作为填充值。
处理后:
可以看到数据集中已经不存在缺失值。
- # 检查重复值
- duplicate_rows = data[data.duplicated()] # 选取所有重复行
- print("重复行数:", duplicate_rows.shape[0])
在这里,我其实一开始不太懂重复行的这个数量是什么意思,,
输出的重复行数量 24 意味着在数据集中有 24 行是与其他行完全相同的重复行。这个数值表示了所有完全重复的行的总数,这些行在数据集中出现了至少一次以上,并且与其他行的值完全相同。
这么一解释的话,可以看出来,这重复的24行是完全可以直接删除的 ,所以可以看到下面删除后的数据数量由32561->32537,删除了24行
存在重复值,进行重复值的处理
- # 处理重复值
- data.drop_duplicates(inplace=True) # 删除重复行
通过统计分析和可视化手段对数据进行初步的分析,了解数据的分布、关系等;
数值型变量
- #数值型变量统计描述
- data.describe()
上面的结果描述了有关数值型变量的简单统计值,包括非缺失观测的个数(count)、平均值(mean)、标准差(std)、最小值(min)、下四分位数(25%)、中位数(50%)、上四分位数(75%)和最大值(max)。
标准差
标准差(Standard Deviation)是用来衡量数据集中数值偏离平均值的程度或数据的离散程度的一种统计量。它可以告诉我们数据点相对于平均值的分散程度,即数据点围绕平均值的散布程度。
标准差的计算步骤如下:
- 计算数据集中每个数据点与平均值的差值。
- 对这些差值进行平方。
- 求取这些平方差的平均值。
- 将结果开方,得到标准差。
标准差越大,表示数据点相对于平均值的分散程度越大;标准差越小,表示数据点相对于平均值的分散程度越小。标准差可以帮助我们理解数据的稳定性和可靠性,以及对数据分布形状的一些了解。
25%分位数(25%):数据的第一个四分位数,将数据分为四等份,位置在所有数据从小到大排列后的25%处。
中位数(50%):数据的中位数,即数据的中间值。
75%分位数(75%):数据的第三个四分位数,将数据分为四等份,位置在所有数据从小到大排列后的75%处。
- #离散型变量统计描述
- data.describe(include= ['object'])
上面为离散变量的统计值,包含每个变量非缺失观测的数量(count)、不同离散值的个数(unique)、出现频次最高的离散值(top)和最高频次数(freq)。以受教育水平变量为例,一共有16种不同的教育水平;3万多居民中,高中毕业的学历是出现最多的;并且一共有10494名。
为了了解数据的分布形状(如偏度、峰度等)可以通过可视化的方法进行展现
核密度图核密度图(kernel density plot)原理详解和代码实现 - 知乎 (zhihu.com)
核密度图本质上是根据有限的数据样本,运用核密度函数,对整体数据的密度进行估计;即已知有限的数据样本和一个核函数,输出整体数据的概率密度,并通过图形展示出结果
尝试画不同收入水平下的资本收益/资本损失 核密度图,发现核密度图呈一条直线,通过查看数据集,发现是由于资本收益和资本损失这两特征值的大部分数据都是0,不0为的数据占不到10%, 所以下面就不进行 不同收入水平下的资本收益/资本损失 核密度图 的绘制
- import matplotlib.pyplot as plt
-
- # 设置绘图风格
- plt.style.use('ggplot')
-
- # 设置多图形的组合
- fig, axes = plt.subplots(3, 1, figsize=(8, 12))
-
- # 绘制不同收入水平下的年龄核密度图
- data.age[data.income == '<=50K'].plot(kind='kde', label='<=50K', ax=axes[0], legend=True, linestyle='-')
- data.age[data.income == '>50K'].plot(kind='kde', label='>50K', ax=axes[0], legend=True, linestyle='--')
- axes[0].set_title('Age Distribution')
-
- # 绘制不同收入水平下的周工作小时核密度图
- data['hours-per-week'][data.income == '<=50K'].plot(kind='kde', label='<=50K', ax=axes[1], legend=True, linestyle='-')
- data['hours-per-week'][data.income == '>50K'].plot(kind='kde', label='>50K', ax=axes[1], legend=True, linestyle='--')
- axes[1].set_title('Hours-per-week Distribution')
-
- # 绘制不同收入水平下的受教育时长核密度图
- data['education-num'][data.income == '<=50K'].plot(kind='kde', label='<=50K', ax=axes[2], legend=True, linestyle='-')
- data['education-num'][data.income == '>50K'].plot(kind='kde', label='>50K', ax=axes[2], legend=True, linestyle='--')
- axes[2].set_title('Education-num Distribution')
-
- plt.tight_layout()
- plt.show()
第一幅图展现的是,在不同收入水平下,年龄的核密度分布图,对于年收入超过5万美元的居民来说,他们的年龄几乎呈现正态分布,而收入低于5万美元的居民,年龄呈现右偏特征,即年龄偏大的居民人数要比年龄偏小的人数多。
第二幅图展现了不同收入水平下,周工作小时数的核密度图,很明显,两者的分布趋势非常相似,并且出现局部峰值。
第三幅图展现了不同收入水平下,教育时长的核密度图,很明显,两者的分布趋势非常相似,并且也多次出现局部峰值。
针对离散型变量,对比居民的收入水平高低在性别、种族状态、家庭关系等方面的差异,进而可以发现这些离散变量是否影响收入水平:
通过上面3.1 对离散变量的统计,我们可以看到workclass、education、marital-status、occupation这四个变量的类型较多,画成直方图也不太合适观察分析 ,对数据分析意义不大
- import seaborn as sns
- # 构造不同收入水平下各种族人数的数据
- race = pd.DataFrame(data.groupby(by = ['race','income']).aggregate(np.size).loc[:,'age'])
- #print(race)
-
- # 重设行索引
- race = race.reset_index()
- #print(race)
-
- # 变量重命名
- race.rename(columns={'age':'counts'}, inplace=True)
- #print(race)
-
- # 排序
- race.sort_values(by = ['race','counts'], ascending=False, inplace=True)
- #print(race)
-
- # 构造不同收入水平下各家庭关系人数的数据
- relationship = pd.DataFrame(data.groupby(by = ['relationship','income']).aggregate(np.size).loc[:,'age'])
- relationship = relationship.reset_index()
- relationship.rename(columns={'age':'counts'}, inplace=True)
- relationship.sort_values(by = ['relationship','counts'], ascending=False, inplace=True)
-
- # 构造不同收入水平下各男女人数的数据
- sex = pd.DataFrame(data.groupby(by = ['sex','income']).aggregate(np.size).loc[:,'age'])
- sex = sex.reset_index()
- sex.rename(columns={'age':'counts'}, inplace=True)
- sex.sort_values(by = ['sex','counts'], ascending=False, inplace=True)
-
- # 设置图框比例,并绘图
- plt.figure(figsize=(9,5))
- sns.barplot(x="race", y="counts", hue = 'income', data=race)
- plt.show()
-
- plt.figure(figsize=(9,5))
- sns.barplot(x="relationship", y="counts", hue = 'income', data=relationship)
- plt.show()
-
- plt.figure(figsize=(9,5))
- sns.barplot(x="sex", y="counts", hue = 'income', data=sex)
- plt.show()
-
图一、反映的是相同的种族下,居民年收入水平高低的人数差异;
图二、反映的是相同的家庭成员关系下,居民年收入水平高低的人数差异。但无论怎么比较,都发现一个规律,即在某一个相同的水平下(如白种人或未结婚人群中),年收入低于5万美元的人数都要比年收入高于5万美元的人数多,这个应该是抽样导致的差异(数据集中年收入低于5万和高于5万的居民比例大致在75%:25%)。
图三、反映的是相同的性别下,居民收入水平高低人数的差异;其中,女性收入低于5万美元的人数比高于5万美元人数的差异比男性更严重,比例大致为90%:10%, 男性大致为70%:30%。
4.1 特征提取
由于数据集中有很多离散型变量,这些变量的值为字符串,不利于建模,因此,需要先对这些变量进行重新编码。编码的方法有很多种:
One-hot编码
是一种数据处理技术,常用于机器学习中处理分类数据。它将分类数据转换成数值格式。这种方法通常用于处理那些没有内在数值顺序或关联的分类变量。
一热编码的工作原理如下:
分类变量:假设你有一个分类变量,比如水果的类型:苹果、橙子、香蕉。
整数标记:首先,对每个分类分配一个唯一的整数标记。例如:
- 苹果:0
- 橙子:1
- 香蕉:2
一热编码:然后,针对每个分类创建一个二进制向量(通常是二进制格式),其中每个向量的长度等于唯一分类的数量。在这个例子中,有3个分类(苹果、橙子、香蕉)。
- 苹果:[1, 0, 0](因为它是第一个分类)
- 橙子:[0, 1, 0](因为它是第二个分类)
- 香蕉:[0, 0, 1](因为它是第三个分类)
每个分类都得到一个长度等于分类数量的向量,其中对应其标记的位置被标记为1,其他位置则为0。
在这里我们采用“字符转数值”的方法对离散型变量进行重编码
- # 离散型变量的重编码
- for feature in income.columns:
- if income[feature].dtype == 'object':
- income[feature] = pd.Categorical(income[feature]).codes
- income.head(10)
-
对字符型离散变量的重编码效果,所有的字符型变量都变成了整数型变量。
在原本的居民收入数据集中,关于受教育程度的有两个变量,一个是education(教育水平),另一个是education-num(受教育时长),而且这两个变量的值都是一一对应的,只不过一个是字符型,另一个是对应的数值型,如果将这两个变量都包含在模型中的话,就会产生信息的冗余;fnlwgt变量代表的是一种序号,其对收入水平的高低并没有实际意义。故为了避免冗余信息和无意义变量对模型的影响,考虑将education变量和fnlwgt变量从数据集中删除。
- #去除对实验没有作用的数据以及特征重复的
- data.drop(['education','fnlwgt'], axis=1, inplace=True)
- data.head(10)
由于所给的数据集只有一个,所以在进行数据的建模分析的时候,我们需要对数据集进行拆分
- from sklearn.model_selection import train_test_split
-
- # 拆分数据
- X_train, X_test, y_train, y_test = train_test_split(data.loc[:,'age':'native-country'],
- data['income'],train_size = 0.75,test_size=0.25, random_state = 1234)
- # print(X_train)
- # print(y_train)
- print("训练数据集中共有 %d 条观测" %X_train.shape[0])
- print("测试数据集中共有 %d 条测试" %X_test.shape[0])
关于分类模型有很多种:
这里主要采用K邻近、朴素贝叶斯和多层感知机进行预测和模型的评估比较
- from sklearn.datasets import load_breast_cancer
- from sklearn.model_selection import train_test_split
- from sklearn.neighbors import KNeighborsClassifier
- from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score, roc_curve
- import matplotlib.pyplot as plt
-
-
- KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
- metric_params=None, n_jobs=1, n_neighbors=6, p=2,
- weights='uniform')
- # 构件K近邻模型
- kn = KNeighborsClassifier()
- kn.fit(X_train, y_train)
- kn_pred = kn.predict(X_test)
-
- # 计算并打印混淆矩阵
- conf_matrix = confusion_matrix(y_test, kn_pred)
- print("混淆矩阵:")
- print(conf_matrix)
-
-
- # 获取分类报告
- report = classification_report(y_test, kn_pred, output_dict=True)
- report_df = pd.DataFrame(report).transpose() # 将分类报告转换为DataFrame格式
-
- # 打印表格形式的分类报告
- print("表格形式的分类报告:")
- report_df
模型的准确率就是基于混淆矩阵计算的,但是该方法存在一定的弊端,即如果数据本身存在一定的不平衡时(正负样本的比例差异较大),一定会导致准确率很高,但并不一定说明模型就是理想的。所以可以绘制ROC曲线,并计算曲线下的面积AUC值 (AUC值越大说明模型性能越好
- from sklearn import metrics
-
- # 计算ROC曲线的x轴 和 y轴数据
- fpr, tpr, _ = metrics.roc_curve(y_test, kn.predict_proba(X_test)[:,1])
- # 绘制ROC曲线
- plt.plot(fpr, tpr, linestyle = 'solid', color ='red')
- # 添加阴影
- plt.stackplot(fpr, tpr, color='steelblue')
- # 绘制参考线
- plt.plot([0,1],[0,1],linestyle='dashed', color='black')
- # 添加文本
- plt.text(0.6, 0.4, 'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict=dict(size =16))
- plt.show()
- from sklearn.datasets import make_classification
- from sklearn.model_selection import train_test_split
- from sklearn.naive_bayes import GaussianNB
- from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
-
-
- # 实例化朴素贝叶斯分类器
- GNB = GaussianNB(priors=[0.3, 0.7], var_smoothing=1e-9)
-
- # 使用训练集训练模型
- GNB.fit(X_train, y_train)
-
- # 使用测试集进行预测
- y_pred = GNB.predict(X_test)
-
- # 计算模型准确率
- accuracy = accuracy_score(y_test, y_pred)
- print(f"模型准确率: {accuracy:.2f}")
-
- # 打印混淆矩阵和分类报告
- print("混淆矩阵:")
- print(confusion_matrix(y_test, y_pred))
-
- print("分类报告:")
- print(classification_report(y_test, y_pred))
- from sklearn import metrics
-
- # 计算ROC曲线的x轴 和 y轴数据
- fpr, tpr, _ = metrics.roc_curve(y_test, GNB.predict_proba(X_test)[:,1])
- # 绘制ROC曲线
- plt.plot(fpr, tpr, linestyle = 'solid', color ='red')
- # 添加阴影
- plt.stackplot(fpr, tpr, color='steelblue')
- # 绘制参考线
- plt.plot([0,1],[0,1],linestyle='dashed', color='black')
- # 添加文本
- plt.text(0.6, 0.4, 'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict=dict(size =16))
- plt.show()
- import numpy as np
- from sklearn.model_selection import train_test_split
- from sklearn.preprocessing import StandardScaler
- from sklearn.metrics import classification_report, confusion_matrix, roc_curve, auc
- import tensorflow as tf
- from tensorflow import keras
- import matplotlib.pyplot as plt
-
- # 数据标准化
- scaler = StandardScaler()
- X_train_scaled = scaler.fit_transform(X_train)
- X_test_scaled = scaler.transform(X_test)
-
- # 构建MLP模型
- MLP = keras.Sequential([
- keras.layers.Dense(128, input_shape=(X_train_scaled.shape[1],), activation='relu'),
- keras.layers.Dropout(0.5),
- keras.layers.Dense(64, activation='relu'),
- keras.layers.Dropout(0.5),
- keras.layers.Dense(1, activation='sigmoid') # 二分类,使用sigmoid激活函数
- ])
-
- # 编译模型
- MLP.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
-
- # 训练模型
- MLP.fit(X_train_scaled, y_train, epochs=20, batch_size=32, validation_data=(X_test_scaled, y_test))
-
- # 在测试集上预测
- y_pred_proba = MLP.predict(X_test_scaled)
- y_pred = np.round(y_pred_proba) # 将概率转换为类别
-
- # 计算测试集准确率
- test_loss, test_accuracy = model.evaluate(X_test_scaled, y_test)
- print("测试集准确率:", test_accuracy)
-
- # 打印混淆矩阵
- print("混淆矩阵:")
- print(confusion_matrix(y_test, y_pred))
-
- # 获取分类报告
- report_mlp = classification_report(y_test, y_pred)
-
- # 打印分类报告
- print("分类报告:")
- print(report_mlp)
- from sklearn import metrics
-
- # 计算并绘制ROC曲线
- fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba)
- roc_auc = auc(fpr, tpr)
-
- plt.figure(figsize=(8, 6))
- plt.plot(fpr, tpr, label='ROC curve (AUC = %0.2f)' % roc_auc)
- plt.plot([0, 1], [0, 1], 'k--') # 绘制随机猜测的直线
- # 添加阴影
- plt.stackplot(fpr, tpr, color='steelblue')
- # 添加文本
- plt.text(0.6, 0.4, 'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict=dict(size =16))
- plt.xlabel('False Positive Rate')
- plt.ylabel('True Positive Rate')
- plt.title('ROC Curve')
- plt.legend(loc='lower right')
- plt.show()
在这里的话,主要应用AUC对模型进行评估,可以看到上面所采用的三种模型,多层感知机的效果是最好的
上述三个模型的准确率都不是很高,所以我再找了一个模型,GBDT模型
GBDT(Gradient Boosting Decision Trees)是一种基于决策树的集成学习算法,它通过迭代地训练决策树来提高模型性能。
通过网格搜索法得到模型的最佳参数,下面是对GBDT模型的参数设置。
gbdt = GradientBoostingClassifier(n_estimators=300, learning_rate=0.05, random_state=42,max_depth= 5)
完整代码
- from sklearn.datasets import make_classification
- from sklearn.model_selection import train_test_split
- from sklearn.ensemble import GradientBoostingClassifier
- from sklearn.metrics import accuracy_score
-
- # 初始化梯度提升树分类器
- gbdt = GradientBoostingClassifier(n_estimators=300, learning_rate=0.05, random_state=42,max_depth= 5)
-
- # 拟合训练集数据
- gbdt.fit(X_train, y_train)
-
- # 对测试集进行预测
- y_pred = gbdt.predict(X_test)
-
- # 计算模型准确率
- accuracy = accuracy_score(y_test, y_pred)
- print(f"模型准确率:{accuracy}")
-
-
- from sklearn.datasets import make_classification
- from sklearn.model_selection import train_test_split
- from sklearn.ensemble import GradientBoostingClassifier
- from sklearn.metrics import accuracy_score, confusion_matrix, roc_curve, auc
- import matplotlib.pyplot as plt
-
- # 计算模型准确率
- accuracy = accuracy_score(y_test, y_pred)
- print(f"模型准确率:{accuracy}")
-
- # 打印混淆矩阵和分类报告
- print("混淆矩阵:")
- print(confusion_matrix(y_test, y_pred))
-
- print("分类报告:")
- print(classification_report(y_test, y_pred))
-
- # 计算ROC曲线的x轴 和 y轴数据
- fpr, tpr, _ = metrics.roc_curve(y_test, gbdt.predict_proba(X_test)[:,1])
- # 绘制ROC曲线
- plt.plot(fpr, tpr, linestyle = 'solid', color ='red')
- # 添加阴影
- plt.stackplot(fpr, tpr, color='steelblue')
- # 绘制参考线
- plt.plot([0,1],[0,1],linestyle='dashed', color='black')
- # 添加文本
- plt.text(0.6, 0.4, 'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict=dict(size =16))
- plt.show()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。