机器学习算法系列之K近邻算法

本系列机器学习的文章打算从机器学习算法的一些理论知识、python实现该算法和调一些该算法的相应包来实现。

目录

K近邻算法

一、K近邻算法原理

k近邻算法

通俗解释

近邻距离的度量

k值的选择

KNN最近邻分类算法的过程

 总结

二、利用K近邻算法实现约会网站的匹配预测

三、sklearn实现鸢尾花的种类预测


K近邻算法

一、K近邻算法原理

k近邻算法

k近邻法(k-nearest neighbor)是一种基于回归和分类的算法。k近邻法的输入为实例的特征向量,对应于特征空间中的点;输出为实例的类别,可以取多类。

通俗解释

可以简单粗暴的认为是:K个最近的邻居,当K=1时,算法便成了最近邻算法,即寻找最近的那个邻居。

用官方的话来说,所谓K近邻算法,即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例(也就是上面所说的K个邻居),这K个实例的多数属于某个类,就把该输入实例分类到这个类中。

 

 

 

如上图所示,有两类不同的样本数据,分别用蓝色的小正方形和红色的小三角形表示,而图正中间的那个绿色的圆所标示的数据则是待分类的数据。也就是说,现在,我们不知道中间那个绿色的数据是从属于哪一类(蓝色小正方形or红色小三角形),KNN就是解决这个问题的。

如果K=3,绿色圆点的最近的3个邻居是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。

如果K=5,绿色圆点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。

 于此我们看到,当无法判定当前待分类点是从属于已知分类中的哪一类时,我们可以依据统计学的理论看它所处的位置特征,衡量它周围邻居的权重,而把它归为(或分配)到权重更大的那一类。这就是K近邻算法的核心思想。

 

近邻距离的度量

K近邻算法的核心在于找到实例点的邻居,这个时候,问题就接踵而至了,如何找到邻居,邻居的判定标准是什么,用什么来度量。这一系列问题便是下面要讲的一些距离度量表示法。

1.欧式距离,最常见的两点之间的距离表示方法,又称为欧几里得度量,

它定义于欧几里得空间中,如点 x = (x1,...,xn) 和 y = (y1,...,yn) 之间的距离为:

二维平面上两点a(x1,y1)与b(x2,y2)间的欧氏距离:

三维空间两点a(x1,y1,z1)与b(x2,y2,z2)间的欧氏距离:

推广到n维:也可以表示成向量的形式:

 

在k近邻算法中通常用到的距离度量方视为欧式距离。

2.曼哈顿距离:在欧几里得空间的固定直角坐标系上两点所形成的线段对轴产生的投影的距离总和。例如在平面上,坐标(x1, y1)的点P1与坐标(x2, y2)的点P2的曼哈顿距离为: ,要注意的是,曼哈顿距离依赖座标系统的转度,而非系统在座标轴上的平移或映射。

二维平面两点a(x1,y1)与b(x2,y2)间的曼哈顿距离:

两个n维向量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的曼哈顿距离:

 

3.切比雪夫距离

在平面几何中,若二点p及q的直角坐标系坐标为(x1,y1)及(x2,y2),则切比雪夫距离为:

两个n维向量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的切比雪夫距离:

这里就介绍这三种度量巨距离的方法,其他度量距离的方法还有闵可夫斯基距离、标准化欧式距离、马氏距离等。

k值的选择

在上面那幅图中我们可以看到,当k值选择为3时,带归类的那个样本就被归类为红色三角形一类;当k值选择为5时就被归类为蓝色正方形类。所以不同的k值对模型的归类影响也会不一样,这里需要选择一个最恰当的k值使得模型能够更好的预测未知样本。

一般而言,如果选择较小的k值,就相当于用较小的领域中的训练实例进行预测,“学习”近似误差会减小,只有与输入实例较近或者相似的训练实例才会对预测结果起作用,与此同时带来的问题是“学习”的误差估计会增大,换句话说:k值的减小就意味着整体模型会变得复杂,容易发生过拟合。

如果选择较大的k值,就相当于用较大的领域中的训练实例进行预测,其优点是可以减少学习的估计误差,但缺点是学习的近似误差会变大。这时候,与输入实例较远(不相似)的训练实例也会起预测作用,使预测发生错误,且k值的增大也意味着整体的模型变得简单。例如,当k=n时,无论输入什么实例,其预测结果都会是训练实例中最多的那一类了。完全失去了预测作用。

在实际应用中,K值一般取一个比较小的数值,例如采用交叉验证法(简单来说,就是一部分样本做训练集,一部分做测试集)来选择最优的K值。
 

KNN最近邻分类算法的过程

  1. 计算测试样本和训练样本中每个样本点的距离(常见的距离度量有欧式距离,马氏距离等);
  2. 对上面所有的距离值进行排序;
  3. 选前 k 个最小距离的样本;
  4. 根据这 k 个样本的标签进行投票,得到最后的分类类别;

K近邻法的实现:kd树

实现k近邻法时,主要考虑的问题是如何对训练数据进行快速k近邻搜索

Kd-树是K-dimension tree的缩写,是对数据点在k维空间(如二维(x,y),三维(x,y,z),k维(x1,y,z..))中划分的一种数据结构,主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索)。。

首先必须搞清楚的是,k-d树是一种空间划分树,说白了,就是把整个空间划分为特定的几个部分,然后在特定空间的部分内进行相关搜索操作。想像一个三维空间,kd树按照一定的划分规则把这个三维空间划分了多个空间,如下图所示:

 kd树算法构建流程:

举例:

6个二维数据点{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)}构建kd树的具体步骤为:

  1. 确定:split域=x。具体是:6个数据点在x,y维度上的数据方差分别为39,28.63,所以在x轴上方差更大,故split域值为x;
  2. 确定:Node-data = (7,2)。具体是:根据x维上的值将数据排序,6个数据的中值(所谓中值,即中间大小的值)为7,所以Node-data域位数据点(7,2)。这样,该节点的分割超平面就是通过(7,2)并垂直于:split=x轴的直线x=7;
  3. 确定:左子空间和右子空间。具体是:分割超平面x=7将整个空间分为两部分:x<=7的部分为左子空间,包含3个节点={(2,3),(5,4),(4,7)};另一部分为右子空间,包含2个节点={(9,6),(8,1)};
  4. 如上算法所述,kd树的构建是一个递归过程,我们对左子空间和右子空间内的数据重复根节点的过程就可以得到一级子节点(5,4)和(9,6),同时将空间和数据集进一步细分,如此往复直到空间中只包含一个数据点。

与此同时,经过对上面所示的空间划分之后,我们可以看出,点(7,2)可以为根结点,从根结点出发的两条红粗斜线指向的(5,4)和(9,6)则为根结点的左右子结点,而(2,3),(4,7)则为(5,4)的左右孩子(通过两条细红斜线相连),最后,(8,1)为(9,6)的左孩子(通过细红斜线相连)。如此,便形成了下面这样一棵k-d树:

 

 总结

1、k近邻法三要素:距离度量、k值的选择和分类决策规则.常用的距离度量是欧氏距离及更一般的Lp距离. k值小时,k近邻模型更复杂; k值大时,k近邻模型更简单.k值的选择反映了对近似误差与估计误差之间的权衡,通常由交叉验证选择最优的k.常用的分类决策规则是多数表决,对应于经验风险最小化。

2、k近邻模型对应于基于训练数据集对特征空间的-一个划分,k近邻法中,当训练集、距离度量、k值及分类决策规则确定后,其结果唯一-确定。

3、k近邻法的实现需要考虑如何快速搜索k个最近邻点,kd树是一种便于对k维空间中的数据进行快速检索的数据结构。kd 树是二叉树,表示对k维空间的一个划分,其每个结点对应于k维空间划分中的一个超矩形区域。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。


二、利用K近邻算法实现约会网站的匹配预测

背景:一个经常使用约会网站寻找适合自己的对象的海伦,但是依旧没有从中找到自己喜欢的人。经过一番总结,她发现自己曾经交往过三种类型的人:不喜欢的人、魅力一般的人、极具魅力的人。此外海伦还收集了一些约会网站未曾记录的数据信息。

数据信息:第一列:每年获得的飞行常客里程数;第二列:玩游戏耗时的百分比;第三列:每周消耗的奶茶升数。

第四列:喜欢程度(didntLike、smallDoses、largeDoses)

飞行常客里程数:飞行常客计划(也称:飞行常客奖励计划)是航空公司给忠实乘客的一种奖励,普遍的形式是:乘客们通过这个计划累计自己的飞行里程,并使用这些里程来兑换免费的机票、商品和服务以及其他类似贵宾休息室或优先值机之类的特权。

40920   8.326976   0.953952   largeDoses
14488  7.153469   1.673904   smallDoses
26052  1.441871   0.805124   didntLike
75136  13.147394  0.428964   didntLike
38344  1.669788   0.134296   didntLike
72993  10.141740  1.032955   didntLike
35948  6.830792   1.213192   largeDoses
42666  13.276369  0.543880   largeDoses
67497  8.631577   0.749278   didntLike
35483  12.273169  1.508053   largeDoses
50242  3.723498   0.831917   didntLike
63275  8.385879   1.669485   didntLike
5569   4.875435   0.728658   smallDoses
51052  4.680098   0.625224   didntLike
77372  15.299570  0.331351   didntLike
43673  1.889461   0.191283   didntLike
61364  7.516754   1.269164   didntLike
69673  14.239195  0.261333   didntLike
15669  0.000000   1.250185   smallDoses
28488  10.528555  1.304844   largeDoses
6487   3.540265   0.822483   smallDoses

代码:

# -*- coding: utf-8 -*-

from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt
import time
import numpy as np
import operator


"""
函数说明:kNN算法,分类器

Parameters:
    inX - 用于分类的数据(测试集)
    dataSet - 用于训练的数据(训练集)(n*1维列向量)
    labels - 分类标准(n*1维列向量)
    k - kNN算法参数,选择距离最小的k个点

Returns:
    sortedClassCount[0][0] - 分类结果

"""


def classify0(inX, dataSet, labels, k):
    # numpy函数shape[0]返回dataSet的行数
    dataSetSize = dataSet.shape[0]
    # 将inX重复dataSetSize次并排成一列
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    # 二维特征相减后平方(用diffMat的转置乘diffMat)
    sqDiffMat = diffMat ** 2
    # sum()所有元素相加,sum(0)列相加,sum(1)行相加
    sqDistances = sqDiffMat.sum(axis=1)
    # 开方,计算出距离
    distances = sqDistances ** 0.5
    # argsort函数返回的是distances值从小到大的--索引值
    sortedDistIndicies = distances.argsort()
    # 定义一个记录类别次数的字典
    classCount = {}
    # 选择距离最小的k个点
    for i in range(k):
        # 取出前k个元素的类别
        voteIlabel = labels[sortedDistIndicies[i]]
        # 字典的get()方法,返回指定键的值,如果值不在字典中返回0
        # 计算类别次数
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    # python3中用items()替换python2中的iteritems()
    # key = operator.itemgetter(1)根据字典的值进行排序
    # key = operator.itemgetter(0)根据字典的键进行排序
    # reverse降序排序字典
    sortedClassCount = sorted(classCount.items(),
                              key=operator.itemgetter(1), reverse=True)
    # 返回次数最多的类别,即所要分类的类别
    return sortedClassCount[0][0]


"""
函数说明:打开解析文件,对数据进行分类,1代表不喜欢,2代表魅力一般,3代表极具魅力

Parameters:
    filename - 文件名

Returns:
    returnMat - 特征矩阵
    classLabelVector - 分类label向量

"""


def file2matrix(filename):
    # 打开文件
    fr = open(filename)
    # 读取文件所有内容
    arrayOlines = fr.readlines()
    # 得到文件行数
    numberOfLines = len(arrayOlines)
    # 返回的NumPy矩阵numberOfLines行,3列
    returnMat = np.zeros((numberOfLines, 3))
    # 创建分类标签向量
    classLabelVector = []
    # 行的索引值
    index = 0
    # 读取每一行
    for line in arrayOlines:
        # 去掉每一行首尾的空白符,例如'\n','\r','\t',' '
        line = line.strip()
        # 将每一行内容根据'\t'符进行切片,本例中一共有4列
        listFromLine = line.split('\t')
        # 将数据的前3列进行提取保存在returnMat矩阵中,也就是特征矩阵
        returnMat[index, :] = listFromLine[0:3]
        # 根据文本内容进行分类1:不喜欢;2:一般;3:喜欢
        if listFromLine[-1] == 'didntLike':
            classLabelVector.append(1)
        elif listFromLine[-1] == 'smallDoses':
            classLabelVector.append(2)
        elif listFromLine[-1] == 'largeDoses':
            classLabelVector.append(3)
        index += 1
    # 返回标签列向量以及特征矩阵
    return returnMat, classLabelVector


"""
函数说明:可视化数据

Parameters:
    datingDataMat - 特征矩阵
    datingLabels - 分类Label

Returns:
    None

"""


def showdatas(datingDataMat, datingLabels):
    # 设置汉字格式为14号简体字
    font = FontProperties(fname=r"C:\Windows\Fonts\simsun.ttc", size=14)
    # 将fig画布分隔成1行1列,不共享x轴和y轴,fig画布的大小为(13,8)
    # 当nrows=2,ncols=2时,代表fig画布被分为4个区域,axs[0][0]代表第一行第一个区域
    fig, axs = plt.subplots(nrows=2, ncols=2, sharex=False, sharey=False, figsize=(13, 8))
    # 获取datingLabels的行数作为label的个数
    # numberOfLabels = len(datingLabels)
    # label的颜色配置矩阵
    LabelsColors = []
    for i in datingLabels:
        # didntLike
        if i == 1:
            LabelsColors.append('black')
        # smallDoses
        if i == 2:
            LabelsColors.append('orange')
        # largeDoses
        if i == 3:
            LabelsColors.append('red')
    # 画出散点图,以datingDataMat矩阵第一列为x,第二列为y,散点大小为15, 透明度为0.5
    axs[0][0].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 1], color=LabelsColors, s=15, alpha=.5)
    # 设置标题,x轴label, y轴label
    axs0_title_text = axs[0][0].set_title(u'每年获得的飞行常客里程数与玩视频游戏所消耗时间占比', FontProperties=font)
    axs0_xlabel_text = axs[0][0].set_xlabel(u'每年获得的飞行常客里程数', FontProperties=font)
    axs0_ylabel_text = axs[0][0].set_ylabel(u'玩视频游戏所消耗时间占比', FontProperties=font)
    plt.setp(axs0_title_text, size=9, weight='bold', color='red')
    plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black')
    plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black')
    # 画出散点图,以datingDataMat矩阵第一列为x,第三列为y,散点大小为15, 透明度为0.5
    axs[0][1].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 2], color=LabelsColors, s=15, alpha=.5)
    # 设置标题,x轴label, y轴label
    axs1_title_text = axs[0][1].set_title(u'每年获得的飞行常客里程数与每周消费的冰淇淋公升数', FontProperties=font)
    axs1_xlabel_text = axs[0][1].set_xlabel(u'每年获得的飞行常客里程数', FontProperties=font)
    axs1_ylabel_text = axs[0][1].set_ylabel(u'每周消费的冰淇淋公升数', FontProperties=font)
    plt.setp(axs1_title_text, size=9, weight='bold', color='red')
    plt.setp(axs1_xlabel_text, size=7, weight='bold', color='black')
    plt.setp(axs1_ylabel_text, size=7, weight='bold', color='black')
    # 画出散点图,以datingDataMat矩阵第二列为x,第三列为y,散点大小为15, 透明度为0.5
    axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=LabelsColors, s=15, alpha=.5)
    # 设置标题,x轴label, y轴label
    axs2_title_text = axs[1][0].set_title(u'玩视频游戏所消耗时间占比与每周消费的冰淇淋公升数', FontProperties=font)
    axs2_xlabel_text = axs[1][0].set_xlabel(u'玩视频游戏所消耗时间占比', FontProperties=font)
    axs2_ylabel_text = axs[1][0].set_ylabel(u'每周消费的冰淇淋公升数', FontProperties=font)
    plt.setp(axs2_title_text, size=9, weight='bold', color='red')
    plt.setp(axs2_xlabel_text, size=7, weight='bold', color='black')
    plt.setp(axs2_ylabel_text, size=7, weight='bold', color='black')
    # 设置图例
    didntLike = mlines.Line2D([], [], color='black', marker='.', markersize=6, label='didntLike')
    smallDoses = mlines.Line2D([], [], color='orange', marker='.', markersize=6, label='smallDoses')
    largeDoses = mlines.Line2D([], [], color='red', marker='.', markersize=6, label='largeDoses')
    # 添加图例
    axs[0][0].legend(handles=[didntLike, smallDoses, largeDoses])
    axs[0][1].legend(handles=[didntLike, smallDoses, largeDoses])
    axs[1][0].legend(handles=[didntLike, smallDoses, largeDoses])
    # 显示图片
    plt.show()


"""
函数说明:对数据进行归一化

Parameters:
    dataSet - 特征矩阵

Returns:
    normDataSet - 归一化后的特征矩阵
    ranges - 数据范围
    minVals - 数据最小值

"""


def autoNorm(dataSet):
    # 获取数据的最小值
    minVals = dataSet.min(0)
    # 获取数据的最大值
    maxVals = dataSet.max(0)
    # 最大值和最小值的范围
    ranges = maxVals - minVals
    # shape(dataSet)返回dataSet的矩阵行列数
    normDataSet = np.zeros(np.shape(dataSet))
    # numpy函数shape[0]返回dataSet的行数
    m = dataSet.shape[0]
    # 原始值减去最小值(x-xmin)
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    # 差值处以最大值和最小值的差值(x-xmin)/(xmax-xmin)
    normDataSet = normDataSet / np.tile(ranges, (m, 1))
    # 归一化数据结果,数据范围,最小值
    return normDataSet, ranges, minVals


"""
函数说明:分类器测试函数

Parameters:
    None

Returns:
    normDataSet - 归一化后的特征矩阵
    ranges - 数据范围
    minVals - 数据最小值

"""


def datingClassTest():
    # 打开文件名
    filename = "datingTestSet.txt"
    # 将返回的特征矩阵和分类向量分别存储到datingDataMat和datingLabels中
    datingDataMat, datingLabels = file2matrix(filename)
    # 取所有数据的10% hoRatio越小,错误率越低
    hoRatio = 0.10
    # 数据归一化,返回归一化数据结果,数据范围,最小值
    normMat, ranges, minVals = autoNorm(datingDataMat)
    # 获取normMat的行数
    m = normMat.shape[0]
    # 10%的测试数据的个数
    numTestVecs = int(m * hoRatio)
    # 分类错误计数
    errorCount = 0.0
    for i in range(numTestVecs):
        # 前numTestVecs个数据作为测试集,后m-numTestVecs个数据作为训练集
        # k选择label数+1(结果比较好)
        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], \
                                     datingLabels[numTestVecs:m], 4)
        print("分类结果:%d\t真实类别:%d" % (classifierResult, datingLabels[i]))
        if classifierResult != datingLabels[i]:
            errorCount += 1.0
    print("错误率:%f%%" % (errorCount / float(numTestVecs) * 100))


"""
函数说明:通过输入一个人的三围特征,进行分类输出

Parameters:
    None

Returns:
    None

"""


def classifyPerson():
    # 输出结果
    resultList = ['不喜欢', '有些喜欢', '非常喜欢']
    # 三维特征用户输入
    income = float(input("每年获得的飞行常客里程数:"))
    percentTats = float(input("玩视频游戏所消耗时间百分比:"))
    milkTea = float(input("每周消费的奶茶公升数:"))
    # 打开的文件名
    filename = "datingTestSet.txt"
    # 打开并处理数据
    datingDataMat, datingLabels = file2matrix(filename)
    # 训练集归一化
    normMat, ranges, minVals = autoNorm(datingDataMat)
    # 生成NumPy数组,测试集
    inArr = np.array([income,percentTats , milkTea])
    # 测试集归一化
    norminArr = (inArr - minVals) / ranges
    # 返回分类结果
    classifierResult = classify0(norminArr, normMat, datingLabels, 4)
    # 打印结果
    print("你可能%s这个人" % (resultList[classifierResult - 1]))


"""
函数说明:main函数

Parameters:
    None

Returns:
    None

"""


def main():
    # 获取程序运行时间
    start = time.clock()
    # 打开文件的名称
    filename = "datingTestSet.txt"
    # 打开并处理数据
    datingDataMat, datingLabels = file2matrix(filename)
    # 训练集归一化
    normDataset, ranges, minVals = autoNorm(datingDataMat)
    datingClassTest()
    # print(normDataset)
    # print(ranges)
    # print(minVals)
    showdatas(datingDataMat, datingLabels)
    classifyPerson()
    # print(datingDataMat)
    # print(datingLabels)
    # 创建数据集
    # group, labels = createDataSet()
    # 测试集
    # test = [100,100]
    # kNN分类
    # test_class = classify0(test, group, labels, 3)
    # 打印分类结果
    # print(test_class)
    end = time.clock()
    # 打印程序运行时间
    print('Running time: %f Seconds' % (end - start))


if __name__ == '__main__':
    main()

程序运行结果:

可以看到当预测出这个对象可能海伦将会非常喜欢(毕竟条件确实比较好,hhhh) 


三、sklearn实现鸢尾花的种类预测

 

数据集介绍

在Sklearn机器学习包中,集成了各种各样的数据集,包括前面的糖尿病数据集,这里引入的是鸢尾花卉(Iris)数据集,它是很常用的一个数据集。鸢尾花有三个亚属,分别是山鸢尾(Iris-setosa)、变色鸢尾(Iris-versicolor)和维吉尼亚鸢尾(Iris-virginica)。

该数据集一共包含4个特征变量,1个类别变量。共有150个样本,iris是鸢尾植物,这里存储了其萼片和花瓣的长宽,共4个属性,鸢尾植物分三类。如表所示:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
from pandas.plotting import scatter_matrix
import mglearn
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
iris_dataset = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris_dataset['data'], iris_dataset['target'], random_state=0)
iris_dataframe = pd.DataFrame(X_train, columns=iris_dataset.feature_names)
# 利用DataFrame创建散点图矩阵,按照y_train着色
grr = scatter_matrix(iris_dataframe, c=y_train, figsize=(15, 15), marker='o',
                     hist_kwds={'bins': 20}, s=60, alpha=.8, cmap=mglearn.cm3)
plt.show()
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
X_new = np.array([[5, 2.9, 1, 0.2]])
print("X_new: {}".format(X_new))
prediction = knn.predict(X_new)
print("Prediction: {}".format(prediction))
print("Predicted target name: {}".format(iris_dataset['target_names'][prediction]))
# 模型评价
print("Test set score: {:.2f}".format(knn.score(X_test, y_test)))
输出结果
X_new: [[5.  2.9 1.  0.2]]
Prediction: [0]
Predicted target name: ['setosa']
Test set score: 0.97

可以看出预测的花的结果为'setosa'(山鸢尾),且在测试集上的准确率为97%。

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值