pythoneps
① python实操:手把手教你用Matplotlib把数据画出来
作者:迈克尔·贝耶勒(Michael Beyeler)
如需转载请联系华章 科技
如果已安装Anaconda Python版本,就已经安装好了可以使用的 Matplotlib。否则,可能要访问官网并从中获取安装说明:
http://matplotlib.org
正如使用np作为 NumPy 的缩写,我们将使用一些标准的缩写来表示 Matplotlib 的引入:
在本书中,plt接口会被频繁使用。
让我们创建第一个绘图。
假设想要画出正弦函数sin(x)的线性图。得到函数在x坐标轴上0≤x<10内所有点的值。我们将使用 NumPy 中的 linspace 函数来在x坐标轴上创建一个从0到10的线性空间,以及100个采样点:
可以使用 NumPy 中的sin函数得到所有x点的值,并通过调用plt中的plot函数把结果画出来:
你亲自尝试了吗?发生了什么吗?有没有什么东西出现?
实际情况是,取决于你在哪里运行脚本,可能无法看到任何东西。有下面几种可能性:
1. 从.py脚本中绘图
如果从一个脚本中运行 Matplotlib,需要加上下面的这行调用:
在脚本末尾调用这个函数,你的绘图就会出现!
2. 从 IPython shell 中绘图
这实际上是交互式地执行Matplotlib最方便的方式。为了让绘图出现,需要在启动 IPython 后使用所谓的%matplotlib魔法命令。
接下来,无须每次调用plt.show()函数,所有的绘图将会自动出现。
3. 从 Jupyter Notebook 中绘图
如果你是从基于浏览器的 Jupyter Notebook 中看这段代码,需要使用同样的%matplotlib魔法命令。然而,也可以直接在notebook中嵌入图形,这会有两种输出选项:
在本书中,将会使用inline选项:
现在再次尝试一下:
上面的命令会得到下面的绘图输出结果:
如果想要把绘图保存下来留作以后使用,可以直接在 IPython 或者 Jupyter Notebook 使用下面的命令保存:
仅需要确保你使用了支持的文件后缀,比如.jpg、.png、.tif、.svg、.eps或者.pdf。
作为本章最后一个测试,让我们对外部数据集进行可视化,比如scikit-learn中的数字数据集。
为此,需要三个可视化工具:
那么开始引入这些包吧:
第一步是载入实际数据:
如果没记错的话,digits应该有两个不同的数据域:data域包含了真正的图像数据,target域包含了图像的标签。相对于相信我们的记忆,我们还是应该对digits稍加 探索 。输入它的名字,添加一个点号,然后按Tab键:digits.<TAB>,这个操作将向我们展示digits也包含了一些其他的域,比如一个名为images的域。images和data这两个域,似乎简单从形状上就可以区分。
两种情况中,第一维对应的都是数据集中的图像数量。然而,data中所有像素都在一个大的向量中排列,而images保留了各个图像8×8的空间排列。
因此,如果想要绘制出一副单独的图像,使用images将更加合适。首先,使用NumPy的数组切片从数据集中获取一幅图像:
这里是从1797个元素的数组中获取了它的第一行数据,这行数据对应的是8×8=64个像素。下面就可以使用plt中的imshow函数来绘制这幅图像:
上面的命令得到下面的输出:
此外,这里也使用cmap参数指定了一个颜色映射。默认情况下,Matplotlib 使用MATLAB默认的颜色映射jet。然而,在灰度图像的情况下,gray颜色映射更有效。
最后,可以使用plt的subplot函数绘制全部数字的样例。subplot函数与MATLAB中的函数一样,需要指定行数、列数以及当前的子绘图索引(从1开始计算)。我们将使用for 循环在数据集中迭代出前十张图像,每张图像都分配到一个单独的子绘图中。
这会得到下面的输出结果:
关于作者:Michael Beyeler,华盛顿大学神经工程和数据科学专业的博士后,主攻仿生视觉计算模型,用以为盲人植入人工视网膜(仿生眼睛),改善盲人的视觉体验。 他的工作属于神经科学、计算机工程、计算机视觉和机器学习的交叉领域。同时他也是多个开源项目的积极贡献者。
本文摘编自《机器学习:使用OpenCV和Python进行智能图像处理》,经出版方授权发布。
② 如何利用cURL和python对服务端和web端进行接口测试
工具描述
cURL是利用URL语法在命令行方式下工作的文件传输工具,是开源爱好者编写维护的免费工具,支持包括Windows、Linux、Mac等数十个操作系统,最新版本为7.27.0,但是我推荐大家使用7.26.0,从这里可以下载7.26.0版本。
以下是官方介绍的翻译: cURL是一个使用URL语法来传输数据的命令行工具,支持DICT,FILE,ftp,FTPS,GOPHER,HTTP,HTTPS,IMAP,IMAPS,LDAP,LDAPS,POP3,POP3S,RTMP,RTSP,SCP,SFTP,SMTP,SMTPS,Telnet和TFTP。 cURL支持SSL证书,HTTP POST,HTTP PUT,FTP上传,HTTP基础表单上传,代理,cookies,用户+密码身份验证(Basic, Digest, NTLM, Negotiate, kerberos...),恢复文件传输,隧道代理等等。
Python是一种面向对象、直译式计算机程序设计语言,由Guido van Rossum于1989年底发明,它的强大和易用就无需多做说明了,在web开发中或者对开发速度要求较高的开发中应用十分广,不过因为属于脚本类语言,它的性能始终比不上C++、C等语言。
本文主要利用实例说明这两款工具的在测试中的部分用途,更多用法留待大家继续探索。
应用场景
使用cURL模拟客户端对服务端进行查询
在进行接口测试时,应该先找开发人员提供接口列表和对应参数,这样测试的时候就可以验证测试方法是否正确,不过如果可以用浏览器模拟操作的话,也可以自己先试试的,后面的例子会提到。
首先使用客户端访问需要测试的服务端接口,用wireshark抓包结果如下:
把查询字符串以multipart方式post数据到服务器的file_health_info.php接口。
Tips:
Windows版的cURL不像Linux或者Mac一样属于系统自带工具,需要下载,如果要在命令提示符下使用就需要跳转到工具所在目录下才能运行,十分麻烦,我们可以直接把这个工具文件放到Windows目录下,这样无论在哪个目录都可以直接使用“curl”命令运行工具了。
cURL默认就是以post方式发送数据的,所以只需要加入multipart方式就可以了,-F在cURL帮助中的解释是:
-F, --form CONTENT Specify HTTP multipart POST data (H)
--form-string STRING Specify HTTP multipart POST data (H)
--ftp-account DATA Account data string (F)
--ftp-alternative-to-user COMMAND String to replace "USER [name]" (F)
--ftp-create-dirs Create the remote dirs if not present (F)
--ftp-method [MULTICWD/NOCWD/SINGLECWD] Control CWD usage (F)
--ftp-pasv Use PASV/EPSV instead of PORT (F)
Specify HTTP multipart POST data正好满足我们的要求,所以模拟的语句是:
curl -F "newmd5=###25016566###d:\test.exe###1###" file_health_info.php
后面的都是参数,测试前就要找开发确认好。
不过因为服务端支持以gzip方式返回数据,所以我们还可以在请求中加入—compressed参数,即是:
curl --compressed -F "newmd5=###25016566###d:\test.exe###1###"file_health_info.php
为了更好判断服务端接口是否正常,除了对返回数据进行判断外,我们还需要对服务端返回的数据包头进行解析,所以还可以在cURL请求中加入-i参数,最终这个测试语句就变为:
curl –i --compressed -F "newmd5=###25016566###d:\test.exe###1###"file_health_info.php
模拟完成后就要考虑判断返回值的事了,我们首先在命令提示符下运行这个语句,看看返回值。
运行以上命令后,返回的数据如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Date: Fri, 24 Aug 2012 07:47:45 GMT
Content-Type: application/xml
Transfer-Encoding: chunked
Connection: keep-alive
Server: 360 web server
Content-Encoding: gzip
<?xml version="1.0" encoding="GBK" ?>
<ret>
<retinfo code="0" msg="Operation success" total="1" success="1" empty="0"
cost="999.92752075195"/>
<softs>
<soft>
<md5></md5>
<sha1></sha1>
<level>4040</level>
<e_level>40.3</e_level>
<size></size>
<soft_name><![CDATA[]]> </soft_name>
<describ><![CDATA[]]></describ>
<file_desc><![CDATA[]]></file_desc>
<upload>0</upload>
<attr_upload>2</attr_upload>
<class><![CDATA[private]]></class>
<malware><![CDATA[cloud.virus]]></malware>
<is_sys_file>0</is_sys_file>
<is_rep>0</is_rep>
<age>0</age>
<pop>0</pop>
</soft>
</softs>
</ret>
数据包包头是常见的,一般来说,我们只要判断包头中含有“HTTP/1.1 200 OK”就可以确定服务端正常返回了数据。另外从内容可以看到这是一个xml格式的数据包,我们只需要判断是否存在关键的字段即可,比如<level>,确定这些之后我们就可以在python中添加如下案例代码了:
首先运行指定的curl命令:
response = os.popen('''curl -i --compressed -F "newmd5=###25016566###d:\test.exe###1###"
然后判断返回值中是否存在我们想要的字段:
self.assertNotEqual(response.find('HTTP/1.1 200 OK'),-1)
self.assertNotEqual(response.find('<level>'),-1)
上面的内容看起来差不多可以了,但实际还不够严谨,因为服务端返回的这些数据是从数据库中获取的,所以我们还需要查询数据库获取指定值,判断是否和数据包中的一致,比如<level>:
首先使用python登录数据库服务器:
conn = MySQLdb.connect(host='172.22.54.31', user='user',passwd='test321',db='cloud')
cursor = conn.cursor()
count = cursor.execute('SELECT plevel FROM file where md5=""')
result = cursor.fetchone()
然后判断返回值中的level是否数据库中的值:
self.assertNotEqual(response.find('''<level>%s</level>'''%result[0]),-1)
测试时除了使用cURL等工具进行模拟,还可以自己用python或其他语言写代码进行post数据,不过当时简单起见所以就选择了用cURL测试。
使用cURL模拟控制台登录
说到模拟登录或者模拟点击,很多同学可能直接联想到QTP等模拟界面操作的工具,事实上这种工具有一个很大的弊端是太依赖控件,如果界面控件变了,那么可能整个脚本就无效了,现在的程序设计都倾向界面逻辑分离,这样修改界面时就不会动到底下的功能接口,开发人员可以随时修改界面控件,如果还是采取QTP等模拟点击的方法测试,结果可能是事倍功半的,如果测试时直接点用接口就可以避免这种问题。
下面的例子是模拟控制台登录的,登录URL为:
index.php?r=site/login ,首先使用浏览器登录一次,看看实际效果如何。
Tips:
测试php或其他web程序时建议使用chrome,因为它自带的Developer Tools十分好用,当然firefox或者IE9也有类似的工具,看个人习惯吧。按F12打开工具,选择Network标签页,然后输入用户名、密码点击登录,这时Network下方会显示登录过程中浏览器想控制台请求的所有数据,包括请求类型、表单数据等,这些是我们模拟操作的数据来源。
从上图可以获取几个重点信息:请求URL、请求类型、数据类型、数据内容、并且支持gzip压缩等。我们用curl模拟如下:
curl -i --compressed cloud/index.php?r=site/login -d “username=admin” -d “userpass=admin”
使用-i和—compressed的原因再上一个例子已经说了,这里不再赘述,重点是后面的-d,它在官方帮助的解释是:
-d, --data DATA HTTP POST data (H)
--data-ascii DATA HTTP POST ASCII data (H)
--data-binary DATA HTTP POST binary data (H)
--data-urlencode DATA HTTP POST data url encoded (H)
--delegation STRING GSS-API delegation permission
--digest Use HTTP Digest Authentication (H)
--disable-eprt Inhibit using EPRT or LPRT (F)
--disable-epsv Inhibit using EPSV (F)
而我们从Developer Tools返回的数据已经知道,返回数据的格式是
“application/x-www-form-urlencoded”,所以很明显需要使用-d的HTTP POST data url encoded特性。不过格式中还有关键字form,莫非也支持-F参数登录,试试:
curl -i --compressed cloud/index.php?r=site/login -F “username=admin” -F “userpass=admin”,果然成功,呵呵~
也许有同学有疑问,为何不模拟warnsetup、refer,这是因为测试时发现登录界面只需要用户名和密码就够了,refer 用于记录来源网页,在这里用处不大,warnsetup只是用于验证登录码的,这个已经去掉了。
接下来要验证数据,我们在命令提示符中运行上面的命令,返回结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 302 Moved Temporarily
Date: Fri, 24 Aug 2012 08:29:07 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/5.4.3
Set-Cookie: PHPSESSID=; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: PHPSESSID=; path=/
Location: ./index.php
Server: 360 web server
很奇怪吧,乍一看控制台没有返回任何有用信息,不过里面那句HTTP/1.1 302 Moved Temporarily是不是特别眼熟?这个特殊的302(表示http重定向)正是我们在Developer Tools看到的状态值,看到这个已经可以80%确定我们登录已经成功了,更重要的是返回包中的内容:Location: ./index.php,这说明控制台已经通知请求端跳转到index.php,如果登录失败控制台会继续停留在当前登录界面的,我们只需要修改curl语句中的用户名或者密码就知道了,这种情况下返回包就是登录界面的php源码,这里不再赘述。
使用cURL以get方式测试控制台搜索接口
测试方法同上面差不多,首先使用chrome打开控制台进入对应页面,在搜索框中输入“test.exe”,点击搜索,使用developer tools抓包,内容如下:
cURL模拟get方式发送数据的参数是-G,了解这个后面要模拟就很简单了:
curl -i --compressed -G “cloud/index.php?r=file_cloud/api/search&filename=&bg=0&lm=19 ”
其中每个参数的含义要分别了解:filename表示输入的关键字,bg表示数据库第一行,lm表示共显示19行数据。这个接口返回的数据如下:
{"rows":[{"level":"10","plevel":"10","id":"20","md5":"","soft_name":"","soft_desc":"","file_desc":"","is_sys_file":"127","size":"41984","is_rep":"0","file_name":"apihex86.d
ll","file_version":"6.1.7600.16385","proct_version":"6.1.7600.16385","right":"\u00a9 Microsoft Corporation. All rights reserved.","lang":"0","org_name":"YXBpaGV4ODYuZGxs","sign_name":"TWljcm9zb2Z0IFdpbmRvd3M=","company_name":"Microsof
t Corporation","update_time":"2012-07-31 18:14:27","create_time":"2012-07-31 18:14:27","creator_mid":""}],"count":"1"}
分别是文件属性和文件信息,这涉及到两个数据库表,所以验证数据需要用python从两张表中获取对应信息。
从第一张表获取文件属性,比如只获取plevel:
SELECT plevel FROM `file` where md5=""
从第二张表获取文件信息,比如只获取company_name:
SELECT company_name FROM file_info where md5=""
Python代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
conn = MySQLdb.connect(host='%s'% self.host, user='user',passwd='test321',db='cloud')
cursor = conn.cursor()
count = cursor.execute('SELECT plevel FROM `file` where md5=""')
result = cursor.fetchone()
self.assertNotEqual(response.find(str(result[0])),-1)
count = cursor.execute('SELECT company_name FROM file_info where md5=""')
result1 = cursor.fetchone()
self.assertNotEqual(response.find(result1[0]),-1)
当然严谨的测试案例是每一个属性字段都需要进行查找和匹配,这里就不再赘述了。
后记:这种接口测试用到的无非就是curl、python的unitest而已,可能有的人会疑问,为什么用curl而不用py自带的pycurl或者request之类,我觉得只要能满足需求,能够用现成的就用现成的,怎么简单怎么来。搞一堆代码维护起来也麻烦啊,现在qa行业跳槽也比较频繁,万一换了个人看不懂你的代码怎么办?呵呵
③ ipo模型python有什么
IPO(InputProcessingOutput)模式,即输入数据→处理数据→输出结果。“IPO”模式,为影响人生InfluenceLife、职前课堂Pre-employmentClass、户外拓展OutdoorExpand”三大子品牌英文首字母缩写。“IPO”模式并非英文字母的简单缩写,https://right.bdstatic.com/vcg/creative/.eps其智慧在于七彩鸟对大学生真实贴切的了解:“I”-“P”-“O”体现了逐步提升的过程,即“影响人生”关注心灵世界的成长,发现自我,影响自我;“职前课堂”关注职业素养的培养,完善自我,实现自我;“户外拓展”关注真实体验与团队意识,感受自我"
④ python用turtle作图能保存吗
用turtle画完图后使用如下代码保存
ts = turtle.getscreen()
ts.getcanvas().postscript(file="pic.eps")
然后可以再用ps等软件来转换为常用的jpg等格式
⑤ python怎么判断两条线段相交
先判断两条线段是否不平行(最好同时判断是否有交点并且不平行,因为浮点运算不精确),然后计算两条线段的交点。以下是C语言代码:
#include<stdio.h>
#include<math.h>
#define eps 1e-8
#define zero(x) (((x)>0?(x):-(x))<eps)
struct point{double x,y;};
//计算交叉乘积(P1-P0)x(P2-P0)
double xmult(point p1,point p2,point p0){
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
//判点是否在线段上,包括端点
int dot_online_in(point p,point l1,point l2){
return zero(xmult(p,l1,l2))&&(l1.x-p.x)*(l2.x-p.x)<eps&&(l1.y-p.y)*(l2.y-p.y)<eps;
}
//判两点在线段同侧,点在线段上返回0
int same_side(point p1,point p2,point l1,point l2){
return xmult(l1,p1,l2)*xmult(l1,p2,l2)>eps;
}
//判两直线平行
int parallel(point u1,point u2,point v1,point v2){
return zero((u1.x-u2.x)*(v1.y-v2.y)-(v1.x-v2.x)*(u1.y-u2.y));
}
//判三点共线
int dots_inline(point p1,point p2,point p3){
return zero(xmult(p1,p2,p3));
}
//判两线段相交,包括端点和部分重合
int intersect_in(point u1,point u2,point v1,point v2){
if (!dots_inline(u1,u2,v1)||!dots_inline(u1,u2,v2))
return !same_side(u1,u2,v1,v2)&&!same_side(v1,v2,u1,u2);
return dot_online_in(u1,v1,v2)||dot_online_in(u2,v1,v2)||dot_online_in(v1,u1,u2)||dot_online_in(v2,u1,u2);
}
//计算两线段交点,请判线段是否相交(同时还是要判断是否平行!)
point intersection(point u1,point u2,point v1,point v2){
point ret=u1;
double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))
/((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
ret.x+=(u2.x-u1.x)*t;
ret.y+=(u2.y-u1.y)*t;
return ret;
}
int main(void)
{
point u1,u2,v1,v2,ans;
printf("请输入线段1的两个端点:\n");
scanf("%lf%lf%lf%lf",&u1.x,&u1.y,&u2.x,&u2.y);
printf("请输入线段2的两个端点:\n");
scanf("%lf%lf%lf%lf",&v1.x,&v1.y,&v2.x,&v2.y);
if (parallel(u1,u2,v1,v2)||!intersect_in(u1,u2,v1,v2)){
printf("无交点!\n");
}
else{
ans=intersection(u1,u2,v1,v2);
printf("交点为:(%lf,%lf)",ans.x,ans.y);
}
return 0;
}
⑥ kmeans算法用Python怎么实现
1、从Kmeans说起
Kmeans是一个非常基础的聚类算法,使用了迭代的思想,关于其原理这里不说了。下面说一下如何在matlab中使用kmeans算法。
创建7个二维的数据点:
复制代码 代码如下:
x=[randn(3,2)*.4;randn(4,2)*.5+ones(4,1)*[4 4]];
使用kmeans函数:
复制代码 代码如下:
class = kmeans(x, 2);
x是数据点,x的每一行代表一个数据;2指定要有2个中心点,也就是聚类结果要有2个簇。 class将是一个具有70个元素的列向量,这些元素依次对应70个数据点,元素值代表着其对应的数据点所处的分类号。某次运行后,class的值是:
复制代码 代码如下:
2
2
2
1
1
1
1
这说明x的前三个数据点属于簇2,而后四个数据点属于簇1。 kmeans函数也可以像下面这样使用:
复制代码 代码如下:
>> [class, C, sumd, D] = kmeans(x, 2)
class =
2
2
2
1
1
1
1
C =
4.0629 4.0845
-0.1341 0.1201
sumd =
1.2017
0.2939
D =
34.3727 0.0184
29.5644 0.1858
36.3511 0.0898
0.1247 37.4801
0.7537 24.0659
0.1979 36.7666
0.1256 36.2149
class依旧代表着每个数据点的分类;C包含最终的中心点,一行代表一个中心点;sumd代表着每个中心点与所属簇内各个数据点的距离之和;D的
每一行也对应一个数据点,行中的数值依次是该数据点与各个中心点之间的距离,Kmeans默认使用的距离是欧几里得距离(参考资料[3])的平方值。
kmeans函数使用的距离,也可以是曼哈顿距离(L1-距离),以及其他类型的距离,可以通过添加参数指定。
kmeans有几个缺点(这在很多资料上都有说明):
1、最终簇的类别数目(即中心点或者说种子点的数目)k并不一定能事先知道,所以如何选一个合适的k的值是一个问题。
2、最开始的种子点的选择的好坏会影响到聚类结果。
3、对噪声和离群点敏感。
4、等等。
2、kmeans++算法的基本思路
kmeans++算法的主要工作体现在种子点的选择上,基本原则是使得各个种子点之间的距离尽可能的大,但是又得排除噪声的影响。 以下为基本思路:
1、从输入的数据点集合(要求有k个聚类)中随机选择一个点作为第一个聚类中心
2、对于数据集中的每一个点x,计算它与最近聚类中心(指已选择的聚类中心)的距离D(x)
3、选择一个新的数据点作为新的聚类中心,选择的原则是:D(x)较大的点,被选取作为聚类中心的概率较大
4、重复2和3直到k个聚类中心被选出来
5、利用这k个初始的聚类中心来运行标准的k-means算法
假定数据点集合X有n个数据点,依次用X(1)、X(2)、……、X(n)表示,那么,在第2步中依次计算每个数据点与最近的种子点(聚类中心)的
距离,依次得到D(1)、D(2)、……、D(n)构成的集合D。在D中,为了避免噪声,不能直接选取值最大的元素,应该选择值较大的元素,然后将其对应
的数据点作为种子点。
如何选择值较大的元素呢,下面是一种思路(暂未找到最初的来源,在资料[2]等地方均有提及,笔者换了一种让自己更好理解的说法):
把集合D中的每个元素D(x)想象为一根线L(x),线的长度就是元素的值。将这些线依次按照L(1)、L(2)、……、L(n)的顺序连接起来,组成长
线L。L(1)、L(2)、……、L(n)称为L的子线。根据概率的相关知识,如果我们在L上随机选择一个点,那么这个点所在的子线很有可能是比较长的子
线,而这个子线对应的数据点就可以作为种子点。下文中kmeans++的两种实现均是这个原理。
3、python版本的kmeans++
在http://rosettacode.org/wiki/K-means%2B%2B_clustering 中能找到多种编程语言版本的Kmeans++实现。下面的内容是基于python的实现(中文注释是笔者添加的):
复制代码 代码如下:
from math import pi, sin, cos
from collections import namedtuple
from random import random, choice
from import
try:
import psyco
psyco.full()
except ImportError:
pass
FLOAT_MAX = 1e100
class Point:
__slots__ = ["x", "y", "group"]
def __init__(self, x=0.0, y=0.0, group=0):
self.x, self.y, self.group = x, y, group
def generate_points(npoints, radius):
points = [Point() for _ in xrange(npoints)]
# note: this is not a uniform 2-d distribution
for p in points:
r = random() * radius
ang = random() * 2 * pi
p.x = r * cos(ang)
p.y = r * sin(ang)
return points
def nearest_cluster_center(point, cluster_centers):
"""Distance and index of the closest cluster center"""
def sqr_distance_2D(a, b):
return (a.x - b.x) ** 2 + (a.y - b.y) ** 2
min_index = point.group
min_dist = FLOAT_MAX
for i, cc in enumerate(cluster_centers):
d = sqr_distance_2D(cc, point)
if min_dist > d:
min_dist = d
min_index = i
return (min_index, min_dist)
'''
points是数据点,nclusters是给定的簇类数目
cluster_centers包含初始化的nclusters个中心点,开始都是对象->(0,0,0)
'''
def kpp(points, cluster_centers):
cluster_centers[0] = (choice(points)) #随机选取第一个中心点
d = [0.0 for _ in xrange(len(points))] #列表,长度为len(points),保存每个点离最近的中心点的距离
for i in xrange(1, len(cluster_centers)): # i=1...len(c_c)-1
sum = 0
for j, p in enumerate(points):
d[j] = nearest_cluster_center(p, cluster_centers[:i])[1] #第j个数据点p与各个中心点距离的最小值
sum += d[j]
sum *= random()
for j, di in enumerate(d):
sum -= di
if sum > 0:
continue
cluster_centers[i] = (points[j])
break
for p in points:
p.group = nearest_cluster_center(p, cluster_centers)[0]
'''
points是数据点,nclusters是给定的簇类数目
'''
def lloyd(points, nclusters):
cluster_centers = [Point() for _ in xrange(nclusters)] #根据指定的中心点个数,初始化中心点,均为(0,0,0)
# call k++ init
kpp(points, cluster_centers) #选择初始种子点
# 下面是kmeans
lenpts10 = len(points) >> 10
changed = 0
while True:
# group element for centroids are used as counters
for cc in cluster_centers:
cc.x = 0
cc.y = 0
cc.group = 0
for p in points:
cluster_centers[p.group].group += 1 #与该种子点在同一簇的数据点的个数
cluster_centers[p.group].x += p.x
cluster_centers[p.group].y += p.y
for cc in cluster_centers: #生成新的中心点
cc.x /= cc.group
cc.y /= cc.group
# find closest centroid of each PointPtr
changed = 0 #记录所属簇发生变化的数据点的个数
for p in points:
min_i = nearest_cluster_center(p, cluster_centers)[0]
if min_i != p.group:
changed += 1
p.group = min_i
# stop when 99.9% of points are good
if changed <= lenpts10:
break
for i, cc in enumerate(cluster_centers):
cc.group = i
return cluster_centers
def print_eps(points, cluster_centers, W=400, H=400):
Color = namedtuple("Color", "r g b");
colors = []
for i in xrange(len(cluster_centers)):
colors.append(Color((3 * (i + 1) % 11) / 11.0,
(7 * i % 11) / 11.0,
(9 * i % 11) / 11.0))
max_x = max_y = -FLOAT_MAX
min_x = min_y = FLOAT_MAX
for p in points:
if max_x < p.x: max_x = p.x
if min_x > p.x: min_x = p.x
if max_y < p.y: max_y = p.y
if min_y > p.y: min_y = p.y
scale = min(W / (max_x - min_x),
H / (max_y - min_y))
cx = (max_x + min_x) / 2
cy = (max_y + min_y) / 2
print "%%!PS-Adobe-3.0\n%%%%BoundingBox: -5 -5 %d %d" % (W + 10, H + 10)
print ("/l {rlineto} def /m {rmoveto} def\n" +
"/c { .25 sub exch .25 sub exch .5 0 360 arc fill } def\n" +
"/s { moveto -2 0 m 2 2 l 2 -2 l -2 -2 l closepath " +
" gsave 1 setgray fill grestore gsave 3 setlinewidth" +
" 1 setgray stroke grestore 0 setgray stroke }def")
for i, cc in enumerate(cluster_centers):
print ("%g %g %g setrgbcolor" %
(colors[i].r, colors[i].g, colors[i].b))
for p in points:
if p.group != i:
continue
print ("%.3f %.3f c" % ((p.x - cx) * scale + W / 2,
(p.y - cy) * scale + H / 2))
print ("\n0 setgray %g %g s" % ((cc.x - cx) * scale + W / 2,
(cc.y - cy) * scale + H / 2))
print "\n%%%%EOF"
def main():
npoints = 30000
k = 7 # # clusters
points = generate_points(npoints, 10)
cluster_centers = lloyd(points, k)
print_eps(points, cluster_centers)
main()
上述代码实现的算法是针对二维数据的,所以Point对象有三个属性,分别是在x轴上的值、在y轴上的值、以及所属的簇的标识。函数lloyd是
kmeans++算法的整体实现,其先是通过kpp函数选取合适的种子点,然后对数据集实行kmeans算法进行聚类。kpp函数的实现完全符合上述
kmeans++的基本思路的2、3、4步。