1 import pandas as pd
  2 import numpy as np
  3 import matplotlib.pyplot as plt
  4 import os
  5 from sklearn.neighbors import KNeighborsClassifier  # knn分类
  6 
  7 
  8 def build_data(file_path):
  9     """
 10     加载并处理数据
 11     :param file_path:文件夹路径
 12     :return: 数据集
 13     """
 14     # 获取文件名称
 15     file_name_list = os.listdir(file_path)
 16     print("file_name_list:\n", file_name_list)
 17 
 18     # 加载每一个文件中的内容
 19     # for循环加载
 20     # 大数组
 21     data_arr = np.zeros(shape=(len(file_name_list), 1025))
 22     for file_index, file_name in enumerate(file_name_list):
 23         # 获取文件序号 文件名称
 24         # print("file_index:", file_index)
 25         # print("file_name:", file_name)
 26         # 获取每一个文件的文件内容
 27         file_content = np.loadtxt(file_path + "/" + file_name, dtype=np.str)
 28         # print("file_content:\n", file_content)
 29         # 单个样本
 30         single_sample_feture = np.zeros(shape=(len(file_content), 32))
 31         for file_content_num, file_content_data in enumerate(file_content):
 32             # print("file_content_num:", file_content_num)
 33             # print("file_content_data:", file_content_data)
 34             # 将每一个元素转化 数值类型 0 1 0 1
 35             # 法1
 36             # li = [int(i) for i in file_content_data]
 37             # print(li)
 38             # map函数
 39             li = list(map(int, file_content_data))
 40             # print(li)
 41             single_sample_feture[file_content_num, :] = li
 42         # print(single_sample_feture)
 43         # 将样本的二维数组展开成一维 作为样本的特征值
 44         single_data_arr_feature = single_sample_feture.ravel()
 45         # 将单个样本的特征值加到data_arr
 46         data_arr[file_index, :1024] = single_data_arr_feature
 47 
 48         # 获取目标值
 49         target = file_name.split("_")[0]
 50 
 51         # 将目标值添加到数组中
 52         data_arr[file_index, 1024] = target
 53 
 54     # print("data_arr:\n",data_arr)
 55     # print("data_arr 的形状:\n",data_arr.shape)
 56 
 57     return data_arr
 58 
 59 
 60 def save_data(file_data, file_name):
 61     """
 62     保存数据
 63     :param file_data: 数据
 64     :param file_name: 文件名称
 65     :return: None
 66     """
 67     # 如果data 文件不存在,就创建,如果存在,则不执行
 68     if not os.path.exists("./data"):
 69         os.makedirs("./data")
 70     # 保存文件
 71     np.save("./data/" + file_name, file_data)
 72 
 73 
 74 def load_data():
 75     """
 76     加载数据
 77     :return:train test
 78     """
 79     train = np.load("./data/train_data.npy")
 80     test = np.load("./data/test_data.npy")
 81 
 82     return train, test
 83 
 84 
 85 def distance(v1, v2):
 86     """
 87     计算距离
 88     :param v1:点1
 89     :param v2: 点2
 90     :return: 距离dist
 91     """
 92     # 法1
 93     # v1 是矩阵 将矩阵转化数组,再进行降为1维
 94     # v1 = v1.A[0]
 95     # print(v1)
 96     # sum_ = 0
 97     # for i in range(v1.shape[0]):
 98     #     sum_ += (v1[i] - v2[i]) ** 2
 99     # dist = np.sqrt(sum_)
100     # print(dist)
101     # 法2
102     dist = np.sqrt(np.sum(np.power((v1 - v2), 2)))
103     return dist
104 
105 
106 def knn_owns(train, test, k):
107     """
108     knn识别手写字
109     :param train: 训练集
110     :param test: 测试集
111     :param k: 邻居个数
112     :return: 准确率
113     """
114     true_num = 0
115     # 计算每一个测试样本与每一个训练样本的距离
116     for i in range(test.shape[0]):
117         # 构建数组 来保存 每一个测试样本与所有训练样本的距离
118         arr_dist = np.zeros(shape=(train.shape[0], 1))
119         for j in range(train.shape[0]):
120             dist = distance(test[i, :1024], train[j, :1024])
121             arr_dist[j, 0] = dist
122         # print(arr_dist)
123         # 将距离与训练集的目标值 组合起来
124         mutile_arr = np.concatenate((arr_dist, train[:, 1024].reshape((-1, 1))), axis=1)
125         # 因为数组排序不是整个样本一块动---排序---dataframe
126         res_df = pd.DataFrame(data=mutile_arr, columns=["dist", "target"])
127         # print(res_df)
128         # 按照距离进行升序排序
129         y_predict = res_df.sort_values(by="dist")["target"].head(k).mode()[0]
130         # print("y_predict:", y_predict)
131         if test[i, 1024] == y_predict:
132             true_num += 1
133     # 准确率
134     score = true_num / test.shape[0]
135 
136     return score
137 
138 
139 def show_res(score_list, k_list):
140     """
141     结果展示
142     :param score_list: 准确率列表
143     :param k_list: k列表
144     :return: None
145     """
146     # 1、创建画布
147     plt.figure()
148     # 修改RC参数,来让其支持中文
149     plt.rcParams['font.sans-serif'] = 'SimHei'
150     plt.rcParams['axes.unicode_minus'] = False
151     # 2、绘图
152     plt.plot(k_list, score_list, color='r', linestyle=':', linewidth=1.2, marker="*", markersize=7, markerfacecolor='b',
153              markeredgecolor='g')
154     # 增加标题
155     plt.title("随着k的不同,准确率的变化趋势")
156     # 增加横轴、纵轴名称
157     plt.xlabel("k值")
158     plt.ylabel("准确率")
159     # 增加横轴刻度
160     plt.xticks(k_list)
161     # 标注
162     for i, j in zip(k_list, score_list):
163         plt.text(i, j, "%.2f" % j, horizontalalignment='center')
164     # 保存图片
165     plt.savefig("./准确率变化走势图_sklearn.png")
166     # 3、展示
167     plt.show()
168 
169 
170 def knn_owns_yh(train, test, k):
171     """
172     优化之后的Knn算法
173     :param train: 训练集
174     :param test: 测试集
175     :param k:邻居个数
176     :return: 准确率
177     """
178     # 最终的训练集中心
179     data = np.zeros(shape=(10, 1025))
180     # 取训练集的各个中心来代替训练集
181     for i in range(10):
182         # i 0 1 2 .. 9
183         bool_index = train[:, 1024] == i
184         # 使用bool数组
185         data[i, :] = train[bool_index, :].mean(axis=0)
186 
187     print("data:\n",data)
188 
189     # 训练
190     true_num = 0
191     # 计算每一个测试样本与每一个训练样本的距离
192     for i in range(test.shape[0]):
193         # 构建数组 来保存 每一个测试样本与所有训练样本的距离
194         arr_dist = np.zeros(shape=(data.shape[0], 1))
195         for j in range(data.shape[0]):
196             dist = distance(test[i, :1024], data[j, :1024])
197             arr_dist[j, 0] = dist
198         # print(arr_dist)
199         # 将距离与训练集的目标值 组合起来
200         mutile_arr = np.concatenate((arr_dist, data[:, 1024].reshape((-1, 1))), axis=1)
201         # 因为数组排序不是整个样本一块动---排序---dataframe
202         res_df = pd.DataFrame(data=mutile_arr, columns=["dist", "target"])
203         # print(res_df)
204         # 按照距离进行升序排序
205         y_predict = res_df.sort_values(by="dist")["target"].head(k).mode()[0]
206         # print("y_predict:", y_predict)
207         if test[i, 1024] == y_predict:
208             true_num += 1
209     # 准确率
210     score = true_num / test.shape[0]
211 
212     return score
213 
214 
215 
216 
217 def main():
218     """
219     主函数
220     :return:
221     """
222     # # 1、加载并处理数据
223     # train = build_data("./trainingDigits")
224     # test = build_data("./testDigits")
225     #
226     # print("train:\n",train)
227     # print("train的形状:\n",train.shape)
228     #
229     # print("test:\n", test)
230     # print("test的形状:\n", test.shape)
231     #
232     # # 2、保存训练集与测试集数据
233     # save_data(train,"train_data")
234     # save_data(test,"test_data")
235 
236     # 1、加载数据
237     train, test = load_data()
238     print("train:\n", train)
239     print("test:\n", test)
240 
241     # 2、优化版 的knn
242     k = 1
243     score = knn_owns_yh(train, test, k)
244 
245     print("准确率:\n",score)
246 
247     # 2、自实现knn算法
248     # score_list = []
249     # k_list = [5, 6, 7, 8, 9, 10]
250     # # 自实现knn原理识别手写字
251     # for k in k_list:
252     #     score = knn_owns(train, test, k)
253     #
254     #     score_list.append(score)
255     # print("score_list:\n", score_list)
256 
257     # 使用sklearn 中的knn算法进行手写字识别
258     # for k in k_list:
259     #     # (1)初始化算法实例
260     #     knn = KNeighborsClassifier(n_neighbors=k)
261     #     # (2) 训练数据
262     #     knn.fit(train[:, :1024], train[:, 1024])
263     #     # (3) 进行预测
264     #     y_predict = knn.predict(test[:, :1024])
265     #
266     #     # 获取准确率
267     #     score = knn.score(test[:, :1024], test[:, 1024])
268     #
269     #     print("预测值 y_predict:\n", y_predict)
270     #     score_list.append(score)
271 
272     # 3、结果展示
273     # show_res(score_list, k_list)
274 
275 
276 if __name__ == '__main__':
277     main()