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

Plot k-Nearest-Neighbor graph with 8 features?

我是机器学习的新手,想使用 k-nearest-Neighbor-methodPythonScikit 设置一个小示例.

转换和拟合数据工作正常,但我无法弄清楚如何绘制图表来显示被 "neighborhood".

包围的数据点

我正在使用的 dataset 看起来像这样:

所以有 8 个特征,加上一个 "outcome" 列。

根据我的理解,我得到一个数组,显示所有数据点的 euclidean-distances,使用来自 Scikitkneighbors_graph。 所以我的第一次尝试是 "simply" 绘制我从该方法得到的矩阵。像这样:

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()

但是,结果图并没有真正可视化数据点之间的预期关系。

所以我尝试调整您可以在关于 Scikit、Iris_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))

但是,这段代码根本不起作用,我不明白为什么。它永远不会终止,所以我不会收到任何我可以处理的错误。我的电脑在等待几分钟后就崩溃了。

代码正在努力处理的行是 Z = clf.predict(np.c_[xx.ravel(), yy.ravel()] )部分

所以我的问题是:

首先,我不明白为什么我需要 fitpredict 来绘制邻居图。欧几里得距离是否足以绘制所需的图形? (所需的图表看起来有点像这样:有两种颜色表示是否患有糖尿病;箭头等不是必需的;照片来源:this tutorial)。

我在 code/why 中的错误在哪里 预测部分 崩溃了?

有没有办法用所有特征绘制数据?我知道我不能有 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()

试试这两个简单的代码,都绘制了一个包含 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()

第二段代码

这里我使用年龄和 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')

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

Table 的内容:

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

特征之间的关系:

表征特征之间 "relationship" 的科学术语是 correlation. This area is mostly explored during PCA (Principal Component Analysis). The idea is that not all your features are important or at least some of them are highly correlated. Think of this as similarity: if two features are highly correlated so they embody the same information and consequently you can drop one of them. Using pandas 这看起来像这样:

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)

输出是以下相关矩阵:

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

这里我们需要考虑feature-to-feature相关性和outcome-to-feature相关性。特征之间:更高的相关性意味着我们可以放弃其中一个。然而,特征与结果之间的高度相关性意味着该特征很重要并且包含大量信息。在我们的图表中,最后一行表示特征与结果之间的相关性。因此,最高值/最重要的特征是 'Glucose' (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)

输出为以下密度图:

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

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

想要的图

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

为什么要拟合和预测

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

与算法类似,您首先将数据集拆分为训练数据和测试数据。在这种情况下,然后将训练数据放入算法或分类器中。这称为训练阶段。之后你测试你的分类器有多好以及他是否可以正确分类新数据。那就是测试阶段。根据测试结果,您可以使用不同的 evaluation-metrics 等准确度来评估分类的性能。这里的经验法则是使用 2/3 的数据进行训练,1/3 的数据进行测试。

绘制 8 个特征?

简单的回答 不可以,如果可以,请告诉我怎么做。

有趣的答案: 可视化 8 个维度,这很容易......想象一下 n-dimensions 然后让 n=8 或者只是可视化 3-D 和对着它尖叫 8。

合乎逻辑的答案: 所以我们生活在物理世界中,我们看到的 objects 是三维的,所以这在技术上是一种极限。但是,您可以将第 4 维可视化为 here you can also use the time as your 5th dimension and make your plot an animation. @Rohan suggested in his answer shapes but his code did not work for me, and I do not see how that would provide a good representation of the algorithm performance. Anyway, colors, time, shapes ... after a while you run out of those and you find yourself stuck. This is one of the reasons people do PCA. You can read about this aspect of the problem under dimensionality-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)

这导致使用权重='uniform' 和权重='distance' 的以下决策边界图(阅读两者之间的差异 here):

注意: x-axis = 'Glucose', y-axis = '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)

正在使用矿石数据 因此,当使用所有数据时,您可能 运行 遇到内存问题(就像我所做的那样),而不是过度拟合问题。您可以通过 pre-processing 您的数据来克服这个问题。将此视为数据的缩放和格式设置。在代码中只需使用:

from sklearn import preprocessing 
X = preprocessing.scale(X_prime)

完整代码可以在gist

中找到