《集体智慧编程》学习笔记 第三章

from math import sqrt
from PIL import Image, ImageDraw
import random

def readfile(filename):
	with open(filename) as f:
		lines = [line for line in f]
	colnames = lines[0].strip().split(‘\t‘)[1:]
	rownames = []
	data = []
	for line in lines[1:]:
		p = line.strip().split(‘\t‘) #split返回的是列表
		rownames.append(p[0]) #某行的第一个元素
		data.append([float(x) for x in p[1:]])
	return rownames, colnames, data

def pearson(v1, v2):
	sum1 = sum(v1)
	sum2 = sum(v2)

	sum1Sq = sum(pow(v, 2) for v in v1)
	sum2Sq = sum(pow(v, 2) for v in v2)

	pSum = sum([v1[i] * v2[i] for i in range(len(v1))])

	#皮尔逊相关度
	num = pSum - (sum1 * sum2 / len(v1))
	den = sqrt((sum1Sq - pow(sum1, 2) / len(v1)) * (sum2Sq - pow(sum2, 2) / len(v1)))
	if den == 0:
		return 0
	return 1.0 - num / den #num/den为皮尔逊相关度,相关度越高,为了让距离越近所以用1减去

class bicluster:
	def __init__(self, vec, left=None, right=None, distance=0.0, id=None):
		self.left = left
		self.right = right
		self.vec = vec
		self.id = id
		self.distance = distance

#聚类函数
def hcluster(rows, distance=pearson): #传入的rows是[[],[],[]]类型的
	distances = {}
	currentclustid = -1
	clust = [bicluster(rows[i], id=i) for i in range(len(rows))] #里面是一个一个的类
	while len(clust) > 1:
		lowestpair = (0, 1) #其实是用一个tuple () 做的key 这个参数就是表示距离最小的是哪两个id之间
		closest = distance(clust[0].vec, clust[1].vec)  #distance在该函数定义的时候已经指定了就是pearson函数
		#就是计算第一个和第二个直接的距离

		#遍历所有配对,寻找最小距离
		for i in range(len(clust)):
			for j in range(i+1, len(clust)):
				#用distances缓存各种两个之间的距离
				if (clust[i].id, clust[j].id) not in distances:
					distances[(clust[i].id, clust[j].id)] = distance(clust[i].vec, clust[j].vec)

				d = distances[(clust[i].id, clust[j].id)]
				if d < closest:
					closest = d
					lowestpair = (i, j)

		#lowestoair[0]其实就是上一行的i,clust[i]是一个对象(就是某一行,某个博客),vec[i]是第i个词出现的次数.就是两个最相近的两个点
		#或者说是两个博客类,相对应的各个词出现的次数,取一个中间值
		mergevec = [(clust[lowestpair[0]].vec[i] + clust[lowestpair[1]].vec[i]) / 2.0 for i in range(len(clust[0].vec))]

		#建立新的聚类 就是相当用上面算出来的这个新值于建一个新的博客类
		newcluster = bicluster(mergevec, left=clust[lowestpair[0]],
							   right=clust[lowestpair[1]], distance=closest, id=currentclustid)

		#不在原始集合中的聚类(博客),其id为负数
		currentclustid -= 1
		del clust[lowestpair[1]]
		del clust[lowestpair[0]]
		clust.append(newcluster)
	return clust[0]

	#因为类有left和right 可以递归出原有的节点,重建整个树

def printclust(clust, labels=None,n=0):
	#利用缩进来建立层级布局
	for i in range(n):
		print(‘ ‘)
	if clust.id < 0:
		#负数代表这是一个新的分支
		print(‘-‘)
	else:
		#正数代表这是一个叶节点
		if labels == None:
			print(clust.id)
		else:
			print(labels[clust.id])
	if clust.left != None:
		printclust(clust.left, labels=labels, n=n+1)
	if clust.right != None:
		printclust(clust.right, labels=labels, n=n+1) ##递归找到原有的结点

#printclust(clust, labels=blognames)

def getheight(clust):
	if clust.left == None and clust.right == None:
		return 1
	return getheight(clust.left) + getheight(clust.right)

def getdepth(clust):
	if clust.left == None and clust.right == None:
		return 0
	return max(getdepth(clust.left), getdepth(clust.right)) + clust.distance #clust返回的是最终的一个节点的对象,里面包含了distance

def drawnode(draw, clust, x, y, scaling, labels):
	if clust.id < 0:
		h1 = getheight(clust.left) * 20
		h2 = getheight(clust.right) * 20
		top = y - (h1 + h2) / 2 #左节点
		bottom = y + (h1 + h2) / 2 #右节点
		#线的长度
		ll = clust.distance * scaling
		#聚类到其子节点的垂直线
		draw.line((x, top + h1 / 2, x, bottom - h2 / 2), fill=(255, 0, 0))
		#连接左侧节点的水平线
		draw.line((x, top + h1 / 2, x + ll, top + h1 / 2), fill=(255, 0, 0))
		#连接右侧节点的水平线
		draw.line((x, bottom - h2 / 2, x + ll, bottom - h2 / 2), fill=(255, 0, 0))
		#调用函数绘制左右节点
		drawnode(draw, clust.left, x + ll, top + h1 / 2, scaling, labels)
		drawnode(draw, clust.right, x + ll, bottom - h2 / 2, scaling, labels)
	else:
		#如果这是一个叶节点,则绘制节点的标签
		draw.text((x - 5, y - 7), labels[clust.id], (0, 0, 0))

def drawdendrogram(clust, labels, jpeg=‘clusters.jpg‘):
	h = getheight(clust) * 20
	w = 1200
	depth = getdepth(clust)
	#图片宽度是固定的,依据不同之间的误差,调整距离
	scaling = float(w - 150) / depth

	#新建一个白色背景的图片
	img = Image.new(‘RGB‘, (w, h), (255, 255, 255))
	draw = ImageDraw.Draw(img)
	draw.line((0, h/2, 10, h/2), fill=(255, 0, 0))

	#画第一个节点
	drawnode(draw, clust, 10, (h/2), scaling, labels)
	img.save(jpeg, ‘JPEG‘)

‘‘‘
blognames, words, data = readfile(‘blogdata1.txt‘)
clust = hcluster(data)     #data是[[], []  ]  返回的是最终聚类
drawdendrogram(clust, blognames, jpeg=‘blogclust.jpg‘)
‘‘‘

#列聚类,比如哪些单词会常结合在一起使用

def rotatemetrix(data):
	newdata = []
	for i in range(len(data[0])):
		newrow = [data[j][i] for j in range(len(data))]
		newdata.append(newrow)
	return newdata

‘‘‘
blognames, words, data = readfile(‘blogdata1.txt‘)
radata = rotatemetrix(data)
wordclust = hcluster(radata)
drawdendrogram(wordclust, labels=words, jpeg=‘wordclust.jpg‘)
‘‘‘

def kcluster(rows, diatance=pearson, k=4):  #ROWS就是data, 就是[[][][][]]类型
	#确定每个点的最小值和最大值.从每一行中找出最大值和最小值(就是出现的次数)
	ranges = [(min([row[i] for row in rows]), max([row[i] for row in rows])) for i in range(len(rows[0]))]

	#随机创建K个中心点
	# random()被静态对象random调用,生成(0,1)之间的float 随机点不是一个(x,y) 而是有多少单词 就有多少维 要跟其他点算pearson
	clusters = [[random.random() * (ranges[i][1] - ranges[i][0]) + ranges[i][0] for i in range(len(rows[0]))] for j in range(k)]

	‘‘‘
		for j in range(k):
			for i in range(len(rows[0])):
			t = []
			t.append(random.random() * (ranges[i][1] - ranges[i][1]) + ranges[i][0])
			 表示生成一个最大值与最小值之间的一个数
		clusters = []
		clusters.append(t)
	‘‘‘
	lastmatches = None
	for t in range(100): #为什么要多次迭代。因为下面会合并点,会再次计算聚点和博客之间的距离 100也只是随便给的数 不一定能够
		print("Iteration %d" % t)
		bestmatches = [[] for i in range(k)]

		for j in range(len(rows)):#j代表博客数
			row = rows[j]
			bestmatche = 0 #假设与第一个聚点最近
			for i in range(k):#每一个博客与四个聚点计算,求最近聚点
				d = diatance(clusters[i], row)
				if d < diatance(clusters[bestmatche], row):
					bestmatche = i
			bestmatches[bestmatche].append(j) #bestmatches里面四个list,第一个list就存与第一个聚点相近的博客(比如第一个博客为0)

		if bestmatches == lastmatches:  #这个if 跟for t in range(100)是匹配的
			break  #如果迭代得到结果与上次一样,则整个过程结束
		lastmatches = bestmatches

	  #把所有中心点移到其所有成员的平均位置处

		for i in range(k):
			avgs = [0.0] * len(rows[0])#产生和单词数一样多的0.0 0.0
			if len(bestmatches[i]) > 0:#如果某个聚点中有许多相近的点
				for rowid in bestmatches[i]:#把每个聚点中的博客所属行数取出来
					for m in range(len(rows[rowid])): #把每列
						avgs[m] += rows[rowid][m]#把每行相同列的单词数加在一起
				for j in range(len(avgs)):
					avgs[j] /= len(bestmatches[i]) #某个单词的总数/聚点中博客的个数 算出这个聚点中各种单词的平均数
				clusters[i] = avgs #新聚点是[[],[],[],[]]的 内部的[]就是不同单词的平均个数

	return bestmatches

blognames, words, data = readfile(‘blogdata1.txt‘)
kclust = kcluster(data)
print([blognames[r] for r in kclust[0]])

def scaledown(data, distance=pearson, rate=0.01):
	n = len(data)
	#每一对数据项之间的真实距离
	realdist = [[distance(data[i], data[j]) for j in range(n)] for i in range(0, n)]
	outersum = 0.0

	#随机初始化节点在二维空间中的起始位置
	loc = [[random.random(), random.random()] for i in range(n)]
	fakelist = [[0.0 for j in range(n)] for i in range(n)]

	lasterror = None
	for m in range(1000):
		#找投影后的距离
		for i in range(n): #就是计算两点的距离 生成一个矩阵 x-x的平方 + y-y的平方 再开方
			for j in range(n):
				fakelist[i][j] == sqrt(sum([pow(loc[i][x] - loc[j][x], 2) for x in range(len(loc[i]))]))

		#移动结点
		grad = [[0.0, 0.0] for i in range(n)]

		totalerror = 0
		for k in range(n):
			for j in range(n):
				if j == k : continue
				#误差值等于目标距离与当前距离之间差值的百分比
				errorterm = (fakelist[j][k] - realdist[j][k]) / realdist[j][k]

				#每个结点按误差的比例,按比例偏离或者移向其他节点
				grad[k][0] += ((loc[k][0] - loc[j][0]) / fakelist[j][k]) * errorterm
				grad[k][1] += ((loc[k][0] - loc[j][0]) / fakelist[j][k]) * errorterm

				#记录总的误差值
			totalerror +=abs(errorterm)
		print(totalerror)

		#如果节点移动后情况变的更糟糕,则结束程序
		if lasterror and lasterror < totalerror:
			break
		lasterror = totalerror

		#根据rate参数与grade值相乘的结果,移动每一个点
		for k in range(n):
			loc[k][0] -= rate * grad[k][0]
			loc[k][1] -= rate * grad[k][1]
	return loc

  

时间: 2024-11-04 19:47:23

《集体智慧编程》学习笔记 第三章的相关文章

Linux shell编程学习笔记---第三章

3.1正则表达式基础 正则表达式的主要功能是文本查询和字符串操作.实际上就是字符串过滤. 正则表达式包括普通字符和元字符,元字符主要的,见书中50页描述.特殊的正则表达式^$匹配空行. 正则表达式的扩展 bash shell中的通配,和原来的字符含义完全不一样了..对比区别 3.4grep命令,强大的文本搜索工具

Shell学习笔记——第三章

第三章 /etc/passwd 文件包含一个所有系统用户账户组成的列表,以及一些关于每个用户的基本信息. 例rich:x:501:Rich Blum:/home/rich:/bin/bash 每个条目都有7个数据字段,例使用冒号分隔.系统使用这些字段中的数据向用户分配具体的特性.这些字段分别有 用户名:用户密码(如果密码存储在其他文件中,则为占位符):用户的系统用户ID编号:用户的系统组的ID编号:用户的全名:用户默认的主目录:用户的默认Shell目录: cd 命令 略过 列表命令(ls) 参数

c#高级编程第七版 学习笔记 第三章 对象和类型

第三章 对象和类型 本章的内容: 类和结构的区别 类成员 按值和按引用传送参数 方法重载 构造函数和静态构造函数 只读字段 部分类 静态类 Object类,其他类型都从该类派生而来 3.1 类和结构 类和结构都是创建对象的模板,每个对象都包含数据,并提供了处理和访问数据的方法 结构和类的区别是他们在内存中的存储方式.访问方式(类是存储在堆上的引用类型,而结构是存储在栈上的值类型)和他们的一些特征(如结构不支持继承).较小的数据类型使用结构可提高性能.但在语法上,结构和类非常相似,主要的区别是使用

Java学习笔记—第三章

第三章  标识符.关键字和数据类型 1. Java标识符和关键字:在程序设计中,通常用一个记号对变量.常量.方法和类等进行标识,这个记号称为标识符.Java语言本身使用了一些标识符,这些标识符称为Java关键字,用户在命名标识符时应该避免使用这些Java关键字,否则将导致程序无法进行编译. Java中的关键字如下: abstract 抽象 assert 断言 boolean 布尔 break 中断 byte 字节 catch 捕获 char 字符 class 类 continue 继续 defa

【PMP】Head First PMP 学习笔记 第三章 过程框架

第三章 过程框架 项目中完成的所有工作都由过程构成. 项目中的完成的所有工作都有一个模式(pattern).先计划,再去做.工作时,总是对项目与原先的计划进行比较.如果开始偏离计划,就要由你做出矫正,让一切重新走上正轨.过程框架--过程租和知识领域--正式这一切顺利完成的关键. 分阶段管理 分阶段,项目的每个阶段(phase)都会经过5个过程租,从启动到收尾,项目的多个阶段就会存在各种关联关系 顺序关系(sequenital relationship).多个阶段相继发生并不存在重叠,每个阶段在前

Java学习笔记—第三章 标识符、关键字和数据类型

第三章  标识符.关键字和数据类型 1. Java标识符和关键字:在程序设计中,通常用一个记号对变量.常量.方法和类等进行标识,这个记号称为标识符.Java语言本身使用了一些标识符,这些标识符称为Java关键字,用户在命名标识符时应该避免使用这些Java关键字,否则将导致程序无法进行编译. Java中的关键字如下: abstract 抽象 assert 断言 boolean 布尔 break 中断 byte 字节 catch 捕获 char 字符 class 类 continue 继续 defa

Java Web 学习笔记 第三章 java基础(二)

第三章 java基础(二) 一.转义符 转义字符是"\",通过转义字符,可表示一些特殊的字符. 例如: '\n'  表示回车 '\t'   表示 制表符字符,一个制表符表示向右跳8-10个字符 '\\'   表示\ '\''   表示单引号 '\"'  表示双引号 "\u4e2d"表示unicode编码对应的字符(汉字:中). 二.布尔类型的概念和用法 boolean 类型用来表示肯定或否定两种可能. boolean 常用在分支语句.循环语句中. true

【MySQL】《高性能MySQL》 学习笔记,第三章,服务器性能剖析

第三章:服务器性能剖析 ? 本章将针对如下三个问题进行解答: ? 如何确认服务器是否达到了性能最佳的状态 ? 找出某条语句为什么执行不够快 ? 诊断被用户描述成"停顿","堆积","卡死"的某些间歇性疑难故障 1.性能优化简介: ? 针对性能问题,1000个DBA,有1000个回答.诸如,"QPS","CPU Load","可扩展性"之类. 原则一:我们将性能定义为完成某件任务所需要的时

UNIX环境高级编程学习笔记(第一章UNIX基础知识)

总所周知,UNIX环境高级编程是一本很经典的书,之前我粗略的看了一遍,感觉理解得不够深入. 听说写博客可以提高自己的水平,因此趁着这个机会我想把它重新看一遍,并把每一章的笔记写在博客里面. 我学习的时候使用的平台是Windows+VMware+debian,使用secureCRT来连接(可以实现多个终端连接). 因为第一章是本书大概的描述,所以第一章的我打算写得详细一点,而且书本的原话占的比例会比较多,重点的东西会用粗体显示出来. 1.1  引言 所有操作系统都为他们所运行的程序提供服务.典型的