From 682dcba395ee4cd8554a839b3f1efc1f3a91671d Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Thu, 25 Jan 2018 23:07:28 +0800 Subject: [PATCH] ch15. --- 15.md | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 119 insertions(+), 8 deletions(-) diff --git a/15.md b/15.md index f02eedd..2cb8725 100644 --- a/15.md +++ b/15.md @@ -30,7 +30,7 @@ ### 慢性肾病 -我们来浏览一个例子。 我们将使用收集的数据集来帮助医生诊断慢性肾病(CKD)。 数据集中的每一行都代表单个患者,过去接受过治疗并且诊断已知。 对于每个患者,我们都有一组血液测试的测量结果。 我们希望找到哪些测量结果对诊断慢性肾病最有用,并根据他们的血液检查结果,开发一种方法,将未来的患者分类为“CKD”或“无 CKD”。 +我们来浏览一个例子。 我们将使用收集的数据集来帮助医生诊断慢性肾病(CKD)。 数据集中的每一行都代表单个患者,过去接受过治疗并且诊断已知。 对于每个患者,我们都有一组血液测试的测量结果。 我们希望找到哪些测量结果对诊断慢性肾病最有用,并根据他们的血液检查结果,开发一种方法,将未来的患者分类为“CKD”或“非 CKD”。 ```py ckd = Table.read_table('ckd.csv').relabeled('Blood Glucose Random', 'Glucose') @@ -83,7 +83,7 @@ ckd 我们来看两列,(病人的血液中)血红蛋白水平和血糖水平(一天中的随机时间;没有专门为血液测试禁食)。 -我们将绘制一个散点图来显示两个变量之间的关系。 蓝点是 CKD 患者; 金点是无 CKD 的患者。 什么样的医学检验结果似乎表明了 CKD? +我们将绘制一个散点图来显示两个变量之间的关系。 蓝点是 CKD 患者; 金点是非 CKD 的患者。 什么样的医学检验结果似乎表明了 CKD? ```py color_table = Table().with_columns( @@ -113,13 +113,13 @@ show_closest(alice) 因此,我们的最近邻分类器是这样工作的: + 找到训练集中离新点最近的点。 -+ 如果最近的点是“CKD”点,则将新点划分为“CKD”。如果最近的点是“无 CKD”点,则将新点划分为“无 CKD”。 ++ 如果最近的点是“CKD”点,则将新点划分为“CKD”。如果最近的点是“非 CKD”点,则将新点划分为“非 CKD”。 -散点图表明这个最近邻分类器应该相当准确。右下角的点倾向于接受“无 CKD”的诊断,因为他们的最近邻是一个金点。其余的点倾向于接受“CKD”诊断,因为他们的最近邻是蓝点。所以这个例子中,最近邻策略似乎很好地捕捉了我们的直觉。 +散点图表明这个最近邻分类器应该相当准确。右下角的点倾向于接受“非 CKD”的诊断,因为他们的最近邻是一个金点。其余的点倾向于接受“CKD”诊断,因为他们的最近邻是蓝点。所以这个例子中,最近邻策略似乎很好地捕捉了我们的直觉。 ## 决策边界 -有时一种分类器可视化的实用方法是,绘制出分类器预测“CKD”的几种属性,以及预测“无 CKD”的几种。我们最终得到两者之间的边界,边界一侧的点将被划分为“CKD”,而另一侧的点将划分为“无 CKD”。这个边界称为决策边界。每个不同的分类器将有不同的决策边界;决策边界只是一种方法,用于可视化分类器实用什么标准来对点分类。 +有时一种分类器可视化的实用方法是,绘制出分类器预测“CKD”的几种属性,以及预测“非 CKD”的几种。我们最终得到两者之间的边界,边界一侧的点将被划分为“CKD”,而另一侧的点将划分为“非 CKD”。这个边界称为决策边界。每个不同的分类器将有不同的决策边界;决策边界只是一种方法,用于可视化分类器实用什么标准来对点分类。 例如,假设爱丽丝的点坐标是`(0, 1.5)`。注意最近邻是蓝色的。现在尝试减少点的高度(`y`坐标)。你会看到,在`y = 0.95`左右,最近邻从蓝色变为金色。 @@ -137,7 +137,7 @@ show_closest(alice) 对于每个红点,它必须找到训练集中最近的点;它必须将红点的颜色改变为最近邻的颜色。 -结果图显示哪些点将划分为“CKD”(全部为蓝色),或者“无 CKD”(全部为黄金)。 +结果图显示哪些点将划分为“CKD”(全部为蓝色),或者“非 CKD”(全部为黄金)。 决策边界是分类器从将红点转换为蓝色变成金色的地方。 @@ -150,7 +150,7 @@ show_closest(alice) ckd.scatter('White Blood Cell Count', 'Glucose', colors='Color') ``` -如您所见,无 CKD 个体都聚集在左下角。大多数 CKD 患者在该簇的上方或右侧,但不是全部。上图左下角有一些 CKD 患者(分散在金簇中的少数蓝点表示)。这意味着你不能从这两个检测结果确定,某些人是否拥有 CKD。 +如您所见,非 CKD 个体都聚集在左下角。大多数 CKD 患者在该簇的上方或右侧,但不是全部。上图左下角有一些 CKD 患者(分散在金簇中的少数蓝点表示)。这意味着你不能从这两个检测结果确定,某些人是否拥有 CKD。 如果提供爱丽丝的血糖水平和白细胞计数,我们可以预测她是否患有慢性肾病嘛?是的,我们可以做一个预测,但是我们不应该期望它是 100% 准确的。直觉上,似乎存在预测的自然策略:绘制 Alice 在散点图中的位置;如果她在左下角,则预测她没有 CKD,否则预测她有 CKD。 @@ -158,7 +158,7 @@ ckd.scatter('White Blood Cell Count', 'Glucose', colors='Color') 我们可以在计算机上自动化吗?那么,最近邻分类器也是一个合理的选择。花点时间思考一下:它的预测与上述直觉策略的预测相比如何?他们什么时候会不同? -它的预测与我们的直觉策略非常相似,但偶尔会做出不同的预测。特别是,如果爱丽丝的血液检测结果恰好把她放在左下角的一个蓝点附近,那么这个直观的策略就可能预测“无 CKD”,而最近邻的分类器会预测“CKD”。 +它的预测与我们的直觉策略非常相似,但偶尔会做出不同的预测。特别是,如果爱丽丝的血液检测结果恰好把她放在左下角的一个蓝点附近,那么这个直观的策略就可能预测“非 CKD”,而最近邻的分类器会预测“CKD”。 最近邻分类器有一个简单的推广,修正了这个异常。它被称为 K 最近邻分类器。为了预测爱丽丝的诊断,我们不仅仅查看靠近她的一个邻居,而是查看靠近她的三个点,并用这三个点中的每一个点的诊断来预测艾丽丝的诊断。特别是,我们将使用这 3 个诊断中的大部分值作为我们对 Alice 诊断的预测。当然,数字 3 没有什么特别之处:我们可以使用 4 或 5 或更多。 (选择一个奇数通常是很方便的,所以我们不需要处理相等)。一般来说,我们选择一个数字`k`,而我们对 Alice 的预测诊断是基于训练集中最接近爱丽丝的`k`个点。直观来说,这些是血液测试结果与爱丽丝最相似的`k`个患者,因此使用他们的诊断来预测爱丽丝的诊断似乎是合理的。 @@ -237,3 +237,114 @@ plt.ylim(-2, 6); 把测试数据放在这个图上,你可以立刻看到分类器对于几乎所有的点都正确,但也有一些错误。 例如,测试集的一些蓝点落在分类器的金色区域。 尽管存在一些错误,但分类器看起来在测试集上表现得相当好。 假设原始样本是从底层总体中随机抽取的,我们希望分类器在整个总体上具有相似的准确性,因为测试集是从原始样本中随机选取的。 + +## 表的行 + +现在我们对最近邻分类有一个定性的了解,是时候实现我们的分类器了。 + +在本章之前,我们主要处理表格的单列。 但现在我们必须看看一个个体是否“接近”另一个个体。 个体数据包含在表格的行中。 + +那么让我们首先仔细看一下行。 + +这里是原始表格`ckd`,包含慢性肾病患者资料。 + +```py +ckd = Table.read_table('ckd.csv').relabeled('Blood Glucose Random', 'Glucose') +``` + +对应第一个患者的数据在表中第 0 行,与 Python 的索引系统一致。 `Table`的`row`方法将行索引作为其参数来访问行。 + +```py +ckd.row(0) +Row(Age=48, Blood Pressure=70, Specific Gravity=1.0049999999999999, Albumin=4, Sugar=0, Red Blood Cells='normal', Pus Cell='abnormal', Pus Cell clumps='present', Bacteria='notpresent', Glucose=117, Blood Urea=56, Serum Creatinine=3.7999999999999998, Sodium=111, Potassium=2.5, Hemoglobin=11.199999999999999, Packed Cell Volume=32, White Blood Cell Count=6700, Red Blood Cell Count=3.8999999999999999, Hypertension='yes', Diabetes Mellitus='no', Coronary Artery Disease='no', Appetite='poor', Pedal Edema='yes', Anemia='yes', Class=1) + +``` + +行拥有自己的数据类型:它们是行对象。 注意屏幕不仅显示行中的值,还显示相应列的标签。 + +行通常不是数组,因为它们的元素可以是不同的类型。 例如,上面那行的一些元素是字符串(如`'abnormal'`),有些是数字。 所以行不能被转换成数组。 + +但是,行与数组有一些特征。 您可以使用`item`来访问行中的特定元素。 例如,要访问患者 0 的白蛋白水平,我们可以查看上面那行的打印输出中的标签,发现它是第 3 项: + +```py +ckd.row(0).item(3) +4 +``` + +### 将行转换为数组(可能的时候) + +元素都是数字(或都是字符串)的行可以转换为数组。 将行转换为数组可以让我们访问算术运算和其他漂亮的 NumPy 函数,所以它通常很有用。 + +回想一下,在上一节中,我们试图根据血红蛋白和血糖两个属性将患者划分为“CKD”或“非 CKD”,这两个属性都是以标准单位测量的。 + +```py +ckd = Table().with_columns( + 'Hemoglobin', standard_units(ckd.column('Hemoglobin')), + 'Glucose', standard_units(ckd.column('Glucose')), + 'Class', ckd.column('Class') +) + +color_table = Table().with_columns( + 'Class', make_array(1, 0), + 'Color', make_array('darkblue', 'gold') +) +ckd = ckd.join('Class', color_table) +ckd +``` + +| Class | Hemoglobin | Glucose | Color | +| --- | --- | --- | --- | +| 0 | 0.456884 | 0.133751 | gold | +| 0 | 1.153 | -0.947597 | gold | +| 0 | 0.770138 | -0.762223 | gold | +| 0 | 0.596108 | -0.190654 | gold | +| 0 | -0.239236 | -0.49961 | gold | +| 0 | -0.0304002 | -0.159758 | gold | +| 0 | 0.282854 | -0.00527964 | gold | +| 0 | 0.108824 | -0.623193 | gold | +| 0 | 0.0740178 | -0.515058 | gold | +| 0 | 0.83975 | -0.422371 | gold | + +(省略了 148 行) + +下面是两个属性的散点图,以及新患者 Alice 对应的红点。 她的血红蛋白值是 0(即平均值)和血糖为 1.1(即比平均值高 1.1 个 SD)。 + +```py +alice = make_array(0, 1.1) +ckd.scatter('Hemoglobin', 'Glucose', colors='Color') +plots.scatter(alice.item(0), alice.item(1), color='red', s=30); +``` + +为了找到 Alice 点和其他点之间的距离,我们只需要属性的值: + +```py +ckd_attributes = ckd.select('Hemoglobin', 'Glucose') +ckd_attributes +``` + + +| Hemoglobin | Glucose | +| --- | --- | +| 0.456884 | 0.133751 | +| 1.153 | -0.947597 | +| 0.770138 | -0.762223 | +| 0.596108 | -0.190654 | +| -0.239236 | -0.49961 | +| -0.0304002 | -0.159758 | +| 0.282854 | -0.00527964 | +| 0.108824 | -0.623193 | +| 0.0740178 | -0.515058 | +| 0.83975 | -0.422371 | + +(省略了 148 行) + +每行由我们的训练样本中的一个点的坐标组成。 由于行现在只包含数值,因此可以将它们转换为数组。 为此,我们使用函数`np.array`,将任何类型的有序对象(如行)转换为数组。 (我们的老朋友`make_array`用于创建数组,而不是用于将其他类型的序列转换为数组。) + +```py +ckd_attributes.row(3) +Row(Hemoglobin=0.59610766482326683, Glucose=-0.19065363034327712) +np.array(ckd_attributes.row(3)) +array([ 0.59610766, -0.19065363]) +``` + +这非常方便,因为我们现在可以在每行的数据上使用数组操作了。 -- GitLab