diff --git a/15.md b/15.md index 2cb8725217ba5c46b05ec4f4609a4cdd8d672bc6..1f4becfbd556b7607dd49f996f515853030cd551 100644 --- a/15.md +++ b/15.md @@ -348,3 +348,171 @@ array([ 0.59610766, -0.19065363]) ``` 这非常方便,因为我们现在可以在每行的数据上使用数组操作了。 + +### 只有两个属性时点的距离 + +我们需要做的主要计算是,找出 Alice 的点与其他点之间的距离。 为此,我们需要的第一件事就是计算任意一对点之间的距离。 + +我们如何实现呢? 在二维空间中,这非常简单。 如果我们在坐标`(x0, y0)`处有一个点,而在`(x1, y1)`处有另一个点,则它们之间的距离是: + +![](http://latex.codecogs.com/gif.latex?D%20%3D%20%5Csqrt%7B%28x_0-x_1%29%5E2%20+%20%28y_0-y_1%29%5E2%7D) + +(这是从哪里来的?它来自勾股定理:我们有一个直角三角形,边长为`x0 - x1`和`y0 - y1`,我们想要求出斜边的长度。) + +在下一节中,我们将看到,当存在两个以上的属性时,这个公式有个直接的扩展。 现在,让我们使用公式和数组操作来求出 Alice 和第 3 行病人的距离。 + +```py +patient3 = np.array(ckd_attributes.row(3)) +alice, patient3 +(array([ 0. , 1.1]), array([ 0.59610766, -0.19065363])) +distance = np.sqrt(np.sum((alice - patient3)**2)) +distance +1.4216649188818471 +``` + +我们需要 Alice 和一堆点之间的距离,所以让我们写一个称为距离的函数来计算任意一对点之间的距离。 该函数将接受两个数组,每个数组包含一个点的`(x, y)`坐标。 (记住,那些实际上是患者的血红蛋白和血糖水平。) + +```py +def distance(point1, point2): + """Returns the Euclidean distance between point1 and point2. + + Each argument is an array containing the coordinates of a point.""" + return np.sqrt(np.sum((point1 - point2)**2)) +distance(alice, patient3) +1.4216649188818471 +``` + +我们已经开始建立我们的分类器:距离函数是第一个积木。 现在让我们来研究下一个片段。 + +### 在整个行上使用`apply` + +回想一下,如果要将函数应用于表的列的每个元素,一种方法是调用`table_name.apply(function_name, column_label)`。 当我们在列的每个元素上调用该函数时,它求值为由函数返回值组成的数组。所以数组的每个条目都基于表的相应行。 + +如果使用`apply`而不指定列标签,则整行将传递给该函数。 让我们在一个非常小的表格上,看看它的工作原理,表格包含训练样本中前五个患者的信息。 + +```py +t = ckd_attributes.take(np.arange(5)) +t +``` + +| Hemoglobin | Glucose | +| --- | --- | +| 0.456884 | 0.133751 | +| 1.153 | -0.947597 | +| 0.770138 | -0.762223 | +| 0.596108 | -0.190654 | +| -0.239236 | -0.49961 | + +举个例子,假设对于每个病人,我们都想知道他们最不寻常的属性是多么的不寻常。 具体而言,如果患者的血红蛋白水平远高于其血糖水平,我们想知道它离平均值有多远。 如果她的血糖水平远远高于她的血红蛋白水平,那么我们想知道它离平均值有多远。 + +这与获取两个量的绝对值的最大值是一样的。 为了为特定的行执行此操作,我们可以将行转换为数组并使用数组操作。 + +```py +def max_abs(row): + return np.max(np.abs(np.array(row))) +max_abs(t.row(4)) +0.49961028259186968 +``` + +现在我们可以将`max_abs`应用于`t`表的每一行: + +```py +t.apply(max_abs) +array([ 0.4568837 , 1.15300352, 0.77013762, 0.59610766, 0.49961028]) +``` + +这种使用`apply`的方式帮助我们创建分类器的下一个积木。 + +### Alice 的 K 最近邻 + +如果我们想使用 K 最近邻分类器来划分 Alice,我们必须确定她的 K 个最近邻。 这个过程中的步骤是什么? 假设`k = 5`。 然后这些步骤是: + ++ 步骤 1:的是 Alice 与训练样本中每个点之间的距离。 ++ 步骤 2:按照距离的升序对数据表进行排序。 ++ 步骤 3:取得有序表的前 5 行。 + +步骤 2 和步骤 3 似乎很简单,只要我们有了距离。 那么我们来关注步骤 1。 + +这是爱丽丝: + +```py +alice +array([ 0. , 1.1]) +``` + +我们需要一个函数,它可以求出 Alice 和另一个点之间的距离,它的坐标包含在一行中。 `distance`函数返回任意两点之间的距离,他们的坐标位于数组中。 我们可以使用它来定义`distance_from_alice`,它将一行作为参数,并返回该行与 Alice 之间的距离。 + +```py +def distance_from_alice(row): + """Returns distance between Alice and a row of the attributes table""" + return distance(alice, np.array(row)) +distance_from_alice(ckd_attributes.row(3)) +1.4216649188818471 +``` + +现在我们可以调用`apply`,将`distance_from_alice`函数应用于`ckd_attributes`的每一行,第一步完成了。 + +```py +distances = ckd_attributes.apply(distance_from_alice) +ckd_with_distances = ckd.with_column('Distance from Alice', distances) +ckd_with_distances +``` + +| Class | Hemoglobin | Glucose | Color | Distance from Alice | +| --- | --- | --- | --- | --- | +| 0 | 0.456884 | 0.133751 | gold | 1.06882 | +| 0 | 1.153 | -0.947597 | gold | 2.34991 | +| 0 | 0.770138 | -0.762223 | gold | 2.01519 | +| 0 | 0.596108 | -0.190654 | gold | 1.42166 | +| 0 | -0.239236 | -0.49961 | gold | 1.6174 | +| 0 | -0.0304002 | -0.159758 | gold | 1.26012 | +| 0 | 0.282854 | -0.00527964 | gold | 1.1409 | +| 0 | 0.108824 | -0.623193 | gold | 1.72663 | +| 0 | 0.0740178 | -0.515058 | gold | 1.61675 | +| 0 | 0.83975 | -0.422371 | gold | 1.73862 | + +(省略了 148 行) + +对于步骤 2,让我们以距离的升序对表排序: + +```py +sorted_by_distance = ckd_with_distances.sort('Distance from Alice') +sorted_by_distance +``` + +| Class | Hemoglobin | Glucose | Color | Distance from Alice | +| --- | --- | --- | --- | --- | +| 1 | 0.83975 | 1.2151 | darkblue | 0.847601 | +| 1 | -0.970162 | 1.27689 | darkblue | 0.986156 | +| 0 | -0.0304002 | 0.0874074 | gold | 1.01305 | +| 0 | 0.14363 | 0.0874074 | gold | 1.02273 | +| 1 | -0.413266 | 2.04928 | darkblue | 1.03534 | +| 0 | 0.387272 | 0.118303 | gold | 1.05532 | +| 0 | 0.456884 | 0.133751 | gold | 1.06882 | +| 0 | 0.178436 | 0.0410639 | gold | 1.07386 | +| 0 | 0.00440582 | 0.025616 | gold | 1.07439 | +| 0 | -0.169624 | 0.025616 | gold | 1.08769 | + +(省略了 148 行) + +步骤 3:前五行对应 Alice 的五个最近邻;你可以将五替换为任意正整数。 + +```py +alice_5_nearest_neighbors = sorted_by_distance.take(np.arange(5)) +alice_5_nearest_neighbors +``` + +| Class | Hemoglobin | Glucose | Color | Distance from Alice | +| --- | --- | --- | --- | --- | +| 1 | 0.83975 | 1.2151 | darkblue | 0.847601 | +| 1 | -0.970162 | 1.27689 | darkblue | 0.986156 | +| 0 | -0.0304002 | 0.0874074 | gold | 1.01305 | +| 0 | 0.14363 | 0.0874074 | gold | 1.02273 | +| 1 | -0.413266 | 2.04928 | darkblue | 1.03534 | + +爱丽丝五个最近邻中有三个是蓝点,两个是金点。 所以 5 邻近的分类器会把爱丽丝划分为蓝色:它可能预测爱丽丝有慢性肾病。 + +下面的图片放大了爱丽丝和她五个最近邻。 这两个金点就在红点正下方的圆圈内。 分类器说,爱丽丝更像她身边的三个蓝点。 + +我们正在实现我们的 K 最近邻分类器。 在接下来的两节中,我们将把它放在一起并评估其准确性。 +