数据分析综合项目案例:幸福指数深度挖掘(KNN,随机森林)
这个项目主要难度在清洗阶段。先看一下数据集。这个数据字段稍微有点多。很多很多字段可以看一下下方的拉条。怎么样去使用字段是很关键的。这里左右字段的含义用一张表格整理了一下。介绍的字段信息有很多。这当中的字段一共有 140 个。有一些预备知识需要介绍一下。预备知识基础好可跳过什么是数据预处理通常获取数据通常都是不完整的缺失值、零值、异常值等情况的出现导致数据的质量大打折扣而数据预处理技术就是为了让数据具有更高的可用性而产生的。原始数据 菜市场刚买回来的生鲜数据预处理 备菜你要做一顿大餐跑机器学习 / 数据分析模型直接拿刚买的菜下锅肯定不行菜里有烂叶子、泥沙、虫眼、大小不一、有的还冻坏了还有不能吃的菜根外皮不处理直接炒味道差、没法吃甚至会吃坏肚子。数据预处理就是给 “原始数据” 做全套备菜操作把脏、乱、残缺的数据整理成能直接下锅分析的干净数据。原始数据有缺失值、零值、异常值缺失值 蔬菜缺块 / 少半颗比如买的白菜有一大半烂掉丢掉、鸡蛋碎了一个放到数据里就是某条记录少填了年龄、销售额、检测指标。不处理的后果模型读到一半 “断食材”直接报错或者计算跑偏。预处理操作补全用平均值填充、直接删掉残缺严重的样本。异常值 菜里混进石子 / 变质食材比如一筐土豆里混了块石头、一颗完全发霉的土豆数据里就是离谱数字正常人身高 2 米以内突然出现一条身高 999cm 的错误录入。不处理的后果这颗 “石子” 会搅乱整锅菜的味道拉高 / 拉低整体平均值分析结果完全失真。预处理操作剔除极端异常、修正错误数值。零值干扰 不该为空的食材全是空比如统计店铺日销量某天设备故障全部记成 0但当天明明有营业不是真实销量为 0是采集出错。不处理的后果误以为店铺那天彻底停业后续营收预测全部出错。预处理操作区分真实零和错误零修正无效零记录。重复数据的处理还有重复的数据首先第一步也是非常重要的一步就是判断是否是重复数据。来看一下哪个是真正的重复数据。表格 A姓名体重身高购买商品付款金额小华67180OPPO Reno164499小华67180oPPO Reno164499表格 B姓名体重身高购买商品付款金额付款时间小华67180OPPO Reno1644992026-06-13 00:00小华67180oPPO Reno1644992026-06-13 00:00通过表格 A 的这两个数据可以发现购买商品不太一样一个是大写的O一个是小写的o。同时观察一下 B发现商品也是一个大写一个小写的。这俩表格的两行数据其实都是完全一摸一样的只不过商品大小写不一样但是其他的部分意思全部都是一样的。如果你认为是一样的内容那么就全部把它改为大写或者全部改为小写然后去重。数据量非常多的时候保险起见可以统一变成大写或者统一都变成小写。一般来说把常用的处理放在前面把去重放在后面。接下来去 jupyter 上面看一下数据。把这段代码直接复制粘贴上去然后运行一下。import pandas as pd # 创建一个带有重复数据的DataFrame df pd.DataFrame(data[[a, 1], [a, 2], [a, 3],[b, 1], [b, 2],[a, 1], [a, 2]],columns[label, num]) df可以发现总共是 5 行 a2 行 b。a 对应的 num 是 1 2 3如果想判断这些数据是否有重复的数据需要用到一个函数。直接 df.duplicated() 就可以。# 判断 df.duplicated() # axis0直接打印就可以发现它第一次出现的时候不认为是重复数据当它第二次出现的时候就认为是重复数据了。默认是按行进行删除的。它里面参数 axis 默认是 0除非手动设置一下 axis 为 1就会变成按列删除。之间介绍过除非这个字段对分析没有任何用处反而执行的时候又影响到执行的效率这个时候就可以考虑把字段给删除掉其余时候要慎删。一般来说都是删除行。把重复值删掉。使用 df.drop_duplicates() 就可以了。打印出来看看。df.drop_duplicates()可以看到运行之后它把最后 5 6 这两行删除了只剩下 0 1 2 3 4。这个就是重复值删除掉的过程。接下来看一下数据的标准化处理。数据标准化Z-score 标准化StandardScaler只做两件事把这一列数据的平均值变成 0数据离散程度波动统一成标准差 1。本质衡量“这个数值离平均水平差了几个档次”消除单位影响。举个栗子身高160、165、170、175、180。全班平均身高 170我们以 170 为基准线 0170 → 0刚好平均175 比平均高 5cm → 1165 比平均矮 5cm → -1180 高 10cm → 2160 矮 10cm → -2处理后数据-2-1012。数字含义正数 高于平均负数 低于平均数字绝对值越大偏离均值越远。再来个栗子考试排名分不看原始卷面分只看你在班里处于上游还是下游。比如语文满分 150、数学满分 100没法直接对比用 Z-score 后两门课都以班级平均分做基准可以公平比较你哪科更好。1. 计算公式原始样本值该特征全部数据的均值该特征全部数据的标准差标准差的公式符号说明总体标准差总体均值你之前 Z 分数公式里的均值总体全部数据量第个数据2. 处理后数据特性新数据集均值 0标准差 1保留原始数据的相对分布、离群点关系数值可正可负无固定区间。3. 适用场景对尺度敏感的算法SVM、逻辑回归、线性回归、PCA 主成分分析、K-Means 聚类、神经网络数据大致服从正态分布需要消除量纲比如收入、年龄、体重单位完全不同。4. 缺点受极端异常值离群点影响很大。代码实现回到 jupyter notebook 中使用代码感受一下这个数据处理方法。将下面代码复制粘贴到 jupyter 当中然后运行。from sklearn.preprocessing import StandardScaler import pandas as pd views pd.DataFrame({ height: [1.8, 1.7, 1.9, 1.75, 1.68, 1.67], weight: [80, 70, 98, 67, 68, 50] }) ss StandardScaler() views_scaled ss.fit_transform(views) # 使用同一个Z-score进行标准化 print(views_scaled)发现身高体重都标准化处理完成了。还可以用定义函数的方式手动实现 Z-score 标准化。效果是一样的。from sklearn.preprocessing import StandardScaler import pandas as pd import numpy as np views pd.DataFrame({ height: [1.8, 1.7, 1.9, 1.75, 1.68, 1.67], weight: [80, 70, 98, 67, 68, 50] }) f lambda x: ((x - np.mean(x)) / np.std(x)) views.apply(f)MinMaxScaler() 最大最小化处理把一整列数据全部压缩到 0 ~ 1 区间里原始最小值变成 0最大值变成 1中间数值按比例缩放。举个栗子班级 5 个同学身高160、165、170、175、180。最小身高 160 → 映射为 0最大身高 180 → 映射为 1。中间 170 刚好在中间就变成 0.5165 就是 0.25175 是 0.75。处理后全部数据0, 0.25, 0.5, 0.75, 1。像压缩视频分辨率原图高低差距很大统一压到固定范围。或者称重不管东西最重多沉、最轻多轻统一换算成 0 到 1 的比例。1. 计算公式该特征最小值该特征最大值2. 处理后数据特性全部数据被压缩到 [0, 1] 区间严格保留数据大小比例关系均值不再固定为 0。3. 适用场景不依赖距离、方差的模型决策树、随机森林树模型对尺度不敏感归一化非必需图像像素归一化像素 0~255 缩放到 0~1神经网络输入层很多网络要求输入 0~1数据无明显极端异常值。4. 缺点如果数据存在极大 / 极小离群值会导致正常数据被挤压在极小区间区分度变差。Z-score vs MinMax 核心对比表格展示维度Z-score 标准化MinMax 归一化输出范围无固定区间均值 0 方差 1固定 [0,1]抗异常值差均值标准差易被偏移极差更易被离群值破坏核心作用消除分布偏移、统一方差压缩数值区间、保留比例代表工具StandardScaler()MinMaxScaler()代码实现回到 jupyter 当中将下面的代码复制粘贴。运行之后可以看到最大最小化处理把每一列的数据都压缩在了 0 ~ 1 之间。from sklearn.preprocessing import MinMaxScaler import pandas as pd views pd.DataFrame({ height: [1.8, 1.7, 1.9, 1.75, 1.68, 1.67], weight: [80, 70, 98, 67, 68, 50] }) mms MinMaxScaler() views_scaled mms.fit_transform(views) print(views_scaled)补充关键知识点fit_transform 和 transform 的区别训练集用 fit_transform一边统计最大最小值一边转换测试集只能用 transform必须沿用训练集算出的 min/max不能重新 fit防止数据泄露接下来看一下哑变量和独热编码。哑变量和独热编码先讲底层问题机器看不懂文字。假设一列特征性别【男、女】颜色【红、黄、蓝】。电脑只认数字如果你手动标男 1女 2模型会误以为女的数值比男大存在大小关系但性别根本没有高低之分会造成严重误差。解决办法独热编码衍生出来的 0/1 新列就叫哑变量。前面两种只处理连续数值特征独热编码专门处理分类离散特征文字 / 类别。1. 概念说明哑变量Dummy Variable把多分类特征拆成若干 0/1 二元变量比如特征 “颜色红 / 蓝 / 黄”拆为红、蓝、黄三列样本为红色则红列 1其余 0。普通哑变量会舍弃一列避免多重共线性线性回归常用。独热编码One-Hot Encoding完整生成全部类别对应的 0-1 向量不丢弃任何一列树模型、神经网络、KNN 常用对应工具 OneHotEncoder()。2. 示例原始分类列[男,女,男,未知]独热编码后生成 3 列男女未知1000101000013. 适用场景类别无序离散特征城市、性别、商品类型、颜色不能直接把文字类别丢进数学模型模型只能计算数字必须转为 0/1 数值。4. 局限类别数量极多时如上万城市会产生维度爆炸此时改用标签编码、目标编码更合适。代码实现现在有性别{男女其他}。性别特征有三个不同的分类值需要三个 bit 的值来表示这些类别。独热码表示为 男{01}女{10}其他{00}。多个特征时表示为性别{男女其他} 性别编码为男{01}女{10}其他{00}。年级{一年级二年级三年级} 年级编码为一年级{10}二年级{01}三年级{00}。对于二年级的男生就可以编码为{0110}前面的 01 表示一年级后面的 10 表示男生。哑变量方式下面代码复制粘贴运行一下。没问题。# 哑变量 import pandas as pd df pd.DataFrame({ 性别: [男, 女, 其他], 年级: [一年级, 二年级, 三年级] }) df pd.get_dummies(df) df这里 False 是 0True 对应的是 1。老版本显示的是 0 和 1版本较新的话显示的是 True 和 False。它是 pandas 里面的方式可以发现性别有三列年级也是有三个。因为它会看里面每一个的唯一值可以给它单独做成一列。独热编码也是一样的用 one_hot 进行转化就可以了。独热码表示 男{001}女{010}其他{100}。多个特征时表示性别{男女其他}性别编码为男{001}女{010}其他{100}年级{一年级二年级三年级}年级编码为一年级{100}二年级{001}三年级{010}对于二年级的男生编码可以为{001001}前面的 001 表示二年级后面的 001 表示男生。将下面的代码直接复制粘贴一下然后运行。import pandas as pd from sklearn.preprocessing import OneHotEncoder df pd.DataFrame({ 性别: [男, 女, 其他], 年级: [一年级, 二年级, 三年级] }) ont_hot OneHotEncoder(categoriesauto) ont_hot.fit(df) ont_hot.transform(df).toarray()这地方也是先拟合后转化可以分开写也可以下划线合一起。可以发现这地方也是转成了数组的格式和上面的方式是一模一样的。以上这些内容复习好了之后正式开始幸福指数分析项目。综合项目案例幸福指数深度挖掘先来看字段说明这个表。这个数据的是做问卷调查得来的。问卷调查是为了了解更多的信息总共是 140 个问答题。这个数据集大概看一下发现这里对应的编号、类型、问卷时间、民族、宗教信仰、还有年收入、政治面貌、房子所有者还有第三方平台的资金等等这个是数据是很全面的。这个问的信息是非常全面的主要针对的是个人信息还有一些关于配偶子女的信息。最难的是对这些数据进行处理。可以看到这里有关键字段已经给标黄了happiness这个字段也就是目标列。就是生活是否幸福。看一下它这里的选项。1 非常不幸福; 2 比较不幸福; 3 说不上幸福不幸福; 4 比较幸福; 5 非常幸福; -8 无法回答。观察一下这个数据可以发现 3 和 -8 是差不多的。接下来看一下怎么去做处理看一下唯一值都有哪些。分析回到 jupyter开始做分析。首先数据集先读取出来并且指定一下编码格式打印出来看看。Happinesspd.read_csv(happiness_train_complete.csv,encodinggbk) Happiness打印出来之后发现这里的数据有很多。总共采集了 8000 条数据。想看人是不是幸福最终通过 140 个问题来看到底幸福不幸福幸福的程度是多少如果总共是 10 分能打多少分。所以通过这些数据happiness 是目标列后面建模预测幸福指数有多少这个就是目标。接下来去看一下目标列重复值。介绍一种新的方式set()。直接把变量名字拿过来目标列拿过来这样就可以直接去做处理。这是一种比较简单的方式。set() 是 Python 内置集合函数集合会自动剔除所有重复元素只保留唯一不重复的值。# 重复值 set(Happiness[happiness])打印出来可以看到一共 6 个值之前分析过-8 和 3 是差不多的意思1 非常不幸福; 2 比较不幸福; 3 说不上幸福不幸福; 4 比较幸福; 5 非常幸福; -8 无法回答。所以在做数据清洗的时候要把这两个字段清洗一下。数据清洗数据清洗比较多慢慢来看。归类处理现在可以先把那个 -8 和 3 给处理了。-8 是问卷里无法回答不能直接删掉样本并且和 3 表达的意思说不上幸福不幸福一样于是统一归 3说不上幸福不幸福方便后续建模分析。直接使用 replaced 函数进行替换把 -8 用 3 来替换就可以了。替换完成之后可以通过画图的方式来进行展示。可以看一下这些数据的分布情况。# 把所有“无法回答(-8)”的样本统一归类为“说不上幸福不幸福(3)” Happiness[happiness]Happiness[happiness].replace(-8,3) Happiness[happiness].plot.hist()可以发现 4 是最高的回到数据当中看一下 4 的意思4 比较幸福。这当中还是存在不幸福的情况只不过是比较少的。这是第一个字段。缺失值处理缺失值查看接下来看一下数据有没有缺失。使用 info() 进行查看。Happiness.info()可以发现info() 在这个地方使用似乎行不通。因为这个数据有点儿多没有办法把全部的数据显示出来。可以使用 isnull 来看一看。在数据比较多的情况下可以筛选进行查看。先查看其中 30 个数据看有没有空值的情况用这种方式试一试。先把字段拿过来先看前 30 个数据。Happiness.isnull().sum()[:30]通过这 30 行的查看可以发现是有空值存在的所以要对空值进行一下处理。统计一下缺失的比例。定义一个函数把 Happiness 拿过来然后首先统计一下总的缺失值个数。接下来要去算它的百分比用上面的名字加上百分比命名新变量然后将缺失的总个数除以数据量总数的值赋值到变量当中。之后要把算出来的个数和百分比拼接用 pandas 里面的 concat 方法进行拼接个数和百分比都拿过来拼接方向是 1 轴axis1。之后添加列名让数据更容易去查看0 这列表示缺失数1 这列表示缺失的百分比。接下来通过切片的方式去取它下标为 1 这列不为 0 的将这列进行降序ascendingFalse排序依据是缺失百分比。最后可以再把这个结果保留一位小数。最终把这个表进行返回。调用这个函数然后打印一下这个表格看一下对应的缺失百分比的结果。# 缺失值比例 def miss_deal(Happiness): # 统计它总体的个数 miss_countHappiness.isnull().sum() # 统计它的百分比 miss_count_precentmiss_count*100/len(Happiness) # 个数和百分比进行拼接 miss_tablepd.concat([miss_count,miss_count_precent],axis1) # 添加列名 miss_tablemiss_table.rename(columns{0:缺失数,1:缺失百分比}) miss_tablemiss_table[miss_table.iloc[:,1]!0].sort_values(缺失百分比,ascendingFalse).round(1) return miss_table miss_tablemiss_deal(Happiness) miss_table这是运行之后缺失比例的结果。这时候就可以根据确实的数据给它做一下处理。观察一下这个数据前面都还是正常的最后的时候打印出来了两行 0.0。其实数据是没有问题的因为它是百分比到最后是 0.0并不是没有把数据处理完而是因为最后小数点值保留了一位有可能是 0.02%给它保留的是一位所以就只剩 0.0 了。缺失比例有了现在就可以依照缺失比例给它去做处理。首先是删除操作。缺失值比例大于 60 的字段处理先筛选出来缺失值比例大于 60 的返回列名存入到变量当中打印出来看一看。# 删除缺失值 del_featurelist(miss_table[miss_table[缺失百分比]60].index) del_feature这些就是缺失比例大于 60% 的字段。前面缺失百分比那个地方没有加百分号是因为加了之后就变成文本类型了为了让字段保持数值类型可以参与计算就没有加百分号但是要清楚这里是百分比。这里是有索引的在原数据里面对应的是列名这个地方缺失了 60% 的数据直接给删除掉了。这里还有 100%99.6% 这种这样的字段就没有任何意义了。这里使用 drop 来删除字段。Happiness.drop(del_feature, axis1, inplaceTrue) print(len(Happiness.loc[0]))打印出来的结果是 130也就是去除缺失值在 60% 以上的数据之后还剩 130 个字段。删除了 10 个。现在缺失值大于 60% 的字段已经做完处理了还有缺失值在 60% 以下的数据没有进行处理。配偶相关字段处理缺失值在 60% 以下的这部分数据不能使用删除处理因为缺失的比较少说明对后面分析肯定是有用处的这些字段就用填充的方法进行处理。具体用什么办法填充要结合介绍里面的资料来看。首先这些字段里以 s_ 开头的比较多。可以到介绍标里筛选一下 s_ 开头的找一下字段。发现这几个都在一起的。可以看到都是关于配偶的数据放在了一起。这里有详细的解释。这个 s_work_exper 字段“1 目前从事非农工作; 2 目前务农曾经有过非农工作; 3 目前务农没有过非农工作; 4 目前没有工作而且只务过农; 5 目前没有工作曾经有过非农工作; 6 从未工作过; ”。感觉一下这个字段的缺失值应该怎样去填充。这个时候就考验对业务的理解能力了。可以看到这一小截全是配有的数据还有一种情况就是这个人没有配偶。所以其实不用管它写的多复杂缺失的地方直接用 0 来填充就好了。把字段拿过来直接复制粘贴进来使用 fillna 函数进行填充。# 填充 Happiness[s_work_exper] Happiness[s_work_exper].fillna(0)剩下几个字段除了 s_work_exper 还有别的。别的其实和刚刚也是一样的写的再复杂ta 有可能没配偶也都直接用 0 来填充就可以了。剩下的那些只要是 s_ 开头的都可以用 0 来填充。Ctrl CCtrl V把那些 s_ 开头的字段全都拿过来全部用 0 来填充。都写在同一个代码框里就可以。直接运行。# 填充 # 配偶 Happiness[s_work_exper]Happiness[s_work_exper].fillna(0) Happiness[s_income]Happiness[s_income].fillna(0) Happiness[s_hukou]Happiness[s_hukou].fillna(0) Happiness[s_political]Happiness[s_political].fillna(0) Happiness[s_birth]Happiness[s_birth].fillna(0) Happiness[s_edu]Happiness[s_edu].fillna(0)这地方就全部填充完成了。教育相关字段处理s_ 开头的全部处理完成之后还有 edu_ 开头的两个字段一个是 edu_yr一个是 edu_status。这俩字段是关于教育教育看一下用什么来进行填充。同样方法先到字段解释的那张表搜索一下。搜到之后定位在这里。搜索之后可以看到 edu_status 表示的是最高教育程度的状态。“1 正在读; 2 辍学和中途退学; 3 肄业; 4 毕业; ”。这里面肄业其实和 2 是差不多的。这里面肄业和辍学的起码是读过书了还有种情况就是压根连书都没读的。这里的缺失值对应的就是这种情况。所以这个地方也用 0 来填充。但是要知道一下上面代码框里面的 0 和下面的不一个意思虽然都是 0。上面的 0 表示没有配偶下面的 0 表示没念过书。# 教育 edu_status Happiness[edu_status]Happiness[edu_status].fillna(0)edu_yr对于这个字段的解释是“您已经完成的最高学历是哪一年获得的“已完成”指已获得毕业证”。这个字段没有太大的意义后面也没有对应的描述所以它和幸福指数没有太大的关系。这里可以手动将这个字段进行删除。del Happiness[edu_yr]这样edu_ 开头的字段也给进行处理了。社交相关字段处理接下来处理 social_ 开头的social_neighbor 和 social_friend 这两个字段social_ 开头一般是社交。看一下社交部分还是刚才的办法从介绍的表格当中查找。在这里。一个是和邻居的社交程度一个是和朋友社交的频繁程度。可以看到对应的值是一模一样的都是“1 几乎每天; 2 一周1到2次; 3 一个月几次; 4 大约一个月1次; 5 一年几次; 6 一年1次或更少; 7 从来不; ”。尽管它再复杂一周几次一个月几次这样的前面都是有社交的。还有一部分人是从出生到现在完全没社交过的。对应的就是 7, 7 就是从来不。这个地方就可以用 7 来进行填充。这两个字段的空值都是用 7 来填充。# 社交 Happiness[social_neighbor]Happiness[social_neighbor].fillna(7) Happiness[social_friend]Happiness[social_friend].fillna(7)孩子相关字段处理字段 minor_child孩子相关。可以从介绍表里看到它表示有多少个未成年子女后面对应的没有给出描述。这里可以用 0 进行填充。压根没有孩子的。# 孩子 Happiness[minor_child]Happiness[minor_child].fillna(0)户口相关字段处理hukou_loc 字段户口相关。还是刚才的方法去找字段是什么意思。可以看到这里是户口登记地。上户口就是出生的时候录入的身份信息。所以这里的空值直接填 4户口待定就可以了。# 户口 Happiness[hukou_loc]Happiness[hukou_loc].fillna(4)婚姻相关字段处理marital_1st 这个字段表示第一次结婚的时间。marital_now 表示与目前的配偶是哪一年结婚的。发现 marital_now 这个字段没有一个具体的描述。这个时候要回到原数据也就是数据集当中来。找到这个字段在这里。然后点一下这个字段的筛选可以看到它最新的年份是 2023 年。我们可以认为这份调查问卷是 2023 年做的。所以如果这里是空值我们就可以默认填写 2023。所以这个字段的缺失值用 2023 来填充就可以了。通过这个字段还可以计算出一个字段婚姻时长就是结婚多少年了。这个字段命名为“marital_yr”直接用 2023 减去结婚年份就可以了。最后再将数据类型转换为 int64。这个算出来之后“marital_now”这个字段就不需要了可以给它删除掉。marital_1st 这个字段也要删除掉。这个字段意思是第一次结婚的时间。结婚年份都有了这个字段没啥用不留着了。这样婚姻情况就处理好了。# 婚姻 marital_now Happiness[marital_now]Happiness[marital_now].fillna(2023) Happiness[marital_yr]2023-Happiness[marital_now].astype(int64) del Happiness[marital_now] del Happiness[marital_1st]家庭收入字段处理family_income 这个字段。收入这个字段是比较特殊的一般都是使用中位数来进行填充。这里也是直接用中位数去填充就可以了。运行看一下。# 收入 用中位数填充 Happiness[family_income].fillna(Happiness[family_income].median())运行之后是这样的结果。把这个填充的值赋给家庭收入字段就处理完成了。最后再打印一下空值占比结果看看是不是都处理完成了是不是都为 0。# 收入 用中位数填充 Happiness[family_income]Happiness[family_income].fillna(Happiness[family_income].median()) miss_tablemiss_deal(Happiness) miss_table可以看到打印出来的确实数和缺失百分比都没有了也就是所有的缺失值都处理完成了。接下来对类型进行处理。类型处理年龄计算首先对年龄进行处理。观察数据发现没有任何一个字段表示年龄。年龄应该如何获取。可以观察一下字段。这个年龄是可以通过两个字段计算出来的。使用问卷当前时间的年份减去出生年份就可以计算出来年龄。也就是 survey_time 和 birth 这两个字段。新增的这个字段命名为“age”。用 survey_time 字段的年减去 birth 就可以了。年龄计算出来之后就可以把这两个字段删掉了。然后把配偶、父亲、母亲的出生年份也都删除掉这些都用不上。# 年龄 Happiness[age]pd.to_datetime(Happiness[survey_time]).dt.year-Happiness[birth] Happiness.drop([survey_time,birth],axis1,inplaceTrue) Happiness.drop([s_birth, f_birth, m_birth], axis1, inplaceTrue)接下来对字段进行标准化处理。对数值型特征进行标准化把所有连续数值特征存入列表使用 Z-Score 标准化StandardScaler完成数据标准化处理。转换后的标准化结果直接覆盖回原数据集对应的列。这么做可以消除不同特征量纲、数值范围差异方便后续建模。# 对数值型特征进行标准化 from sklearn.preprocessing import StandardScaler, MinMaxScaler numeric_cols [ income, height_cm, weight_jin, s_income,family_income, family_m, house, car, son, daughter, minor_child, inc_exp, public_service_1, public_service_2, public_service_3, public_service_4, public_service_5, public_service_6, public_service_7, public_service_8, public_service_9, floor_area ] Happiness[numeric_cols] (StandardScaler().fit_transform(Happiness.loc[:, numeric_cols]))如果对这一步不熟悉可以查看本篇开头预备知识里的数据标准化的那部分内容。标准化处理完成之后整个的数据清洗阶段也就完成了。开始下一个阶段建模。建模KNN随机森林KNN 方法KNN 算法介绍建模处理需要看一下用到的算法。使用的是 KNNK近邻算法。它是监督学习而且既不属于分类也不属于回归。思想很简单就是为了找到它的邻居这里有一个欧氏距离的概念这是距离的公式。可以看一下。KNN 算法它的理论就是通过这个公式去算距离。给定一个数据集对于一个新来到的样本模型在数据集中找到距离该样本最近的 K 个样本在这 K 个样本中某一类出现的次数最多就把这个新的样本分到这个类别中少数服从多数原则。比如下图这个黄色圆形属于哪个类别如果是在最里面实心圆圈的范围内这个黄色圆形就会分在橘色三角这个类别里面。因为这个实心圆圈的范围里面橘色三角形有两个绿色方形只有一个遵循少数服从多数原则这个黄色圆形样本就是属于橘色三角形类别的。如果设置了 K5就要找它最近的 5 个邻居就不在实心圆圈的范围了而是在虚线圆圈的范围里。这个时候绿色方形有 3 个而橘色三角形只有 2 个这时候预测出来的黄色圆形它就不再属于橘色三角形的类别而是绿色方形类别。也就是这个黄色圆形在 K3 的时候预测出来是橘色三角形在 K5 的时候预测出来是绿色方形。K 值的选择会影响预测出来的类别。KNN 中 K 值的选取对分类的结果影响至关重要K 值选取的太小模型太复杂K 值选取的太大导致分类模糊。提取特征列目标列回到 jupyter 上来先把特征列和目标列提取出来。首先是特征列把目标列删除id 也删除剩下的全是特征列。接下来目标列直接去获取就可以了。然后打印一下 y 对应各个值出现的次数看一看这个数据。# 建模 from sklearn.model_selection import train_test_split XHappiness.drop([happiness,id],axis1) yHappiness[happiness] print(y.value_counts())打印出来 y 的次数可以发现它这个分布是不均匀的。分布不均匀的话之前介绍过过拟合的概念过拟合就是在测试集效果不好在训练集效果好。在这里模型会偏向预测大类、完全忽略小类这时用过采样解决。过采样过采样复制、生成少数类样本人为扩充少数类数量让两类样本数量接近均衡核心思路增加少数类数据消除类别数量差距使数据达到平衡。看一下这个图。可以看到(a) 就是单纯取了附近的几个点直接进行计算在 (b) 中它还是那几个点但是在中间加了个正方形。它在两个数据点之间插入了这个点这个点就是数据这个操作叫插值法通过这个方法把类别数据增加实现过采样。用这个方式不会影响模型性能它会让数据的每个类别更加均衡更加平均一点儿。实现过采样导入过采样的包使用 SMOTE 来进行处理。使用的时候随机种子设置为 0 就可以了把特征拿过来进行拟合然后重采样。再来打印一下 y 各个值出现的次数。这里有个警告不用管。能运行就行。from imblearn.over_sampling import SMOTE over_samplesSMOTE(random_state0) X,yover_samples.fit_resample(X,y) # 重采样 print(y.value_counts())再次打印 y 的次数发现这几类样本数量变得非常均匀了。让数据均衡有两种方式一种是把多的类别数据量变少一种是把少的变多。本次是把少的变多。因为重采样的方法是通过 SMOTE 这样的方式SMOTE 是经典过采样算法算法逻辑固定通过插值生成新的少数类样本把少数类样本数量提升至和多数类持平实现均衡。让每一类数据都是一样多的。其实实际上在处理这类问题的时候不一定必须一样多的相差一两个或者三四个都是没问题的。但是像之前那样样本量一个是几百个一个是几千这就差别太大了。这次是数据量都一样了换成别的数据集有可能有少一点儿或者多一点儿的情况都是正常的。划分数据集、训练、拟合、预测、评估数据均衡了之后下一步就是要划分数据集了将数据集划分出训练集和测试集。并且使用算法将算法导入进来。评估方式顺便也导入进来分类算法评估常用的指标是准确率把准确率的包导入进来。之后开始建模这里需要设置它的 K 值这里的 K 值是用 n_neighbors设置为 5。这个如果不合适后面可以调整的肘部法则。严谨一点儿是要用肘部法则来调整这里直接设置为 5 了。下一步就是拟合数据用训练集数据训练模型。接下来可以去预测了放的是测试集数据存入到 y_hat 里之后就可以把预测值打印一下。最后准确率也打印一下。from sklearn.neighbors import KNeighborsClassifier # 分类算法 from sklearn.metrics import accuracy_score X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.2,random_state0) KnnKNeighborsClassifier(n_neighbors5) # 可以通过肘部法则去调整 Knn.fit(X_train,y_train) y_hatKnn.predict(X_test) print(y_hat) accaccuracy_score(y_test,y_hat) print(acc)可以看到准确率在 80% 以上模型准确率能达到 80 分性能还是非常好的。KNN 算法实现就是这么实现的比较简单。这样就可以通过输入一个人的特征就能预测出来这个人的幸福情况。还可以尝试一下其他算法这个案例用随机森林也可以做。随机森林方法导入随机森林要用到的包接下来实例化。这里设置一下随机森林当中的随机种子数一般来说树模型结构都设置为 42。实例化之后训练模型训练数据放进去。之后预测预测出来的结果存在 y_hat2 变量当中。最后算一下准确率预测的值和准确率打印出来。# 随机森林算法 from sklearn.ensemble import RandomForestClassifier tree_clfRandomForestClassifier(random_state42) tree_clf.fit(X_train,y_train) y_hat2tree_clf.predict(X_test) print(y_hat2) acc2accuracy_score(y_test,y_hat2) print(acc2)可以发现准确率结果接近 90%模型性能非常好。它比 KNN 准确率是要高一些的。基本上分类算法都是用随机森林来做随机森林比绝大多数的分类算法效果都要好一点。