绘制具有8个特征的k最近邻图?

son*_*nja 10 python plot machine-learning nearest-neighbor scikit-learn

我是新来的机器学习,想建立使用一个小样本k-nearest-Neighbor-methodPython的Scikit

转换和拟合数据可以很好地工作,但是我无法弄清楚如何绘制一个显示数据点被“邻居”包围的图形。

我正在使用的数据集如下所示:

数据集的头。 因此,这里有8个功能,外加一个“结果”列。

从我的理解,我得到一个数组,显示euclidean-distances所有数据点,使用kneighbors_graphScikit。因此,我的第一个尝试是“简单地”绘制从该方法得到的矩阵。像这样:

def kneighbors_graph(self):
    self.X_train = self.X_train.values[:10,] #trimming down the data to only 10 entries
    A = neighbors.kneighbors_graph(self.X_train, 9, 'distance')
    plt.spy(A)
    plt.show()
Run Code Online (Sandbox Code Playgroud)

但是,结果图并不能真正可视化数据点之间的预期关系。 具有kneighbors_graph的结果图-方法

因此,我试图调整您可以在有关ScikitIris_dataset的每个页面上找到的示例。不幸的是,它仅使用两个功能,因此它并不是我想要的,但我仍然希望至少获得第一个输出:

  def plot_classification(self):
    h = .02
    n_neighbors = 9
    self.X = self.X.values[:10, [1,4]] #trim values to 10 entries and only columns 2 and 5 (indices 1, 4)
    self.y = self.y[:10, ] #trim outcome column, too

    clf = neighbors.KNeighborsClassifier(n_neighbors, weights='distance')
    clf.fit(self.X, self.y)

    x_min, x_max = self.X[:, 0].min() - 1, self.X[:, 0].max() + 1
    y_min, y_max = self.X[:, 1].min() - 1, self.X[:, 1].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) #no errors here, but it's  not moving on until computer crashes

    cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA','#00AAFF'])
    cmap_bold = ListedColormap(['#FF0000', '#00FF00','#00AAFF'])
    Z = Z.reshape(xx.shape)
    plt.figure()
    plt.pcolormesh(xx, yy, Z, cmap=cmap_light)
    plt.scatter(self.X[:, 0], self.X[:, 1], c=self.y, cmap=cmap_bold)
    plt.xlim(xx.min(), xx.max())
    plt.ylim(yy.min(), yy.max())
    plt.title("Classification (k = %i)" % (n_neighbors))
Run Code Online (Sandbox Code Playgroud)

但是,此代码根本不起作用,我也不知道为什么。它永远不会终止,所以我没有遇到任何我可以使用的错误。等待几分钟后,我的电脑便崩溃了。

代码正在苦苦挣扎的那一行是Z = clf.predict(np.c_ [xx.ravel(),yy.ravel()])部分

所以我的问题是:

首先,我根本不理解为什么我需要拟合预测来绘制邻居。欧氏距离是否不足以绘制所需图形?(所需的图形看起来有点像:有两种颜色可用于糖尿病或非糖尿病;无需箭头等;图片来源:本教程)。

所需图

我的代码错误在哪里/为什么预测部分崩溃?

有没有一种方法可以绘制具有所有特征的数据?我知道我不能有8个轴,但是我想用所有8个特征而不是只有两个特征来计算欧几里得距离(有两个不是很准确,是吗?)。

更新资料

这是一个使用虹膜代码的工作示例,但是我的糖尿病数据集:它使用了我的数据集的前两个特征。我对代码的唯一区别是数组的切割->这里需要前两个功能,我想要功能2和5,所以我以不同的方式切割它。但我不明白为什么我的作品行不通。这是工作代码;复制并粘贴它,它与我之前提供的数据集一起运行:

from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import neighbors, datasets

diabetes = pd.read_csv('data/diabetes_data.csv')
columns_to_iterate = ['glucose', 'diastolic', 'triceps', 'insulin', 'bmi', 'dpf', 'age']
for column in columns_to_iterate:
    mean_value = diabetes[column].mean(skipna=True)
    diabetes = diabetes.replace({column: {0: mean_value}})
    diabetes[column] = diabetes[column].astype(np.float64)
X = diabetes.drop(columns=['diabetes'])
y = diabetes['diabetes'].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
                                                                        random_state=1, stratify=y)
n_neighbors = 6

X = X.values[:, :2]
y = y
h = .02

cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#00AAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#00AAFF'])

clf = neighbors.KNeighborsClassifier(n_neighbors, weights='distance')
clf.fit(X, y)

x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                     np.arange(y_min, y_max, h))

Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

Z = Z.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z, cmap=cmap_light)

plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("3-Class classification (k = %i)" % (n_neighbors))
plt.show()
Run Code Online (Sandbox Code Playgroud)

示例代码的结果

Sup*_*ito 15

目录:

  1. 特征之间的关系
  2. 想要的图
  3. 为什么要拟合和预测?
  4. 绘制 8 个特征?

特征之间的关系:

表征特征之间“关系”的科学术语是相关性。该领域主要在PCA(主成分分析)期间进行探索。这个想法是,并非所有特征都很重要,或者至少其中一些特征高度相关。将此视为相似性:如果两个特征高度相关,则它们包含相同的信息,因此您可以删除其中一个。使用熊猫这看起来像这样:

import pandas as pd
import seaborn as sns
from pylab import rcParams
import matplotlib.pyplot as plt


def plot_correlation(data):
    '''
    plot correlation's matrix to explore dependency between features 
    '''
    # init figure size
    rcParams['figure.figsize'] = 15, 20
    fig = plt.figure()
    sns.heatmap(data.corr(), annot=True, fmt=".2f")
    plt.show()
    fig.savefig('corr.png')

# load your data 
data  = pd.read_csv('diabetes.csv')

# plot correlation & densities
plot_correlation(data)
Run Code Online (Sandbox Code Playgroud)

输出是以下相关矩阵: 在此处输入图片说明

所以这里 1 表示完全相关,正如预期的那样,对角线都是 1,因为一个特征与其自身完全相关。此外,数字越低,特征的相关性越低。

这里我们需要考虑特征与特征的相关性和结果与特征的相关性。特征之间:更高的相关性意味着我们可以放弃其中之一。但是,特征与结果之间的高度相关性意味着该特征很重要并且包含大量信息。在我们的图中,最后一行代表特征与结果之间的相关性。因此,最高值/最重要的特征是“葡萄糖”(0.47)和“MBI”(0.29)。此外,这两者之间的相关性相对较低(0.22),这意味着它们不相似。

我们可以使用与结果相关的每个特征的密度图来验证这些结果。这并不复杂,因为我们只有两个结果:0 或 1。所以它在代码中看起来像这样:

import pandas as pd
from pylab import rcParams
import matplotlib.pyplot as plt


def plot_densities(data):
    '''
    Plot features densities depending on the outcome values
    '''
    # change fig size to fit all subplots beautifully 
    rcParams['figure.figsize'] = 15, 20

    # separate data based on outcome values 
    outcome_0 = data[data['Outcome'] == 0]
    outcome_1 = data[data['Outcome'] == 1]

    # init figure
    fig, axs = plt.subplots(8, 1)
    fig.suptitle('Features densities for different outcomes 0/1')
    plt.subplots_adjust(left = 0.25, right = 0.9, bottom = 0.1, top = 0.95,
                        wspace = 0.2, hspace = 0.9)

    # plot densities for outcomes
    for column_name in names[:-1]: 
        ax = axs[names.index(column_name)]
        #plt.subplot(4, 2, names.index(column_name) + 1)
        outcome_0[column_name].plot(kind='density', ax=ax, subplots=True, 
                                    sharex=False, color="red", legend=True,
                                    label=column_name + ' for Outcome = 0')
        outcome_1[column_name].plot(kind='density', ax=ax, subplots=True, 
                                     sharex=False, color="green", legend=True,
                                     label=column_name + ' for Outcome = 1')
        ax.set_xlabel(column_name + ' values')
        ax.set_title(column_name + ' density')
        ax.grid('on')
    plt.show()
    fig.savefig('densities.png')

# load your data 
data  = pd.read_csv('diabetes.csv')
names = list(data.columns)

# plot correlation & densities
plot_densities(data)
Run Code Online (Sandbox Code Playgroud)

输出是以下密度图: 在此处输入图片说明

在图中,当绿色和红色曲线几乎相同(重叠)时,这意味着该特征没有将结果分开。在“BMI”的情况下,您可以看到一些分离(两条曲线之间的轻微水平偏移),而在“葡萄糖”中,这更清晰(这与相关值一致)。

=> 结论:如果我们只需要选择 2 个特征,那么 'Glucose' 和 'MBI' 是可以选择的。

想要的图

我对此没有太多要说的,只是该图代表了对 k 最近邻概念的基本解释。它根本不是分类的表示。

为什么要拟合和预测

这是一个基本且重要的机器学习 (ML) 概念。您有一个 dataset=[inputs, associated_outputs] 并且您想构建一个 ML 算法,该算法可以很好地学习将输入与其关联的输出相关联。这是一个两步程序。首先,你训练/教你的算法是如何完成的。在这个阶段,你只需像对待孩子一样给它输入和答案。第二步是测试;现在孩子已经学会了,你想测试她/他。所以你给她/他类似的输入并检查她/他的答案是否正确。现在,你不想给她/他他学到的相同输入,因为即使她/他给出了正确的答案,她/他也可能只是记住了学习阶段的答案(这称为过度拟合),所以她/他什么都没学到。

与算法类似,首先将数据集拆分为训练数据和测试数据。然后,在这种情况下,将训练数据拟合到算法或分类器中。这称为训练阶段。之后你测试你的分类器有多好,以及他是否可以正确地对新数据进行分类。那是测试阶段。根据测试结果,您可以使用不同的评估指标(例如准确性)来评估分类的性能。这里的经验法则是将 2/3 的数据用于训练,1/3 用于测试。

绘制 8 个特征?

简单的答案是不,你不能,如果可以,请告诉我如何。

有趣的答案:可视化 8 维,很容易……想象 n 维,然后让 n=8 或者只是可视化 3-D 并尖叫 8。

合乎逻辑的答案:所以我们生活在物理单词中,我们看到的物体是 3 维的,所以这在技术上是一种限制。但是,您可以将第 4 维可视化为这里的颜色,您也可以使用时间作为第 5 维,并使您的绘图成为动画。@Rohan 在他的答案形状中建议,但他的代码对我不起作用,我看不出这将如何提供算法性能的良好表示。不管怎样,颜色、时间、形状……过了一段时间你用完了这些,你发现自己被卡住了。这是人们进行 PCA 的原因之一。您可以在Dimensionity-reduction下阅读有关问题的这一方面的信息。

那么如果我们在 PCA 之后满足 2 个特征,然后训练、测试、评估和绘图会发生什么?.

那么你可以使用以下代码来实现:

import warnings 
import numpy as np
import pandas as pd
from pylab import rcParams
import matplotlib.pyplot as plt
from sklearn import neighbors
from matplotlib.colors import ListedColormap
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
# filter warnings
warnings.filterwarnings("ignore")

def accuracy(k, X_train, y_train, X_test, y_test):
    '''
    compute accuracy of the classification based on k values 
    '''
    # instantiate learning model and fit data
    knn = KNeighborsClassifier(n_neighbors=k)    
    knn.fit(X_train, y_train)

    # predict the response
    pred = knn.predict(X_test)

    # evaluate and return  accuracy
    return accuracy_score(y_test, pred)

def classify_and_plot(X, y):
    ''' 
    split data, fit, classify, plot and evaluate results 
    '''
    # split data into training and testing set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.33, random_state = 41)

    # init vars
    n_neighbors = 5
    h           = .02  # step size in the mesh

    # Create color maps
    cmap_light = ListedColormap(['#FFAAAA', '#AAAAFF'])
    cmap_bold  = ListedColormap(['#FF0000', '#0000FF'])

    rcParams['figure.figsize'] = 5, 5
    for weights in ['uniform', 'distance']:
        # we create an instance of Neighbours Classifier and fit the data.
        clf = neighbors.KNeighborsClassifier(n_neighbors, weights=weights)
        clf.fit(X_train, y_train)

        # Plot the decision boundary. For that, we will assign a color to each
        # point in the mesh [x_min, x_max]x[y_min, y_max].
        x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
        y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
        xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                             np.arange(y_min, y_max, h))
        Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

        # Put the result into a color plot
        Z = Z.reshape(xx.shape)
        fig = plt.figure()
        plt.pcolormesh(xx, yy, Z, cmap=cmap_light)

        # Plot also the training points, x-axis = 'Glucose', y-axis = "BMI"
        plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold, edgecolor='k', s=20)   
        plt.xlim(xx.min(), xx.max())
        plt.ylim(yy.min(), yy.max())
        plt.title("0/1 outcome classification (k = %i, weights = '%s')" % (n_neighbors, weights))
        plt.show()
        fig.savefig(weights +'.png')

        # evaluate
        y_expected  = y_test
        y_predicted = clf.predict(X_test)

        # print results
        print('----------------------------------------------------------------------')
        print('Classification report')
        print('----------------------------------------------------------------------')
        print('\n', classification_report(y_expected, y_predicted))
        print('----------------------------------------------------------------------')
        print('Accuracy = %5s' % round(accuracy(n_neighbors, X_train, y_train, X_test, y_test), 3))
        print('----------------------------------------------------------------------')


# load your data 
data  = pd.read_csv('diabetes.csv')
names = list(data.columns)

# we only take the best two features and prepare them for the KNN classifier
rows_nbr = 30 # data.shape[0]
X_prime  = np.array(data.iloc[:rows_nbr, [1,5]])
X        = X_prime # preprocessing.scale(X_prime)
y        = np.array(data.iloc[:rows_nbr, 8])

# classify, evaluate and plot results
classify_and_plot(X, y)
Run Code Online (Sandbox Code Playgroud)

这导致使用 weights='uniform' 和 weights='distance' 的决策边界图如下(阅读两者之间的差异请点击此处):

在此处输入图片说明 在此处输入图片说明

注意: x 轴 = '葡萄糖',y 轴 = 'BMI'

改进:

K 值使用 什么 k 值?要考虑多少邻居。低 k 值意味着数据之间的依赖性较小,但大值意味着更长的运行时间。所以这是一种妥协。您可以使用此代码找到 k 的值,从而获得最高的准确度:

best_n_neighbours = np.argmax(np.array([accuracy(k, X_train, y_train, X_test, y_test) for k in range(1, int(rows_nbr/2))])) + 1
print('For best accuracy use k = ', best_n_neighbours)
Run Code Online (Sandbox Code Playgroud)

使用更多数据 因此,当使用所有数据时,除了过度拟合问题之外,您可能会遇到内存问题(就像我所做的那样)。您可以通过预处理您的数据来克服这个问题。将此视为数据的缩放和格式化。在代码中只需使用:

from sklearn import preprocessing 
X = preprocessing.scale(X_prime)
Run Code Online (Sandbox Code Playgroud)

完整代码可以在这个要点中找到

  • @SuperKogito 可能是我加入这个社区以来在 stackoverflow 上读到的最全面、最令人愉快的答案之一。你应该多发帖。 (2认同)

Roh*_*mar 5

尝试这两段简单的代码,都绘制一个带有6个变量的3D图,总是很难绘制出更高维度的数据,但是您可以使用它并检查是否可以对其进行调整以获得所需的邻域图。

第一个是非常直观的,但是它会为您提供随机光线或盒子(取决于您的变量数量),您不能绘制超过6个变量,但是在使用更多尺寸时,它总是对我造成错误,但是您必须具有足够的创造力才能以某种方式使用其他两个变量。当您看到第二段代码时,这将很有意义。

第一段代码

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
X, Y, Z, U, V, W = zip(*df)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.quiver(X, Y, Z, U, V, W)    
ax.set_xlim([-2, 2])
ax.set_ylim([-2, 2])
ax.set_zlim([-2, 2])
ax.legend()
plt.show()
Run Code Online (Sandbox Code Playgroud)

第二段代码

在这里,我使用age&BMI作为数据点的颜色和形状,通过调整此代码可以再次获得6个变量的邻域图,并使用其他两个变量按颜色或形状进行区分。

fig = plt.figure(figsize=(8, 6))
t = fig.suptitle('name_of_your_graph', fontsize=14)
ax = fig.add_subplot(111, projection='3d')

xs = list(df['pregnancies'])
ys = list(df['glucose'])
zs = list(df['bloodPressure'])
data_points = [(x, y, z) for x, y, z in zip(xs, ys, zs)]

ss = list(df['skinThickness'])
colors = ['red' if age_group in range(0,35) else 'yellow' for age_group in list(df['age'])]
markers = [',' if q > 33 else 'x' if q in range(19,32) else 'o' for q in list(df['BMI'])]

for data, color, size, mark in zip(data_points, colors, ss, markers):
    x, y, z = data
    ax.scatter(x, y, z, alpha=0.4, c=color, edgecolors='none', s=size, marker=mark)

ax.set_xlabel('pregnancies')
ax.set_ylabel('glucose')
ax.set_zlabel('bloodPressure')
Run Code Online (Sandbox Code Playgroud)

请发布您的答案。我正在研究类似的问题,可能会有所帮助。如果您无法绘制所有8-D图形,那么您还可以通过每次使用6个不同变量的组合来绘制多个邻域图。