Python用KNN(K-近邻)回归、分类、异常值检测预测房价、最优K值选取、误差评估可视化

简介: Python用KNN(K-近邻)回归、分类、异常值检测预测房价、最优K值选取、误差评估可视化

全文链接:https://tecdat.cn/?p=33917


KNN是一种非参数学习算法,这意味着它不会对底层数据做出任何假设。这是一个非常有用的特性,因为大多数客户的数据并不真正遵循任何理论假设,例如线性可分性,均匀分布等等点击文末“阅读原文”获取完整代码数据


何时应使用KNN?

假设您想要租一间公寓并最近发现您的朋友的邻居可能在两周内将她的公寓出租。由于该公寓尚未出现在租赁网站上,因此您如何尝试估算其租金?

假设您的朋友每月支付1,200的租金。则您的租金价值可能约为该数字,但是公寓并不完全相同(方向、面积、家具质量等),因此如果有更多其他公寓的数据将会很好。

通过询问其他邻居并查看同一建筑物上的租赁网站列出的公寓,最接近的三个邻居公寓的租金分别为1,200、1,210、1,210和1,215。这些公寓在您朋友的公寓所在街区和楼层上。

在同一楼层但不同街区的其他公寓的租金分别为1,400、1,430、1,500和1,470。它们似乎更昂贵,因为晚上有更多的阳光。

考虑到公寓的接近性,估计您的租金应该约为1,210。这就是K-最近邻(KNN)算法的一般思想!

住房数据集

我们将使用住房数据集来说明KNN算法的工作原理。该数据集源于1990年的人口普查。数据集的一行代表一个街区的普查。

街区组是人口普查局发布样本数据的最小地理单位。除了街区组之外,还有一个变量是家庭,家庭是住在同一所房子里的一群人。

该数据集包含九个属性:

  • MedInc - 每个街区的收入中位数。
  • HouseAge - 每个街区的房屋年龄中位数。
  • AveRooms - 每户人家的平均房间数量。
  • AveBedrms - 每户人家的平均卧室数量。
  • Population - 街区人口。
  • AveOccup - 平均每户人家的居住成员数量。
  • Latitude - 街区纬度。
  • Longitude - 街区经度。
  • MedHouseVal - 房屋中位价值(以千元为单位)。

让我们导入 Pandas 并查看前几行数据:

df.head()

执行该代码将显示我们数据集的前五行:

image.png

我们将使用 MedIncHouseAgeAveRoomsAveBedrmsPopulationAveOccupLatitudeLongitude 来预测 MedHouseVal

现在,让我们直接实现 KNN 回归算法。

使用 Scikit-Learn 进行 K-Nearest Neighbors 回归

到目前为止,我们已经了解了数据集,现在可以继续进行 KNN 算法的其他步骤。

为 KNN 回归预处理数据

y = df['Mexis = 1)

为了进行特征标准化,我们将在稍后使用Scikit-Learn的StandardScaler类。

将数据拆分为训练集和测试集

为了能够在没有泄漏的情况下标准化数据,同时评估我们的结果并避免过拟合,我们将把数据集分为训练集和测试集。

为了使这个过程可重现(使该方法始终对相同的数据点进行采样),我们将random_state参数设置为一个特定的SEED

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=SEED)

这段代码将数据的75%用于训练,将数据的25%用于测试。通过将test_size更改为0.3,例如,您可以使用70%的数据进行训练,并使用30%进行测试。

现在我们可以在X_train数据集上拟合数据标准化,并对X_trainX_test进行标准化。

KNN回归的特征标准化

通过导入StandardScaler,实例化它,根据我们的训练数据进行拟合,并对训练和测试数据集进行转换,我们可以进行特征标准化:

# 仅在X_train上进行拟合
scaler.fit(X_train)
# 对X_train和X_test进行标准化
X_test = scaler.transform(X_test)

现在我们的数据已经标准化。

KNN回归的训练和预测

拟合到我们的训练数据:

......
regressor.fit(X_train, y_train)

最后一步是对我们的测试数据进行预测。要这样做,请执行以下脚本:

python

predict(X_test)

现在,我们可以评估我们的模型在具有标签(真实值)的新数据上的泛化能力-即测试集!

评估KNN回归算法

用于评估算法的最常用的回归指标是平均绝对误差(MAE)、均方误差(MSE)、均方根误差(RMSE)和决定系数(R2)

可以使用sklearn.metricsmean_absolute_error()mean_squared_error()方法来计算这些指标,如下面的代码片段所示:

......
print(f'mse: {mse}')
print(f'rmse: {rmse}')

上面脚本的输出结果如下:

image.png

可以使用score()方法直接计算R2:

regrscore(X_test, y_test)

输出结果如下:

image.png

平均值为2.06,标准差为平均值的1.15,因此我们的得分约为0.44,既不是非常好,也不是太糟糕。

对于R2,得分越接近1(或100),越好。R2表示KNN能够理解(解释)多少数据变化或数据方差。

image.png

得分为0.67,我们可以看到模型解释了67%的数据方差。这已经超过了50%,还可以,但不是非常好。我们能否有其他方法提高预测的效果?

为了确定理想的K值,我们可以分析算法的误差并选择最小化损失的K。

查找最佳的K值

为此,我们将创建一个for循环,并运行具有从1到X个邻居的模型。在每次交互时,我们将计算MAE并绘制K值以及MAE结果:

error = []
# 计算不同k的MAE误差
for i in range(1, 40):
 ......
    error.append(mae)

现在,让我们绘制error

import matplotlib.pyplot as plt 
plt.figure(figsize=(12, 6))
......
plt.ylabel('Mean Absolute Error')

image.png

点击标题查阅往期内容


Python信贷风控模型:Adaboost,XGBoost,SGD, SVC,随机森林, KNN预测信贷违约支付


01

02

03

04



看着图表,当 K 值为 12 时,似乎最小的平均绝对误差(MAE)值。让我们通过绘制更少的数据来仔细观察图表,确认一下:

plt.figure(figsize=(12, 6))
......

image.png

您还可以使用内置的 min() 函数(适用于列表)获取最小误差及其索引,或将列表转换为 NumPy 数组并使用 argmin() 函数(找出具有最小值的元素的索引):

import numpy as np 
......
print(np.array(error).argmin()) # 11

我们从 1 开始计数邻居,而数组从 0 开始,所以第 11 个索引是 12 邻居!

这意味着我们需要 12 个邻居才能以最小的 MAE 误差预测一个点。我们可以再次使用 12 个邻居执行模型和指标,以比较结果:

knn_reg12 = KNei......
print(f'r2: {r2}, \nmae: {mae12} \nmse: {mse12} \nrmse: {rmse12}')

以下代码输出:

image.png

我们已经看到了如何使用 KNN 进行回归,但如果我们想要分类一个点而不是预测其值呢?现在,我们可以看看如何使用 KNN 进行分类。

使用 Scikit-Learn 进行 K-Nearest Neighbors 分类

在这个任务中,我们不再预测连续值,而是想要预测这些街区群组属于的类别。为了做到这一点,我们可以将区域的房屋中位数价值划分为具有不同房屋价值范围或“箱子”(bins)的组。

为分类预处理数据

让我们创建数据的箱子,将连续值转化为类别:

# 创建 4 个类别并将其分配给 MedHouseValCat 列
df["M......False, labels=[1, 2, 3, 4])

然后,我们可以将数据集拆分为属性和标签:

y = df['Med......], axis = 1)

由于我们使用 MedHouseVal 列创建了箱子,我们需要从 X 中删除 MedHouseVal 列和 MedHouseValCat 列。

将数据集拆分为训练集和测试集

与回归一样,我们还将将数据集分为训练集和测试集。由于我们有不同的数据,因此我们需要重复这个过程:

from sklearn.model_selection ......_test_split(X, y, test_size=0.25, random_state=SEED)

我们将再次使用标准的 Scikit-Learn 值,即 75% 的训练数据和 25% 的测试数据。这意味着我们的训练和测试记录数量与之前的回归相同。

对分类进行特征标准化

由于我们处理的是相同的未经处理的数据集及其不同的测量单位,我们将再次执行特征标准化,方式与之前的回归数据相同:

from sklearn.preprocessing import StandardScaler
......
X_test = scaler.transform(X_test)


进行分类的训练和预测

在对数据进行分箱、拆分和标准化之后,我们终于可以在其上拟合一个分类器了。对于预测,我们将再次使用 5 个邻居作为基线。您也可以实例化KNeighbors_类而不使用任何参数,它将自动使用 5 个邻居。这次,我们将导入KNeighborsClassifier类而不是KNeighborsRegressor

from sklearn.neighbors import KNeighborsClassifier
......
classifier.fit(X_train, y_train)

在拟合之后,我们可以预测测试数据的类别:

评估 KNN 进行分类

要评估 KNN 分类器,我们可以使用score方法,但它执行不同的度量标准,因为我们评分的是分类器而不是回归器。

image.png

让我们评分我们的分类器:

acc = classifier.score(X_test, y_test)
print(acc)  # 0.6191860465116279

通过观察得分结果,我们可以推断出我们的分类器大约有 62% 的类别预测正确。这已经有助于分析,尽管仅仅知道分类器预测正确的内容并不容易改进它。

我们可以使用其他指标更深入地研究结果,以确定。在这里,我们将使用与回归不同的步骤:

  1. 混淆矩阵:了解我们对每个类别预测正确或错误的程度。被正确预测的值称为真阳性,被预测为阳性但不是阳性的称为假阳性。同样,真阴性和假阴性的命名方式适用于负值。

image.png

  1. 精度:了解分类器认为哪些正确预测值是正确的。精度将真阳性值除以任何被预测为阳性的值;
  2. 召回率:了解分类器识别出多少真阳性。召回率通过将真阳性除以应该被预测为阳性的任何值来计算。

image.png

  1. F1得分:是精度和召回率的平衡或谐波平均值。最低值为0,最高值为1。当f1-score等于1时,意味着所有类别都被正确预测 - 这是使用真实数据非常难获得的分数(几乎总会存在例外情况)。

image.png

要获取度量值,请执行以下代码段:

## 导入Seaborn以使用热力图
import seaborn as sns
# # 为更好的解释添加类别名称
classes_names = ['cla......
# # 使用Seaborn的热力图更好地可视化混淆矩阵
sns.hea......ue, fmt='d');
print(cla......t(y_test, y_pred))

上述脚本的输出如下所示:

image.png

结果表明,KNN能够以62%的准确率对测试集中的所有5160个记录进行分类,这高于平均水平。支持度相当平均(数据集中类别的分布相当均匀),因此带权重的F1和未加权的F1将大致相同。

通过观察混淆矩阵,我们可以看到以下情况:

  • 类别1在238个样本中大多被误分类为类别2
  • 类别2在256个样本中被误分类为类别1,在260个样本中被误分类为类别3
  • 类别3在374个样本中大多被误分类为类别2,在193个样本中被误分类为类别4
  • 类别4在339个样本中被错误地分类为类别3,在130个样本中被误分类为类别2

寻找最佳的K值进行KNN分类

让我们重复之前用于回归的操作,并绘制K值和测试集的相应指标的图表。您也可以选择适合您上下文的指标,这里我们将选择f1-score

f1s = []
# 计算K值在1到40之间的f1分数
for i in range(1, 40):
    ......
    # 使用average='weighted'计算4个类别的加权平均值
    f1s.append(f1_sc......hted'))

下一步是绘制f1_score值与K值的关系图。与回归不同的是,这次我们不再选择最小化误差的K值,而是选择最大化f1-score值的K值。

执行以下脚本创建绘图:

plt.figure(figsize=(12, 6))
plt.plot(r......
plt.ylabel('F1 Score')

输出的图表如下所示:

image.png

从输出中我们可以看到,当K值为15时,f1-score最高。让我们使用15个邻居重新训练分类器,并查看它对分类报告结果的影响:

classiier15 = ......est)
print(clasificaion_report(y_test, y_pred15))

这将输出以下结果

image.png

除了在回归和分类中使用KNN来确定块值和确定块类别之外,我们还可以使用KNN来检测与大部分数据不同的均值块值 - 不遵循大部分数据趋势的块值。换句话说,我们可以使用KNN来检测异常值

使用Scikit-Learn实现异常值检测的KNN算法

异常值检测使用一种与我们之前进行回归和分类不同的方法。

导入后,我们将实例化一个带有5个邻居的NearestNeighbors类 - 你也可以使用12个邻居实例化它来识别回归示例中的异常值,或者使用15个邻居执行分类示例相同的操作。然后,我们将拟合我们的训练数据,并使用kneighbors()方法找到每个数据点和邻居索引的计算距离:

nbrs.fit(X_train)
# 距离和邻居的索引
distances,......hbors(X_train)

现在,我们对于每个数据点有了5个距离 - 它与其5个邻居之间的距离,并且还有一个标识它们的索引。让我们看一下前三个结果以及数组的形状,以更好地可视化。

执行以下代码以查看前三个距离的形状:

distances[:3], distances.shape

image.png

观察到有3行,每行有5个距离。我们也可以查看邻居的索引:

indexes[:3], indexes[:3].shape

在上面的输出中,我们可以看到每个5个邻居的索引。现在,我们可以继续计算这5个距离的平均值,并绘制一张图表,X轴上计数每一行,Y轴上显示每个平均距离:

dist_means = distances.mean(axis=1)
plt.plot(dis......)

image.png

注意到图表中有一个部分,平均距离具有均匀的值。当平均距离不太高也不太低时,表示该点是我们需要识别并剔除异常值的点。

在此示例中,平均距离为3。让我们再次绘制该图表,并使用水平虚线来标记它:

dist_means = distan(axis=1)
plt.plot(......)

image.png

这条线标记了平均距离,在该线上方的所有值都是变化的异常值。这意味着所有平均距离大于3的点都是异常值。

# 目视确定大于3的截断值
outlie......here(dist_means > 3)
outlierndex

以上代码输出的结果为:

image.png

# 过滤异常值
outlies = df.ioc[outier_index]
outlier_lues

image.png

我们的异常值检测完成了。这就是我们找到与整体数据趋势不符的每个数据点的方法。我们可以看到在我们的训练数据中有16个点应该进一步查看、调查、处理,甚至从我们的数据中删除(如果它们是错误输入)以改善结果。这些点可能是由于输入错误、均值块值不一致或两者兼而有之导致的。

KNN算法的优缺点

优点

  • 实现简单
  • 它是一种惰性学习算法,因此不需要对所有数据点进行训练(仅使用K个最近邻来预测)。这使得KNN算法比其他需要使用整个数据集进行训练的算法(如支持向量机和线性回归等)要快得多。
  • 由于KNN在进行预测之前不需要训练,因此可以无缝添加新数据。
  • 使用KNN只需要两个参数,即K的值和距离函数。

缺点

  • KNN算法在处理高维数据时效果不好,因为在高维空间中,点与点之间的距离计算更复杂,而我们使用的距离度量方法就不再适用。
  • 最后,KNN算法在处理具有分类特征的数据时效果不佳,因为很难计算具有分类特征的维度之间的距离。
相关文章
|
6天前
|
JSON 数据可视化 数据处理
Python基础第九篇(Python可视化的开发)
Python基础第九篇(Python可视化的开发)
|
5天前
|
编译器 索引 Python
Python常见异常类型说明及触发代码示例
开发过程中需要根据具体情况选择处理异常,并使用try-except语句来捕获并处理异常,从而保证程序的健壮性和稳定性。在Python中,异常是程序执行期间发生的错误或意外情况。当解释器遇到异常时,会停止程序的正常执行,并在控制台输出异常的相关信息。Python中有许多不同的异常类型,每个异常类型表示不同的错误或意外情况。OSError:操作系统产生的异常(例如打开一个不存在的文件)。AttributeError:尝试访问一个对象没有的属性。UnicodeError:Unicode相关的异常。
13 5
|
3天前
|
机器学习/深度学习 自然语言处理 数据可视化
文本挖掘与可视化:生成个性化词云的Python实践【7个案例】
词云(Word Cloud),又称为文字云或标签云,是一种用于文本数据可视化的技术,通过不同大小、颜色和字体展示文本中单词的出现频率或重要性。在词云中,更频繁出现的单词会显示得更大,反之则更小。
|
4天前
|
机器学习/深度学习 自然语言处理 数据可视化
文本挖掘与可视化:生成个性化词云的Python实践【7个案例】
词云是文本数据可视化的工具,显示单词频率,直观、美观,适用于快速展示文本关键信息。 - 用途包括关键词展示、数据探索、报告演示、情感分析和教育。 - 使用`wordcloud`和`matplotlib`库生成词云,`wordcloud`负责生成,`matplotlib`负责显示。 - 示例代码展示了从简单词云到基于蒙版、颜色和关键词权重的复杂词云生成。 - 案例覆盖了中文分词(使用`jieba`库)、自定义颜色和关键词权重的词云。 - 代码示例包括读取文本、分词、设置词云参数、显示和保存图像。
21 1
|
3天前
|
数据可视化
Seaborn 可视化(三)
Seaborn的pairplot用于多变量数据可视化,但上半部分与下半部分重复。可通过PairGrid手动定制,如示例所示,用regplot和kdeplot分别绘制对角线以上和以下的图,histplot画对角线。hue参数增强可视化,比如在violinplot和lmplot中按性别着色,展示不同类别。还能通过点的大小和形状(如markers参数)添加信息。Seaborn提供darkgrid等5种样式,用sns.set_style切换。
|
3天前
|
数据可视化 Python
Seaborn 可视化(二)
Seaborn教程展示了如何用`jointplot`创建蜂巢图,以及使用`matplotlib`的`hexbin`函数绘制2D核密度图。此外,它还介绍了2D核密度图,强调其在展示两个变量联合分布上的作用。条形图、箱线图和小提琴图也被讨论,其中箱线图揭示了数据的统计特性,而小提琴图结合了箱线图和核密度图的信息。`pairplot`函数用于可视化数据集中所有变量之间的两两关系。每种图表类型都配有示例图像。
|
3天前
|
数据可视化 数据挖掘 API
Seaborn 可视化(一)
Seaborn是Python的一个基于matplotlib的统计图形库,提供交互式界面,便于创建吸引人的统计图表。它与Pandas集成良好,支持直接使用DataFrame数据进行绘图。Seaborn能绘制直方图(distplot)、密度图(核密度估计)、条形图(计数图)以及散点图(regplot、lmplot、jointplot),适用于单变量和双变量数据分析,如展示分布、关系和趋势。例如,`sns.distplot()`用于直方图,`sns.lmplot()`和`sns.jointplot()`则用于绘制散点图并可添加回归线。
|
1月前
|
机器学习/深度学习 存储 数据可视化
数据分享|Python在Scikit-Learn可视化随机森林中的决策树分析房价数据
数据分享|Python在Scikit-Learn可视化随机森林中的决策树分析房价数据
|
1月前
|
传感器 数据可视化 BI
python研究汽车传感器数据统计可视化分析
python研究汽车传感器数据统计可视化分析