用 Processing 进行数据可视化.docx
《用 Processing 进行数据可视化.docx》由会员分享,可在线阅读,更多相关《用 Processing 进行数据可视化.docx(17页珍藏版)》请在冰豆网上搜索。
用Processing进行数据可视化
用Processing进行数据可视化,第1部分:
语言和环境简介
虽然很多开源项目的初衷都是为现有的应用程序构建替代方案,但是仍有大量的项目彰显了创新性。
Processing就是其中的一个。
Processing在2001年诞生于麻省理工学院(MIT)的媒体实验室,主创者为BenFry和CaseyReas,当然还有来自CarnegieMellon、洛杉矶的加利福尼亚大学以及迈阿密大学等的贡献。
Processing的最初目标是开发图形的sketchbook和环境,用来形象地教授计算机科学的基础知识。
之后,它逐渐演变成了可用于创建图形可视化专业项目的一种环境。
如今,围绕它已经形成了一个专门的社区,致力于构建各种库以供用这种语言和环境进行动画、可视化、网络编程以及很多其他的应用。
在本文中,您会发现Processing是一个很棒的进行数据可视化的环境,具有一个简单的接口、一个功能强大的语言以及一套丰富的用于数据以及应用程序导出的机制。
Processing运行于GNU/Linux®以及MacOSX和Windows®上,并且支持将图像导出成各种格式。
对于动态应用程序,甚至可以将Processing应用程序作为Java™applet导出以用在Web环境内。
本文将先介绍ProcessingIDE,然后再讨论Processing语言的第一个层面。
之后将会介绍一些关键的图形原语,最后则探讨几个利用这些原语的应用程序。
Processing的起源
Processing最早是一个用于教授计算机编程的简化编程语言。
这些理念起源于MITMediaLab(JohnMaeda)的DesignByNumbers项目,目的是通过可视应用程序的开发来教授编程。
虽然它供编程初学者使用,但该项目也针对艺术家及可视化设计者而设。
有关Processing及衍生产品的更多信息,请参见 参考资料。
Processing环境
第一步是安装Processing环境。
去到 Processing.org,单击 DownloadProcessing 并选择您的操作系统。
请注意,本文中的例子使用的是ProcessingV1.2.1。
下载了压缩文件后,用tarxvfzprocessing-1.2.1.tgz 对其进行展开。
此外,还需要确保Java技术已经可用。
在Ubuntu上,只需键入 sudoapt-getinstallopenjdk-6-jdk。
安装完成后,转到之前解压缩时创建的processing-1.2.1目录并尝试键入 ./processing。
这应该会弹出ProcessingDevelopmentEnvironment(PDE或ProcessingIDE),如图1所示。
占此窗口较大的部分是文本编辑器。
如果输入图中所示的两行代码,然后单击 Run(左上角的三角形),出现一个窗口,显示您所输入的简单程序(或Processing术语所指的 sketch)的结果。
单击 Stop(左上角的方框)退出程序,窗口消失。
图1.PDE和Results窗口
现在,让我们先来深入研究Processing语言,探讨它的主要特性,并且开发一些有趣的应用程序。
回页首
Processing语言
Processing是用Java编程语言写的,并且Java语言也是在语言树中最接近Processing的。
所以,如果您熟悉 C 或Java语言,Processing将很容易学。
并且在程序如何构造方面,也作了一些简化。
Processing并不包括Java语言的一些较为高级的特性,但这些特性中的很多特性均已集成到了Processing,所以您无需了解它们。
之所以选择Java语言是因为Processing应用程序被翻译成Java代码执行。
选择Java范型简化了这种翻译并让开发和执行可视化程序变得十分简单和直观。
若要对比Processing语言和Java语言,请参阅 参考资料。
回页首
图形环境
正如您在 图1 所见,在Processing内进行开发涉及到的是PDE和显示窗口。
2-D图形的坐标系如图2所示。
size 关键字以像素为单位定义了显示窗口的大小并且通常都是Processing应用程序内的首要步骤。
图2.2-D显示窗口的坐标
如图2所示,size 关键字指定显示窗口的X和Y坐标。
line 关键字则会在两个像素点之间绘制一条线(以 x1、y1tox2、y2 的格式)。
请注意,超出屏幕边界(size 定义的边界外)画线并非不允许,只是被忽略了而已。
本文无意对此做深入探讨,但 size 接受可选的第三个参数 mode。
mode 用来定义要使用的呈现引擎并支持PDF(直接呈现为Adobe®PDF文档)、OPENGL (利用一个可用的Open-GL图形适配器)、P3D(为了迅速的3-D呈现)等。
默认的是 JAVA2D,它最适合于高质量的2-D成像。
现在,我们来看一些基本的图形原语,然后再深入探讨几个示例应用程序。
回页首
图形原语
Processing包含了大量各种各样的几何形状以及这些形状的控件。
本节会简介一些基本的图形原语。
背景和颜色
background 功能被用来设置显示窗口的颜色。
此函数可以使用各种不同的参数(来定义一个灰度值或Red-Green-Blue[RGB]颜色)。
清单1内的代码片段会生成如 图3 所示的输出,其中的cella)。
清单1.使用Background函数
size(100,100);
background(0,128,0);
绘制像素点
可以使用 set 函数绘制单个像素点。
此函数接受显示窗口内的x,y坐标以及作为颜色的第三个参数。
Processing也有一个类型,称为 color,通过它,可以定义用于某个操作的颜色。
在本例中,我们创建了一个颜色实例并用它来设置显示窗口内的某个像素点(参见清单2和 图3 中的cellb)。
清单2.设置像素点和颜色
size(100,100);
for(intx=0;x<100;x++){
for(inty=0;y<100;y++){
colorc=color(x*2,y*2,128);
set(x,y,c);
}
}
可以使用 get 操作来读取显示中的一个给定像素点的颜色。
虽然 set 很简单,但它不是操做显示的最快方式。
要想快速访问,可以使用pixels 数组(与 loadPixels 和 updatePixels 函数一致)。
绘制形状
在Processing内使用单个函数绘制形状十分简单。
要设置在绘制形状时使用何种颜色,可以利用 stroke 函数。
此函数可接受一个单独的灰度参数或三个RGB参数。
此外,还可以用 fill 命令定义这个形状的填充色。
清单3 显示了如何绘制线、矩形、圆(使用椭圆)及椭圆。
line 函数接受四个参数,代表的是要在其间绘制线条的点。
rect 函数可绘制一个矩形,并且前两个点定义位置,而后面两个点则分别定义宽度和高度。
ellipse 函数也接受四个参数,分别定义位置和宽/高度。
当宽和高相等时,就是一个圆形。
还可以使用 ellipseMode 函数定制椭圆,它指定x,y位置是否代表了椭圆的角(CORNER)或中心(CENTER)。
参见 图3中的cellC。
清单3.线和形状
size(100,100);
stroke(0,128,0);
line(10,10,90,90);
fill(20,50,150);
rect(30,30,60,40);
fill(190,0,30);
ellipse(30,70,20,20);
fill(0,150,90);
ellipse(70,30,30,20);
绘制四边形
在Processing内使用 quad 可以很容易地绘制有四个边的多边形。
四边形接受八个参数,代表的是这个四边形的四个顶点。
清单4内的示例创建了10个随机的四边形(其中这些点必须是顺时针或逆时针顺序。
此代码还会为每个四边形创建一个随机的灰度。
清单4.绘制四边形
size(100,100);
for(inti=0;i<10;i++){
intx1=(int)random(50);
inty1=(int)random(50);
intx2=(int)random(50)+50;
inty2=(int)random(50);
intx3=(int)random(50)+50;
inty3=(int)random(50)+50;
intx4=(int)random(50);
inty4=(int)random(50)+50;
fill(color((int)random(255)));
quad(x1,y1,x2,y2,x3,y3,x4,y4);
}
图3.清单1至4的图形输出
其他形状多得不胜枚举,因此对于行宽及图像平滑度的控制也是不计其数的。
图4显示了来自 清单4 的 quad 函数的例子,其中就调用了smooth 函数。
此函数提供了去掉边缘锯齿的功能,虽然牺牲了速度,却改进了图像的质量。
图4.使用平滑函数
回页首
Processing应用程序的结构
至此,通过几个简单的脚本,您已经对Processing语言有了大致的了解,但这些脚本是一些非结构化的代码,只提供了应用程序的一些简单元素。
Processing应用程序是有一定结构的,这一点在开发能够持续运行且随时更改显示窗口的图形应用程序(比如动画)时非常重要。
在这种情况下,就凸显了 setup 和 draw 这两个函数的重要性。
setup 函数用于初始化,由Processing运行时执行一次。
通常,setup 函数包含 size 函数(用于定义窗口的边界)以及在操作期间要使用的变量的初始化。
Processing运行时会不断执行 draw 函数。
每次 draw 函数结束后,就会在显示窗口绘制一个新的画面,并且 draw 函数也会被再次调用。
默认的绘制速度是每秒60个画面,但是您也可以通过调用 frameRate 函数来更改这个速度。
此外,还可以使用 noLoop 和 draw 来控制在何时绘制画面。
noLoop 函数会导致绘制停止,而使用 loop 函数则可以重新开始绘制。
通过调用redraw 可以控制 draw 在何时调用。
现在,了解了如何开发一个Processing应用程序后,让我们来看一个展示文本使用的简单例子。
使用文本
Processing不仅支持显示窗口内的文本,还支持控制台形式的用于调试的文本。
要在显示窗口内使用文本,需要一种字体。
所以,第一步是创建一种字体(使用PDE的 Tools 选项)。
选择了要创建的字体后,字体文件(VLW)就会显示在项目的./data子目录内。
之后,就可以使用loadFont 函数加载这个文件,然后再使用 textFont 将它定义为默认。
这两个步骤在 图5 的 setup 函数内有所显示。
还请注意我们已经将画面速度减慢为每秒1个画面(因为这也是更新自然发生的频率)。
draw 函数展示了您在之前没有见过的其他一些函数。
首先是时间函数,它返回的是时钟的小时、分和秒。
请注意有一些传统的函数可以返回年、月和日。
存储了时间数据后,就可以使用 nf 函数创建一个字符串,它可以将数字转变为字符串。
为了将向时钟添加一些花样,可以使用background 和 fill 函数处理背景和时钟的颜色。
背景的颜色范围是从255(白)到137(淡灰)。
fill 函数可用于给文本上色,范围是从100(淡灰)到218(接近于黑色)。
颜色设好后,text 函数就会向将时间字符串发送到显示窗口已定义的坐标位置。
也可以使用 println 函数将字符串发到控制台(参见图5左下角)。
图5.在Processing应用程序内使用文本
回页首
构建简单的应用程序
现在,让我们来看几个用Processing构建的模拟仿真。
第一个是一个实现森林火灾模型的2-D元胞自动机实现。
这个模型来自Chopard和Dro的“物理系统的元胞自动机建模”,它提供了一个简单系统,展示了树的生长以及由雷击导致的大火的蔓延。
这个模拟包含了一组简单规则,定义如下:
∙在一个空场地(灰色),一棵树以 pGrowth 的机率成长。
∙如果其相邻树中有至少有一棵树正在燃烧,那么这颗树也会成为一棵燃烧树(红色)。
∙一棵燃烧树(红色)成为一个空场地(灰色)。
∙如果周围没有任何燃烧树,那么这个树成为燃烧树的可能性为 pBurn。
比如由雷击导致的燃烧,就是其中的一种可能。
这些规则的代码可以在 update 函数(参见 清单5)内找到,它迭代2-D空间以决定根据已定义的规则,状态如何转换。
请注意这个2-D空间实际上是3-D的,因为保存了此空间的两个副本—一个针对的是当前迭代,一个针对的是上一个迭代。
这么做是为了避免更改对空间的破坏。
此空间然后会成为一个显示空间(被显示的东西)和一个计算空间(规则的应用)。
这些空间按每次生成对调。
从很大程度上讲,这个应用程序使用了极少的Processing图形关键字。
为空间定义的颜色只有几个:
stroke 用来更改颜色,point 用于绘制像素点。
使用Processing模型,draw 函数调用 update 以应用规则;返回后,draw 将这个更新了的空间发到显示窗口。
清单5.元胞自动机森林火灾模型
int[][][]pix=newint[2][400][400];
inttoDraw=0;
inttree=0;
intburningTree=1;
intemptySite=2;
intx_limit=400;
inty_limit=400;
colorbrown=color(80,50,10);//brown
colorred=color(255,0,0);//red;
colorgreen=color(0,255,0);//green
floatpGrowth=0.01;
floatpBurn=0.00006;
booleanprob(floatp)
{
if(random(0,1)
elsereturnfalse;
}
voidsetup()
{
size(x_limit,y_limit);
frameRate(60);
/*Initializetoallemptysites*/
for(intx=0;xfor(inty=0;ypix[toDraw][x][y]=emptySite;
}
}
}
voiddraw()
{
update();
for(intx=0;xfor(inty=0;yif(pix[toDraw][x][y]==tree){
stroke(green);
}elseif(pix[toDraw][x][y]==burningTree){
stroke(red);
}elsestroke(brown);
point(x,y);
}
}
toDraw=(toDraw==0)?
1:
0;
}
voidupdate()
{
intx,y,dx,dy,cell,chg,burningTreeCount;
inttoCompute=(toDraw==0)?
1:
0;
for(x=1;xfor(y=1;ycell=pix[toDraw][x][y];
//Surveyareaforburningtrees
burningTreeCount=0;
for(dx=-1;dx<2;dx++){
for(dy=-1;dy<2;dy++){
if((dx==0)&&(dy==0))continue;
elseif(pix[toDraw][x+dx][y+dy]==burningTree)burningTreeCount++;
}
}
//Determinenextstate
if(cell==burningTree)chg=emptySite;
elseif((cell==emptySite)&&(prob(pGrowth)))chg=tree;
elseif((cell==tree)&&(prob(pBurn)))chg=burningTree;
elseif((cell==tree)&&(burningTreeCount>0))chg=burningTree;
elsechg=cell;
pix[toCompute][x][y]=chg;
}
}
}
图6显示了这个元胞自动机森林火灾模型的迭代,跳跃恰当,很好地显示了所设规则的效果。
Time0包含的只有树木在其中生长的空间。
在time40,就可以开始看到大火在燃烧并最终占据整个空间。
在大约time100,树木生长更为明显,但在time120时,起火更多,过程循环。
图6.元胞自动机森林火灾模型的输出
回页首
易染/感染/免疫模型
易染/感染/免疫模型模拟的是疾病在医院内的蔓延。
与森林火灾模型类似,SIR也是通过一套简单规则实现的,只不过添加了一些复杂性和有趣的行为。
在这个模型内,有一个由病人占据的病床组成的网格。
在time0,所有病人都是某一种新疾病的易染人群,这意味着这些病人从未患过这种疾病,因此才有可能被感染。
如果在某个病人的东/南/西/北的四个邻居中有一个患了这种疾病,那么该病人受感染的可能性为 tau。
一个受感染的病人的患病时间为 K 天,在此期间病人有感染其他病人的可能性。
在 K 天后,该病人康复并有了对这种疾病的免疫力。
正如之前的例子所示,setup 函数先初始化这个医院以及所有易染病人,只有最中心的这个病人是已经患病的。
在该实现内,0 是易染病人,1-K 是感染病人,-1 是免疫病人。
draw 函数将这种几何分布发到显示窗口,update 实施这些SIR规则。
与之前一样,可以用一个3D数组保存当前的这些几何分布。
清单6给出了此代码。
清单6.Processing内的SIR模型
int[][][]beds=newint[2][200][200];
inttoDraw=0;
intx_limit=200;
inty_limit=200;
colorbrown=color(80,50,10);//brown
colorred=color(255,0,0);//red;
colorgreen=color(0,255,0);//green
intsusceptible=0;
intrecovered=-1;
floattau=0.2;
intk=4;
booleanprob(floatp)
{
if(random(0,1)
elsereturnfalse;
}
voidsetup()
{
size(x_limit,y_limit);
frameRate(50);
for(intx=0;xfor(inty=0;ybeds[toDraw][x][y]=susceptible;
}
}
beds[toDraw][100][100]=1;
}
voiddraw()
{
update();
for(intx=0;xfor(inty=0;yif(beds[toDraw][x][y]==recovered)stroke(brown);
elseif(beds[toDraw][x][y]==susceptible)stroke(green);
elseif(beds[toDraw][x][y]point(x,y);
}
}
toDraw=(toDraw==0)?
1:
0;
}
booleansick(intpatient)
{
if((patient>0)&&(patientreturnfalse;
}
voidupdate()
{
intx,y,cell;
inttoCompute=(toDraw==0)?
1:
0;
for(x=1;xfor(y=1;ycell=beds[toDraw][x][y];
if(cell==k)cell=recovered;
elseif(sick(cell))cell++;
elseif(cell==susceptible){
if(sick(beds[toDraw][x][y-1])||sick(beds[toDraw][x][y+1])||
sick(beds[toDraw][x-1][y])||sick(beds[toDraw][x+1][y])){
if(prob(tau))cell=1;
}
}
beds[toCompute][x][y]=cell;
}
}
}
Processing内SIR模型的输出如图7所示。
请注意这里这些绿色的像