zoukankan      html  css  js  c++  java
  • 《集体智慧编程》学习笔记 第三章

    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('	')[1:]
    	rownames = []
    	data = []
    	for line in lines[1:]:
    		p = line.strip().split('	') #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
    

      

  • 相关阅读:
    @@IDENTITY 存储过程
    ASP.NET的Cookie和Session
    数据格式设置表达式
    DataTable类(MSDN)
    用静态变量代替appliction
    C cgi url 编码解码问题
    C#视频头操作
    c#网页抓取
    c语言字符串分隔
    CGI c 上传文件
  • 原文地址:https://www.cnblogs.com/caidongzhou/p/5921015.html
Copyright © 2011-2022 走看看