2 python kNN示例: 改进约会网站的配对结果

从文本解析数据

  • 已经有文本数据 datingTestSet2.txt.
  • 有1000个样本.
  • 每个样本有三种特征:每年获得的飞行常客里程数, 玩视频游戏所耗时间百分比, 每周消耗的冰激凌公升数.

使用到的函数

主要是文件处理的函数.

  • 函数open()

用于打开一个文件,并返回文件对象.
如果该文件无法被打开,会抛出 OSError.
注意: 使用 open() 函数一定要保证关闭文件对象,即调用 close() 函数.
格式: open(file, mode=‘r’)

  • 方法 f.readlines()

用于读取文件中的所有行,它和调用不指定 size 参数的 read() 函数类似,只不过该函数返回是一个字符串列表,其中每个元素为文件中的一行内容.
和 readline() 函数一样,readlines() 函数在读取每一行时,会连同行尾的换行符一块读取.

f= open("students.txt")
lines = f.readlines()
print(lines)
['James 12 m\n', 'Chris 13 m\n', 'Tom 11 m\n', 'Lucy 12 f']
  • 函数 np.zeros((m,n))

返回mxn大小的全0数组.

  • 字符串的 s.strip() 方法

Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
注意: 该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。
★注意: python中字符串是不可变对象,所以使用strip方法时,返回的是一个新的字符串对象,而不是在原先的字符串上改变。

f= open("students.txt")
lines = f.readlines()
print(lines)

[print(line) for line in lines]
[print(line.strip()) for line in lines]
['James 12 m\n', 'Chris 13 m\n', 'Tom 11 m\n', 'Lucy 12 f']
James 12 m

Chris 13 m

Tom 11 m

Lucy 12 f
James 12 m
Chris 13 m
Tom 11 m
Lucy 12 f
['James 12 m\n', 'Chris 13 m\n', 'Tom 11 m\n', 'Lucy 12 f']
  • 字符串 s.split()方法

split() 通过指定分隔符对字符串进行切片,如果第二个参数 num 有指定值,则分割为 num+1 个子字符串。
语法: str.split(str=“”, num=string.count(str))
返回字符串列表.

line = lines[0]
split_line = line.split()
print(split_line)
['James', '12', 'm']

程序

  • 书上的数据集有三个特征,但是我下载下来的text文件里只有两个特征的数据,所以这里dataSet只提取原数据前两列。
    (缺少 每周消耗的冰激凌公升数)
def file2matrix(file_name):
    """目的:将文本文件变成numpy数组"""
    f = open(file_name)  # 获取文件对象
    lines = f.readlines()  # 按行读取文件,返回每一行组成的字符串列表
    len_data = len(lines)
    data_set = np.zeros((len_data, 2))  # 预分配数据集矩阵
    ind = 0  # 记录行标
    labels = []
    for line in lines:
        line = line.strip('\n')  # 先将行末的换行符去掉
        list_from_line = line.split('\t')  # 按tab隔开将每行字符串变成一个字符串列表
        data_set[ind, :] = list_from_line[:2]  # 将一行中的前两个元素提取为特征
        labels.append(int(list_from_line[-1]))
        ind += 1
    return data_set, labels
dating_mat, dating_labels = file2matrix('datingTestSet2.txt')
print(dating_mat, dating_labels[:10], sep='\n')
[[4.0920000e+04 8.3269760e+00]
 [1.4488000e+04 7.1534690e+00]
 [2.6052000e+04 1.4418710e+00]
 ...
 [2.6575000e+04 1.0650102e+01]
 [4.8111000e+04 9.1345280e+00]
 [4.3757000e+04 7.8826010e+00]]
[3, 2, 1, 1, 1, 1, 3, 3, 1, 3]
  • 直接从text文件里读取的数字是字符串格式
  • 读取出的python list里的数是字符串形式,但是直接转化成numpy array时,会自动帮助转化成数字格式。比如函数中的这一行:
data_set[ind, :] = list_from_line[:2]
  • 以上dataSet数组在读取文件数据前,使用zeros进行预分配了。然后借助index来把文件数据放置dataSet的合适位置。
  • python list 是可变对象,也就是能在原对象上改变这个对象,所以可以append等等操作。
  • numpy array需要添加一行或者一个元素,可以用np.append(),np.row_stack(),np.column_stack(),np.concanate()…

Matplotlib散点图

  • 需要先导入matplotlib模块
  • 导入matplotlib模块中的pyplot,重命名为plt

– Matplotlib是Python的绘图库,其中的pyplot包封装了很多画图的函数。
– Matplotlib.pyplot 包含一系列类似 MATLAB 中绘图函数的相关函数。
– 每个 Matplotlib.pyplot 中的函数会对当前的图像进行一些修改,例如:产生新的图像,在图像中产生新的绘图区域,在绘图区域中画线,给绘图加上标记,等等……
– Matplotlib.pyplot 会自动记住当前的图像和绘图区域,因此这些函数会直接作用在当前的图像上。
来自:https://www.sohu.com/a/343708772_120104204

  • List item
# 画散点图进行数据分析
# 散点图的横纵坐标都是特征
# 使用matplotlib库
fig = plt.figure(1)  # 创建画布对象,并且将编号设置为1
ax = fig.add_subplot(111)  # 创建副画布对象
ax.scatter(dating_mat[:, 0], dating_mat[:, 1], s=5)  # 在ax这个画布上画散点图
plt.show()  # 展示所有画布

figure对象是没有scatter()方法的,ax对象有scatter()、legend()之类的方法。在这应该也是使用add_subplot(111)的原因。

在这里插入图片描述

  • 使用色彩标记不同分类的点

将上面的scatter那一行换成下面加长的:
ax.scatter(dating_mat[:, 0], dating_mat[:, 1], 15 * np.array(dating_labels), 15 * np.array(dating_labels)) # 在ax这个画布上画散点图
详细解释一下:scatter第三个位置默认是参数s,调整点的大小,这里使用dating_labels序列来控制前面dating_mat对应的点的大小。dating_labels是1-3的整数,所以dating_mat里属于不同类别的点会被赋予不同的大小。
scatter第四个位置默认是参数c,调整颜色,同理,属于不同分类的点会被赋予不同颜色。也说明颜色参数c不仅可以接受字符串,也可以接受整数。

在这里插入图片描述

  • 再完善一下,加上标题,坐标轴名称,标签。【默认不支持添加中文标题和轴标题,菜鸟有解决办法,之后补上】
  • 将数据分为三个部分画图,但是在一个坐标上。
marker_label = [1, 2, 3]
for i in range(1, 4):
    index_aux = (np.array(dating_labels) == i)
    ax2.scatter(dating_mat[index_aux, 0], dating_mat[index_aux, 1], label=marker_label[i - 1], s=i * 15)

在这里插入图片描述

分三部分画的时候,会自动给不同的部分添加不同的颜色。

准备数据:归一化数值

  • 根据距离公式,发现里程数对计算结果的影响要比游戏时间多得多,因为它的差值跨度很大,所以需要对数据进行归一化,使得每个特征对结果的影响权重是一样的。(认为每个特征的重要性相同)
  • 归一化公式:(newValue = (oldValue - minValue) / (maxValue- minValue)
  • 注意:归一化是对数据中 相同特征为一组 进行归一化。 表示 不同特征对结果的影响应该是相同的。
  • 定义一个归一化函数来给数据进行归一化。
# 归一化函数
def autoNorm(dataSet):
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    r_num = dataSet.shape[0]
    ranges = maxVals - minVals
    normDataSet = (dataSet - np.tile(minVals, (r_num, 1))) / (np.tile(ranges, (r_num, 1)))
    return normDataSet, ranges, minVals

测试数据

  • 测试分类器的效果。
  • 通常使用错误率进行评估。分类器给出错误结果的次数 / 测试数据的总数
  • 一般只提供已有数据的90%作为训练样本,而10%作为测试样本。
  • 测试数据应该是随机选择的。 但是这个约会数据并没有按照特定目的来排序,所以可以随意选择10%。这里选前10%。
  • 定义一个计数器变量,每次分错就+1.
def datingClassTest():
    # 准备数据
    hoRatiao = 0.1
    dating_mat, labels = file2matrix('datingTestSet2.txt')  # 从文件提取数据
    norm_dating_mat, ranges, min_vals = autoNorm(dating_mat)  # 处理数据,归一化数据
    # 提取测试数据,在总的数据上提取10%,由于数据本来就是随机排序,不需要随机抽取数据进行测试,直接选取前10%
    num_data = dating_mat.shape[0]  # 总的数据数量
    num_test = int(hoRatiao * num_data)  # 要测试的数据数量
    error_count = 0  # 错误计数器
    for i in range(num_test):
        res = classify0(norm_dating_mat[i], norm_dating_mat[num_test:], labels[num_test:], 3)
        '''norm_dating_mat[i]是要分类的向量, norm_dating_mat[num_test:]是训练特征集,labels[num_test:]训练标签集,3是k'''
        print("the classifier came back with %d, the real answer is %d" % (res, labels[i]))
        if not res == labels[i]: error_count += 1  # 如果分错,错误计数器加1
    error_rate = error_count / num_test
    print("the total error rate is : ", error_rate)
    return error_count / num_test

可以修改k值,hoRatiao,观察对错误率有何影响。

使用算法:构建完整可用系统

  • 写一个函数,用户交互型,使得海伦可以直接输入数据,然后返回给她结论。
# 约会网站预测函数,用户交互型
def classifyPerson():
    res_list = [0, '不喜欢', '魅力一般', '极具魅力']
    # 数据
    dating_mat, labels = file2matrix('datingTestSet2.txt')
    # 归一化
    norm_dating_mat = autoNorm(dating_mat)
    # 获取用户数据
    ff_miles = float(input("每年获取的飞行常客里程数?"))
    percenttTats = float(input("玩视频游戏所耗时间百分比?"))
    inX = [ff_miles, percenttTats]
    res = classify0(inX, dating_mat, labels, 3)
    print("你觉得这个人" + res_list[res] + '.')
每年获取的飞行常客里程数?10000
玩视频游戏所耗时间百分比?10
你觉得这个人魅力一般.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值