• Archive by category "可视化技术"

Blog Archives

R语言天气可视化应用

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-app-china-weather/

r-app-chinaweather

前言

在很多人看来,R语言还只是个玩具,完全不具备企业级应用的能力。说这些话的人,根本就不了解R语言,更不清楚如何做企业级应用开发。从我最早接触R语言时,就把R做为可视化引擎嵌入到了晒粉丝的微博应用中;后来又开发了数据挖掘算法竞赛网站,并把R语言做为算法引擎,并支持在线编程及运行;我做的第三个R语言应用就是本文要给大家分享的每日中国天气微博应用,这次同样是把R做为可视化引擎,并让R完成爬虫、XML文档解析及数据处理等的任务;当然,我还实现了第四个、第五个、第六个以R为核心的应用,都是量化投资方面的,会在下一本书《R的极客理想-量化投资篇》再介绍给大家。

从我的使用经验来看,R语言已经具备了企业级应用的能力,但我并不是要用R语言完成所有编程任务。在我的项目环境中,大都是多种编程语言配合使用的,只有发挥各自语言的特性优势,才是未来的发展方向。

本文所介绍的每日中国天气微博应用开发,将分为3篇介绍R语言和PHP语言的混合编程,第一篇为R语言功能实现,第二篇为R包开发,第三篇为用PHP构建微博应用。本文是第一篇。

目录

  1. 项目介绍
  2. 系统架构设计
  3. R语言程序现实

1. 项目介绍

谈到多语言混编,同如在计算机领域跨学科一样,是我所一直倡导一种工作模式。当编程语言百花齐放,各种细分市场的小众语言如雨后春笋般地成长起来,比起通用型编程语言来说,这些小众语言在特定的领域中有着非常明显的优势。 比如统计应用,如果用Java写个逻辑回归程序感觉深不见底,而用R语言实现逻辑回归就是个很平常的一件事情。 再比如做一个Web网站,用PHP或Nodejs实现轻而易举,如果用Java做不仅代码量大,而且程序复杂。 所以,对于一个应用来说,一种通用的语言并不一定是最好的解决方案,如果能实现多种语言的结合,那么你做出来的应用可以很酷,很不一样!

对于本文要介绍的 每日中国天气 这个新浪微博应用,就是一种多语言混编的实现。

项目介绍

这个项目的出发点很简单,就是通过可视化技术,展示中国每个省份的天气情况,给准备旅游的朋友,提供一种出行的提示。

要做实现这个应用,我们首先要列出,要实现哪些功能,会遇到哪些问题等。

  • 天气数据:数据从哪里找到,如何下载,如何存储。
  • 定时任务:天气数据需要每日更新,图片需要每日新生成。
  • 地图和天气可视化:要把中国行政区图和天气数据结合在一起画图,让用户一眼就能看明白。
  • Web展示:通过可视化技术,我们生成的只是一张静态图片,如何发布到Web端进行展示。
  • 微博:通过结合新浪微博,让更多的用户看到并使用这个应用。
  • 用户交互:用户可以查看不同日期、不同类型的图片,用户还可以通过微博分享。
  • 虽然是个很小的应用,但五脏俱全,我们也需要完整的思考,如何才能实现这个应用呢!

2. 系统架构设计

从上面的功能描述中,单独使用一种语言也可以实现的。 如果单独用PHP开发,做一个Web网站非常容易,连接新浪微博也有现成的SDK可以调用,爬取数据及存储也不麻烦,那么如何实现地图和天气数据的可视化,似乎就是卡在这里了。 如果单独用R开发,爬取数据及存储同样很容易实现,地图和天气数据的可视化也是很方便就能画出来,但是用R做Web网站,那就会遇到很大的瓶颈了,因为R是单线程同步的计算模型,Web应用的高并发特点,会直接让R程序崩溃的。 所以,综合上面的问题,如果R语言和PHP语言能结合在一起使用,不仅能避开每种语言不擅长的地方,还能在擅长的领域发挥出每种语言的特性,我们将通过多语言的混编技术做出很不一样的应用来。

为了实现应用的功能需求,我们要设计一套系统架构。

w1

系统架构解释:

  • 通过定时器启动爬虫程序,到Yahoo的天气数据源下载数据。
  • 爬虫下载数据到本地服务器进行解析,存储应用相关的数据到CSV文件。
  • 可视化程序,读入天气数据及地图数据,生成静态的图片作为可视化输出。
  • 最终用户通过新浪微博,加载Web应用,看到了可视化生成的静态图片。
  • 最终用户通过新浪微博分享了这个应用,让更多的人看到这个应用。

下面按照语言的优势,把应用架构以语言的特性来划分,让R语言实现爬虫、处理数据和可视化,让PHP完成Web开发、新浪API接入和用户交互。

w2

由于我们这个应用,不需要让R和PHP直接进行通信,那么复杂度就会变得小很多了,像我之前做的晒粉丝应用,是3种语言的结合包括了R, PHP, Java,通过Java实现中间程序的调度,让R和PHP能够实现通信。

我们通过语言的划分,就可以扬长避短,让每种语言在最擅长的领域,完成最擅长的事情。

对于后台技术应用,定时器可以用Linux系统的CRON实现;然后用R语言程序来爬取数据,通过RCurl包来完成;爬取后的数据为XML格式,再通过R语言用XML包进行解析,以CSV格式进行本地存储;接下来,再用R语言处理数据,加载地图包ggmap、mapdata、maptools,最后配合plot()函数实现图片的输出,保存在本地服务器上。

对于前端的PHP应用来说,用PHP做一个Web网站很简单,使用YII快速开发框架;用PHP的新浪微博SDK进行API操作,实现新浪登陆,新浪分享等功能;最后Nginx + Spawn构建出PHP运行时环境,让Nginx完成负载均衡和图片加载,并配合PHP的访问规则,实现功能的切换。

合理的架构设计加上适应的语言的分工,就能轻松实现了 每日中国天气 这样的一个微博应用。其实,我们可以用这种多语言混搭的方式,创建出各种创新型的网站应用,但前提是先能掌握多种语言。

这里我想再多说一句,通常我认识的程序员,都是在自己的技术领域中无限畅快,一旦他们掌握了一种语言的核心技术,并有了一些开发经验后,往往不愿意再去学第二种语言。 对这些人来说,总觉得自己就是世界的中心,自己有能力实现的所有的功能。这些也都是有理想的程序员,只不过他们进入了一个误区,被现有的技术给迷住了,看不到、也不愿意看到外面的世界已经变了。我曾经就是这样的!

我承认Java是一种无所不能的编程语言,但是如果你所有程序都用Java实现,难道不觉得又费时又费力吗?通用性越强,反而专有领域的应用性就越差。这也是我从Java单一的技术路线走出来的原因。其实,在精通一门语言后,再去学习另外一门新的语言,就不是那么难了。但如果只是沉醉于已掌握的技术,很快就会被一代新人,一代新工具所超越的。

3. R语言程序现实

下面就开始介绍R语言的部分程序开发,在写代码之前,我们需要先梳理开发流程,做一下程序设计,R语言都需要实现哪些功能,用到哪些第三方R包。

我用一幅图来说明程序之间的调用关系,R语言的程序实现一共包括了6个部分,爬虫程序、本地存储,地图加载、数据可视化处理、生成静态图、生成可交互的静态图。

w3

上图中,分别标出了每个步骤用的到R包或者功能函数,同时我们可以按照这个流程来定义功能函数,这样我们就把整个应用程序都规划好,最后再对应的写代码就不难了。

3.1 爬虫部分

对于爬虫部分来说,就是定时下载每个城市的或地区的天气数据,并解析数据,只保留我们需要的字段,并以CSV的格式存储。互联网上有很多免费公开的天气数据源,对我来说,最方便的数据源有2个,一个是Yahoo的天气数据,另一个Google的天气数据,但由于Google的API从中国大陆会经常会访问不到,所以我在这里选择Yahoo的天气数据源进行访问。

yahoo天气数据源的访问地址,如下所示。

http://weather.yahooapis.com/forecastrss?w=WOEID

其中WOEID代表城市对应的代码,如果想查看北京的天气数据,北京对应的WOEID为2151330,可以访问用浏览器访问 http://weather.yahooapis.com/forecastrss?w=2151330

我们通过浏览器打开地址,就可以看到这个数据,数据是以XML格式进行发布的。

w_1

我们要解析这个XML文件,从中找到我们需要数据进行提邓。在R语言中,通过RCurl包实现HTTP的网络访问,抓取到整个的XML文档数据,然后通过XML包解析XML文档的DOM树,就能找到我们需要的数据了。

本文的系统环境

  • Win7 64bit
  • R: 3.1.1 x86_64-w64-mingw32/x64 (64-bit)

当我们把业务逻辑和技术实现都想清楚了,就可以动手写代码了,只十几行代码就能完成爬虫和XML文档解析的功能。


> library(RCurl)        # 加载类库
> library(XML)
>
> getWeather<-function (x){
+     url<-paste('http://weather.yahooapis.com/forecastrss?w=',x,'&u=c',sep="")       # yahoo的数据源地址
+     doc = xmlTreeParse(getURL(url),useInternal = TRUE)                              # 解析XML文档
+
+     ans<-getNodeSet(doc, "//yweather:atmosphere")
+     humidity<-as.numeric(sapply(ans, xmlGetAttr, "humidity"))                       # 温度
+     visibility<-as.numeric(sapply(ans, xmlGetAttr, "visibility"))                   # 能见度
+     pressure<-as.numeric(sapply(ans, xmlGetAttr, "pressure"))                       # 气压
+     rising<-as.numeric(sapply(ans, xmlGetAttr, "rising"))                           # 气压变动
+
+     ans<-getNodeSet(doc, "//item/yweather:condition")
+     code<-sapply(ans, xmlGetAttr, "code")                                           # 天气情况
+
+     ans<-getNodeSet(doc, "//item/yweather:forecast[1]")
+     low<-as.numeric(sapply(ans, xmlGetAttr, "low"))                                 # 最高气温
+     high<-as.numeric(sapply(ans, xmlGetAttr, "high"))                               # 最低气温
+
+     print(paste(x,'==>',low,high,code,humidity,visibility,pressure,rising))
+     cbind(low,high,code,humidity,visibility,pressure,rising)                        # 以data.frame格式返回
+ }

运行程序,查看返回结果。


> w<-getWeather(2151330)    # 执行爬虫程序
[1] "2151330 ==> 9 13 21 59 4.1 1016.4 0"

> w                         # 返回的结果集
     low high code humidity visibility pressure rising
[1,] "9" "13" "21" "59"     "4.1"      "1016.4" "0"

对于功能需求来说,一个城市只保存7个字段就行了,其他的XML文档的数据可以全部过滤掉不管。

3.2 本地存储

我们通过爬虫下载并过滤后的数据,已经是data.frame的格式了,通过write.csv()函数就把这些数据输出到本地文件系统中保存起来,做为数据的备份。

我们在处理本地存储的过程中,除了要生成一个CSV文件,还包括了 文件命名,把多个城市的数据合并到一个文件存储的问题。下面我们需要再定义两个函数,filename()函数用于新生成文件的命名,loadDate()函数用于多个城市数据的加载,合并在一个文件中保存。

城市列表应该是我们需要提单准备好的,我这里只选取了中国的34个城市作为我们要获得的城市天气数据的信息。如果想爬取更多的城市天气数据的信息,那么补充这个列表就行了。

城市列表数据文件WOEID.csv。


beijing,2151330,北京,北京市,116.4666667,39.9
shanghai,2151849,上海,上海市,121.4833333,31.23333333
tianjin,2159908,天津,天津市,117.1833333,39.15
chongqing,20070171,重庆,重庆市,106.5333333,29.53333333
harbin,2141166,哈尔滨,黑龙江省,126.6833333,45.75
changchun,2137321,长春,吉林省,125.3166667,43.86666667
shenyang,2148332,沈阳,辽宁省,123.4,41.83333333
hohhot,2149760,呼和浩特,内蒙古自治区,111.8,40.81666667
shijiazhuang,2171287,石家庄,河北省,114.4666667,38.03333333
wulumuqi,26198317,乌鲁木齐,新疆维吾尔自治区,87.6,43.8
lanzhou,2145605,兰州,甘肃省,103.8166667,36.05
xining,2138941,西宁,青海省,101.75,36.63333333
xian,2157249,西安,陕西省,108.9,34.26666667
yinchuan,2150551,银川,宁夏回族自治区,106.2666667,38.33333333
zhengzhou,2172736,郑州,河南省,113.7,34.8
jinan,2168327,济南,山东省,117,36.63333333
taiyuan,2154547,太原,山西省,112.5666667,37.86666667
hefei,2127866,合肥,安徽省,117.3,31.85
wuhan,2163866,武汉,湖北省,114.35,30.61666667
changsha,26198213,长沙,湖南省,113,28.18333333
nanjing,2137081,南京,江苏省,118.8333333,32.03333333
chengdu,2158433,成都,四川省,104.0833333,30.65
guiyang,2146703,贵阳,贵州省,106.7,26.58333333
kunming,2160693,昆明,云南省,102.6833333,25
nanning,2166473,南宁,广西壮族自治区,108.3333333,22.8
lasa,26198235,拉萨,西藏自治区,91.16666667,29.66666667
hangzhou,2132574,杭州,浙江省,120.15,30.23333333
nanchang,26198151,南昌,江西省,115.8666667,28.68333333
guangzhou,2161838,广州,广东省,113.25,23.13333333
fuzhou,2139963,福州,福建省,119.3,26.08333333
taipei,2306179,台北,台湾省,121.5166667,25.05
haikou,2162779,海口,海南省,110.3333333,20.03333333
hongkong,24865698,香港,香港特别行政区,114.1666667,22.3
macau,20070017,澳门,澳门特别行政区,113.5,22.2

字段解释:

  • 第一列,城市的英文名
  • 第二列,WOEID代码
  • 第三列,城市的中文名
  • 第四列,城市所在的省中文名
  • 第五列,经度(默认为东经)
  • 第六列,纬度(默认为北纬)
    • 用于生成数据文件的R语言的函数实现。

      
      > filename<-function(date=Sys.time()){            # 文件根据日期来命名
      +     paste(format(date, "%Y%m%d"),".csv",sep="")
      + }
      
      > loadDate<-function(date){                       # 读取城市列表,调用爬虫函数,合并数据保存到一个文件中。
      +     print(paste('Date','==>',date))
      +     city<-read.csv(file="WOEID.csv",header=FALSE,fileEncoding="utf-8", encoding="utf-8")  # 加载城市列表
      +     names(city)<-c("en","woeid","zh",'prov','long','lat')
      +     city<-city[-nrow(city),]
      +
      +     wdata<-do.call(rbind, lapply(city$woeid,getWeather))
      +     w<-cbind(city,wdata)
      +     write.csv(w,file=filename(date),row.names=FALSE,fileEncoding="utf-8")
      + }
      

      运行程序loadDate()的函数,程序会根据城市列表的数据,调用getWeather()函数自动爬取我们定义的所有城市的天气数据。

      
      > date=Sys.time();date              # 选择日期
      [1] "2014-10-01 13:01:08 CST"
      
      > loadDate(date)                    # 爬取数据
      [1] "Date ==> 2014-10-01 13:01:08"
      [1] "2151330 ==> 9 13 21 59 4.1 1016.4 0"
      [1] "2151849 ==> 18 23 30 57 9.99 1015.92 0"
      [1] "2159908 ==> 12 22 30 58 9.99 1017 0"
      [1] "20070171 ==> 16 22 26 79 NA 1013.6 0"
      [1] "2141166 ==> 2 13 34 29 9.99 1015.92 0"
      [1] "2137321 ==> 3 6 11 81 9.99 1015.92 1"
      [1] "2148332 ==> 7 16 34 27 9.99 1015.92 0"
      [1] "2149760 ==> 4 19 30 59 9.99 982.05 0"
      [1] "2171287 ==> 12 14 11 94 2.49 982.05 2"
      [1] "26198317 ==> 12 23 34 52 9.99 1015.92 2"
      [1] "2145605 ==> 6 17 20 82 8 812.73 0"
      [1] "2138941 ==> 3 21 32 63 9 745.01 0"
      [1] "2157249 ==> 13 23 11 91 2.99 1017.9 0"
      [1] "2150551 ==> 8 22 28 60 7 1016.8 0"
      [1] "2172736 ==> 13 19 32 52 8 1015.92 0"
      [1] "2168327 ==> 14 22 32 49 NA 1017 0"
      [1] "2154547 ==> 9 18 20 88 1.59 982.05 2"
      [1] "2127866 ==> 17 23 34 60 9.99 1015.92 2"
      [1] "2163866 ==> 19 26 28 78 6 982.05 2"
      [1] "26198213 ==> 21 28 28 65 9.99 982.05 2"
      [1] "2137081 ==> 15 23 34 57 9.99 1015.92 2"
      [1] "2158433 ==> 19 27 20 69 4.01 1015.92 0"
      [1] "2146703 ==> 18 26 28 73 9.99 1015.92 0"
      [1] "2160693 ==> 13 23 28 64 9.99 1015.92 2"
      [1] "2166473 ==> 24 32 30 62 9.99 982.05 0"
      [1] "26198235 ==> -1 15 30 50 NA 643.41 0"
      [1] "2132574 ==> 16 23 30 53 9.99 1015.92 0"
      [1] "26198151 ==> 21 27 20 75 7 1016.4 0"
      [1] "2161838 ==> 25 31 28 58 8 982.05 2"
      [1] "2139963 ==> 21 29 28 65 9.99 982.05 0"
      [1] "2306179 ==> 24 28 28 70 9.99 982.05 0"
      [1] "2162779 ==> 24 31 30 58 9.99 982.05 0"
      [1] "24865698 ==> 26 30 30 59 9.99 982.05 2"
      

      程序运行完成后,会在当前目录生成一个名字为20141001.csv文件。打开20141001.csv文件,这个文件就是接下来用于生成可视化图片的基础数据了。

      
      "en","woeid","zh","prov","long","lat","low","high","code","humidity","visibility","pressure","rising"
      "beijing",2151330,"北京","北京市",116.4666667,39.9,"9","13","21","59","4.1","1016.4","0"
      "shanghai",2151849,"上海","上海市",121.4833333,31.23333333,"18","23","30","57","9.99","1015.92","0"
      "tianjin",2159908,"天津","天津市",117.1833333,39.15,"12","22","30","58","9.99","1017","0"
      "chongqing",20070171,"重庆","重庆市",106.5333333,29.53333333,"16","22","26","79",NA,"1013.6","0"
      "harbin",2141166,"哈尔滨","黑龙江省",126.6833333,45.75,"2","13","34","29","9.99","1015.92","0"
      "changchun",2137321,"长春","吉林省",125.3166667,43.86666667,"3","6","11","81","9.99","1015.92","1"
      "shenyang",2148332,"沈阳","辽宁省",123.4,41.83333333,"7","16","34","27","9.99","1015.92","0"
      "hohhot",2149760,"呼和浩特","内蒙古自治区",111.8,40.81666667,"4","19","30","59","9.99","982.05","0"
      "shijiazhuang",2171287,"石家庄","河北省",114.4666667,38.03333333,"12","14","11","94","2.49","982.05","2"
      "wulumuqi",26198317,"乌鲁木齐","新疆维吾尔自治区",87.6,43.8,"12","23","34","52","9.99","1015.92","2"
      "lanzhou",2145605,"兰州","甘肃省",103.8166667,36.05,"6","17","20","82","8","812.73","0"
      "xining",2138941,"西宁","青海省",101.75,36.63333333,"3","21","32","63","9","745.01","0"
      "xian",2157249,"西安","陕西省",108.9,34.26666667,"13","23","11","91","2.99","1017.9","0"
      "yinchuan",2150551,"银川","宁夏回族自治区",106.2666667,38.33333333,"8","22","28","60","7","1016.8","0"
      "zhengzhou",2172736,"郑州","河南省",113.7,34.8,"13","19","32","52","8","1015.92","0"
      "jinan",2168327,"济南","山东省",117,36.63333333,"14","22","32","49",NA,"1017","0"
      "taiyuan",2154547,"太原","山西省",112.5666667,37.86666667,"9","18","20","88","1.59","982.05","2"
      "hefei",2127866,"合肥","安徽省",117.3,31.85,"17","23","34","60","9.99","1015.92","2"
      "wuhan",2163866,"武汉","湖北省",114.35,30.61666667,"19","26","28","78","6","982.05","2"
      "changsha",26198213,"长沙","湖南省",113,28.18333333,"21","28","28","65","9.99","982.05","2"
      "nanjing",2137081,"南京","江苏省",118.8333333,32.03333333,"15","23","34","57","9.99","1015.92","2"
      "chengdu",2158433,"成都","四川省",104.0833333,30.65,"19","27","20","69","4.01","1015.92","0"
      "guiyang",2146703,"贵阳","贵州省",106.7,26.58333333,"18","26","28","73","9.99","1015.92","0"
      "kunming",2160693,"昆明","云南省",102.6833333,25,"13","23","28","64","9.99","1015.92","2"
      "nanning",2166473,"南宁","广西壮族自治区",108.3333333,22.8,"24","32","30","62","9.99","982.05","0"
      "lasa",26198235,"拉萨","西藏自治区",91.16666667,29.66666667,"-1","15","30","50",NA,"643.41","0"
      "hangzhou",2132574,"杭州","浙江省",120.15,30.23333333,"16","23","30","53","9.99","1015.92","0"
      "nanchang",26198151,"南昌","江西省",115.8666667,28.68333333,"21","27","20","75","7","1016.4","0"
      "guangzhou",2161838,"广州","广东省",113.25,23.13333333,"25","31","28","58","8","982.05","2"
      "fuzhou",2139963,"福州","福建省",119.3,26.08333333,"21","29","28","65","9.99","982.05","0"
      "taipei",2306179,"台北","台湾省",121.5166667,25.05,"24","28","28","70","9.99","982.05","0"
      "haikou",2162779,"海口","海南省",110.3333333,20.03333333,"24","31","30","58","9.99","982.05","0"
      "hongkong",24865698,"香港","香港特别行政区",114.1666667,22.3,"26","30","30","59","9.99","982.05","2"
      

      数据一共有10列,字段解释:

      • en,城市英文名
      • woeid, Yahoo天气API定义的WOEID,用于匹配城市
      • zh,城市中文名
      • prov,城市所在省的中文名
      • long,经度(中国处于东经,不区别东经西经)
      • lat,纬度(中国处于北纬,不区别南纬北纬)
      • low,最低温度
      • high,最高温度
      • code,天气概括代码
      • humidity,湿度
      • visibility,能见度
      • pressure,大气压
      • rising,气压变动

      这样数据就准备好了,那么接下来就是把天气数据对应到中国行政区地图上了。

      3.3 中国地国加载

      R语言通过第三方的地图R包,可以很方便的实现基于地图的可视化或基于地理信息的数据处理。那么R语言是如何做到的呢,是通过maps, mapdata, maptools这3个包合作完成的。

      我们调用maptools包的readShapePoly()函数,加载中国行政区地图的数据信息,保存在map的变量中,直接用plot()函数就可以看到可视化的效果了。地图数据是我提前下载好的,保存放在mapdata目录中,一共全部3个文件bou2_4p.dbf,bou2_4p.shp和bou2_4p.shx。

      
      > library(maps)
      > library(mapdata)
      > library(maptools)
      
      > map<-readShapePoly('mapdata/bou2_4p.shp')     # 加载中国行政区地图数据
      > plot(map)                                     # 画出中国行政区图
      

      m1

      是不是很神奇,2行就画出是中国行政区地图的轮廓,我们再继续来分析map这个变量。先检查一下的map的类型,发现是sp包中定义的SpatialPolygonsDataFrame类型的。

      
      > class(map)                                    # 查看map对象类型
      [1] "SpatialPolygonsDataFrame"
      attr(,"package")
      [1] "sp"
      

      SpatialPolygonsDataFrame类型我们并不熟悉,再用pryr包的otype查检一下,面向对象系统的类型。

      
      > library(pryr)
      > otype(map)        # 发现是S4类型的data.frame
      [1] "S4"
      

      R语言基于S4的面向对象编程一文,我们已经掌握了S4类型的基础知识,在知道map是一个S4类型的实例后,大概就能猜出这个对象如何使用了。另外从命名上看,SpatialPolygonsDataFrame类型,应该是用data.frame存储了SpatialPolygons的类型的数据。 先通过length()函数和names()函数,从data.frame的角度查看一下map对象,包括7列925行。

      
      > length(map)       # 一共有925条记录
      [1] 925
      
      > names(map)        # data.frame包括有7列
      [1] "AREA"       "PERIMETER"  "BOU2_4M_"   "BOU2_4M_ID" "ADCODE93"
      [6] "ADCODE99"   "NAME"
      

      再通过str()函数查看map对象第一行数据的静态结构。

      
      > str(map[1,])
      Formal class 'SpatialPolygonsDataFrame' [package "sp"] with 5 slots
        ..@ data       :'data.frame': 1 obs. of  1 variable:
        .. ..$ AREA: num 54.4
        ..@ polygons   :List of 1
        .. ..$ :Formal class 'Polygons' [package "sp"] with 5 slots
        .. .. .. ..@ Polygons :List of 1
        .. .. .. .. ..$ :Formal class 'Polygon' [package "sp"] with 5 slots
        .. .. .. .. .. .. ..@ labpt  : num [1:2] 127.8 47.9
        .. .. .. .. .. .. ..@ area   : num 54.4
        .. .. .. .. .. .. ..@ hole   : logi FALSE
        .. .. .. .. .. .. ..@ ringDir: int 1
        .. .. .. .. .. .. ..@ coords : num [1:5784, 1:2] 121 121 122 122 122 ...
        .. .. .. ..@ plotOrder: int 1
        .. .. .. ..@ labpt    : num [1:2] 127.8 47.9
        .. .. .. ..@ ID       : chr "0"
        .. .. .. ..@ area     : num 54.4
        ..@ plotOrder  : int 1
        ..@ bbox       : num [1:2, 1:2] 121.2 43.4 135.1 53.6
        .. ..- attr(*, "dimnames")=List of 2
        .. .. ..$ : chr [1:2] "x" "y"
        .. .. ..$ : chr [1:2] "min" "max"
        ..@ proj4string:Formal class 'CRS' [package "sp"] with 1 slots
        .. .. ..@ projargs: chr NA
      

      从这两个维度的观察,我们基本清楚map的结构,map里每一行是一个SpatialPolygonsDataFrame对象,包括5个属性,用于存储地图数据信息。取第一行数据data属性,查看结果,发现是黑龙江省的行政区地图数据。

      
      > map[1,]@data
          AREA PERIMETER BOU2_4M_ BOU2_4M_ID ADCODE93 ADCODE99     NAME
      0 54.447    68.489        2         23   230000   230000 黑龙江省
      

      用第一行数据画图。

      
      > plot(map[1,])
      

      m2

      如果取前100行数据画图,那么应该是部分中国省的行政区地图了,果然如我所料。

      
      > plot(map[1:100,])
      

      m3

      由于本文并不是地图包的详细介绍,只要了解到map对象的基本使用就行了,稍后在博客中我会单独介绍用R做地图可视化的开发。

      3.4 数据可视化

      完成了地图数据加载后,再接下来就是数据可视化了。数据可视化,我认为要分成2部分操作,一部分是数据处理,另一部分是可视化输出。

      我们先想一下要怎么进行数据处理,才能把天气数据和地图数据结合起来呢。我们的目标是要画出中国各省天气概况,会用到过之前过滤出的数据中code的数据,code的数据都是代码,我们还要定义code代码和实际意义的映射关系。

      Yahoo的源数据中,一共定义了49种天气情况,如code.csv文件所示,根据描述我把相似的天气情况进行合并,最后保留18种天气概况特征。code代码映射文件为lablecode.csv。

      code.csv文件。

      
      "code","en","zh","type"
      0,tornado,龙卷风,3
      1,tropical storm,热带风暴,2
      2,hurricane,暴风,3
      3,severe thunderstorms,强雷雨天气,16
      4,thunderstorms,雷雨,11
      5,mixed rain and snow,雨雪,12
      6,mixed rain and sleet,雨雪,12
      7,mixed snow and sleet,雨雪,12
      8,freezing drizzle,冻毛毛雨,11
      9,drizzle,毛毛雨,11
      10,freezing rain,冻雨,11
      11,showers,阵雨,11
      12,showers,阵雨,11
      13,snow flurries,小雪,13
      14,light snow showers,阵雪,13
      15,blowing snow,飞雪,13
      16,snow,雪,14
      17,hail,冰雹,15
      18,sleet,雨雪,12
      19,dust,灰尘,5
      20,foggy,雾,7
      21,haze,薄雾,7
      22,smoky,烟雾弥漫,6
      23,blustery,大风,3
      24,windy,风,4
      25,cold,冷,18
      26,cloudy,多云,8
      27,mostly cloudy (night),满云密布,8
      28,mostly cloudy (day),满云密布,8
      29,partly cloudy (night),少云,9
      30,partly cloudy (day),少云,9
      31,clear (night),晴,10
      32,sunny,睛,10
      33,fair (night),晴,10
      34,fair (day),晴,10
      35,mixed rain and hail,大雨和冰雹,16
      36,hot,热,1
      37,isolated thunderstorms,局部雷雨,11
      38,scattered thunderstorms,零星雷雨,11
      39,scattered thunderstorms,零星雷雨,11
      40,scattered showers,零星阵雨,11
      41,heavy snow,大雪,14
      42,scattered snow showers,零星阵雪,13
      43,heavy snow,大雪,14
      44,partly cloudy,少云,9
      45,thundershowers,雷阵雨,11
      46,snow showers,阵雪,13
      47,isolated thundershowers,局部雷雨,11
      3200,not available,无数据,19
      

      字段解释:

      • code,源数据天气特征代码
      • en,英文描述
      • zh,中文描述
      • type,分类代码

      lablecode.csv文件。

      
      "type","alias"
      1,热
      2,风暴
      3,大风
      4,微风
      5,灰尘
      6,大雾
      7,薄雾
      8,多云
      9,少云
      10,晴
      11,阵雨
      12,雨加雪
      13,小雪
      14,大雪
      15,冰雹
      16,大雨
      17,雷暴雨
      18,冷
      19,无数据
      

      字段解释:

      • type,分类代码
      • alias,用于显示的别名

      有了天气特征定义后,我们再把特征匹配到不同的颜色,并增加图例及文字描述,就生成了最终的中国各省天气概况的静态图片了。

      
      > library("RColorBrewer")
      > getColors2<-function(map,prov,ctype){
      +     #name change to ADCODE99
      +     ADCODE99<-read.csv(file="ADCODE99.csv",header=TRUE,fileEncoding="utf-8", encoding="utf-8")
      +     fc<-function(x){ADCODE99$ADCODE99[which(x==ADCODE99$prov)]}
      +     code<-sapply(prov,fc)
      +     
      +     f=function(x,y) ifelse(x %in% y,which(y==x),0);
      +     colIndex=sapply(map$ADCODE99,f,code);
      +     ctype[which(is.na(ctype))]=19
      +     return(ctype[colIndex])
      + }
      > summary<-function(data=data,output=FALSE,path=''){
      +     colors<-c(rev(brewer.pal(9,"Blues")),rev(c('#b80137','#8c0287','#d93c5d','#d98698','#f6b400','#c4c4a7','#d6d6cb','#d1b747','#ffeda0')))    # 定义18种天气特征对应的颜色
      +
      +     temp<-data$code
      +     title<-"中国各省天气概况"
      +     ofile<-paste(format(date,"%Y%m%d"),"_code.png",sep="")
      +     sign<-''
      +     colors<-rev(colors)
      +     code<-read.csv(file="code.csv",header=TRUE,fileEncoding="utf-8", encoding="utf-8")
      +     labelcode<-read.csv(file="labelcode.csv",header=TRUE,fileEncoding="utf-8", encoding="utf-8")
      +     ctype<-sapply(temp,function(x){code$type[which(x==code$code)]})
      +
      +     if(output)png(file=paste(path,ofile,sep=''),width=600,height=600)
      +     layout(matrix(data=c(1,2),nrow=1,ncol=2),widths=c(8,1),heights=c(1,2))
      +     par(mar=c(0,0,3,12),oma=c(0.2,0.2,0.2,0.2),mex=0.3)
      +     plot(map,border="white",col=colors[getColors2(map,data$prov,ctype)])       # 地图和天气可视化
      +     points(data$long,data$lat,pch=19,col=rgb(0,0,0,0.3),cex=0.8)                     # 标出采样城市
      +
      +     #=======================================          # 图片中的辅助文字
      +     if(FALSE){
      +         grid()
      +         axis(1,lwd=0);axis(2,lwd=0);axis(3,lwd=0);axis(4,lwd=0)
      +     }
      +     text(100,58, title,cex=2)
      +     text(105,54,format(date,"%Y-%m-%d"))
      +     text(98,65,paste('每日中国天气','http://apps.weibo.com/chinaweatherapp'))
      +     text(120,-8,paste('provided by The Weather Channel',format(date, "%Y-%m-%d %H:%M")),cex=0.8)
      +
      +     #=======================================          # 文字说明
      +     for(row in 1:nrow(data)){
      +         name<-as.character(data$zh[row])
      +         label<-labelcode$alias[labelcode$type==ctype[row]]
      +         x1<-ceiling(row/7)
      +         x2<-ifelse(row%%7==0,7,row%%7)
      +         x3<-ctype[row]
      +         fontCol<-'#000000'
      +         if(x3<=5)fontCol<-head(colors,1)
      +         if(x3>=12)fontCol<-tail(colors,1)
      +         text(68+x1*11,17-x2*3,paste(name,' ',label,sign,sep=''),col=fontCol)
      +     }
      +
      +     #=======================================          # 图例
      +     par(mar = c(5, 0, 15, 10))
      +     image(x=1, y=1:length(colors),z=t(matrix(1:length(colors))),col=rev(colors),axes=FALSE,xlab="",ylab="",xaxt="n")
      +     axis(4, at = 1:(nrow(labelcode)-1), labels=rev(labelcode$alias)[-1], col = "white", las = 1)
      +     abline(h=c(1:(nrow(labelcode)-2)+0.5), col = "white", lwd = 2, xpd = FALSE)
      +     if(output)dev.off()
      + }
      

      运行程序,生成静态图片。

      
      > data<-read.csv(file=filename(date),header=TRUE,fileEncoding="utf-8", encoding="utf-8")   # 定义数据源
      > path=''                                                                                  # 定义输出路径
      > summary(data,output=TRUE,path=path)      # 生成中国各省天气概况图
      RStudioGD
              2
      

      20141001_code

      代码量大概100行左右,就可以生成这么复杂的天气和地图结合的图片,R真的很神奇!

      3.5 可交互的静态图

      这是锦上添花的一步,静态图片对于一般应用来说就够了。但如果图片还能动起来,是不是会更吸引人呢?我们可以尝试生成基于HTML5的、有动态效果的图,通过recharts包调Echarts库实现基于HTML5的动画,生成会动的可交互的图片。

      由于recharts包没有发布的CRAN,我们需要用devtools包通过Github安装这个包。

      
      > library(devtools)                     # 加载devtools
      > install_github("taiyun/recharts")     # 下载安装recharts包
      > library(recharts)                     # 加载recharts包
      

      由于上面的天气概况是由离散值组成的,利用echarts的库,我们做一个连续值的可视化例子,比如白天气温和夜间气温。定义weather_html()函数,提供气温数据并调用recharts包,实现可视化的输出。

      
      > weather_html<-function(data=data,type='high',output=FALSE,path=''){    # 输入HTML的天气图
      +     if(type=='high') {                                   # 白天气温
      +         df<-data[,c('prov','high')]
      +         names(df)<-c("prov","气温")
      +         title<-paste(format(date,"%Y-%m-%d"),"中国各省白天气温",sep="")
      +         ofile<-paste(format(date,"%Y%m%d"),"_day.html",sep="")
      +     }else if(type=='low'){                               # 夜间气温
      +         df<-data[,c('prov','low')]
      +         names(df)<-c("prov","气温")
      +         title<-paste(format(date,"%Y-%m-%d"),"中国各省夜间气温",sep="")
      +         ofile<-paste(format(date,"%Y%m%d"),"_night.html",sep="")
      +     }
      +
      +     df[,1]<-substr(df[,1],0,2)                           # 数据格式整理
      +     df[which(df$prov=='黑龙'),]$prov<-'黑龙江'
      +     df[which(df$prov=='内蒙'),]$prov<-'内蒙古'
      +
      +     recharts.eMap <- eMap(df, namevar=1, datavar = 2, title=title)      # 数据JSON化处理
      +     if(output){                                                         # 输出HTML文件
      +         recharts.eMap$outList[c('chartid','type')]<-NULL
      +         writeLines(unlist(recharts.eMap$outList),paste(path,ofile,sep=''))
      +     }else{                                                              # 在浏览器中打开HTML网页
      +         plot(recharts.eMap)
      +     }
      + }
      

      运行程序,以HTML输出中国各省白天气温。

      
      > date<-as.Date('20141001',format='%Y%m%d')     # 设置日期
      > data<-read.csv(file=filename(date),header=TRUE,fileEncoding="utf-8", encoding="utf-8")      # 加载数据
      > path=''                                                                                     # 设置文件输出路径
      
      > weather_html(data,type='high',output=FALSE,path='')           # 输出中国各省白天气温
      [1] "气温"
      [1] "chart path C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\RtmpqCHFPY"
      

      程序会自动打开浏览器,呈现HTML的网页。

      w6

      运行程序,以HTML输出中国各省夜间气温。在网页中,通过鼠标对地图进行交互,移动左下角的温度条,选择最高温度30,最低温度8.8,中国地图中由西南到东北变为灰色,说明这些地区的温度不在8.8到30度之间。当鼠标路过海南省的时候,海南省呈现黄色,并提示出温度为23度。

      
      > weather_html(data,type='low', output=FALSE,path='')           # 中国各省夜间气温
      [1] "气温"
      [1] "chart path C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\RtmpqCHFPY"
      

      w7

      如果不需要在浏览器中打开,只能想存储生成的网页,可以在程序中设置output为TRUE,当前目录下会生成20141001_night.html的文件。

      
      > weather_html(data,type='low',output=TRUE,path='')
      [1] "气温"
      

      本文介绍了每日中国天气应用项目完整情况,并完成R语言部分的功能实现。那么下一篇文章,将介绍如何把R语言的功能代码封装成R包。

      转载请注明出处:
      http://blog.fens.me/r-app-china-weather/

      打赏作者

R语言的高质量图形渲染库Cairo

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-cairo/

r-cairo

前言

R语言不仅在统计分析,数据挖掘领域,计算能力强大。在数据可视化上,也不逊于昂贵的商业。当然,背后离不开各种开源软件包的支持,Cairo就是这样一个用于矢量图形处理的类库。

Cairo可以创建高质量的矢量图形(PDF, PostScript, SVG) 和 位图(PNG, JPEG, TIFF),同时支持在后台程序中高质量渲染!

本文将介绍,Cairo在R语言中的使用。

目录

  1. Cairo介绍
  2. Cairo安装
  3. Cairo使用

1. Cairo介绍

在信息领域中,cairo 是一个让用于提供矢量图形绘图的免费库,cairo 提供在多个背景下做 2D 的绘图,高级的更可以使用硬件加速功能。

虽然 cairo 是使用C语言撰写的,但是当使用 cairo 时,可以用许多其他种语言来使用,包括有 C++、C#、Java、Python、Perl、Ruby、Scheme、Smalltalk 以及许多种语言,cairo 在 GNU LGPL 与 Mozilla Public License (MPL) 两个认证下发布。

上文介绍,摘自:http://zh.wikipedia.org/wiki/Cairo_(绘图)

R语言Cairo接口的官方发布页:http://www.rforge.net/Cairo/

2. Cairo安装

系统环境

  • Linux: Ubuntu 12.04.2 LTS 64bit
  • R: 3.0.1 x86_64-pc-linux-gnu

Cairo基本库安装


~ sudo apt-get install libcairo2-dev
~ sudo apt-get install libxt-dev

~ R 
> install.packages("Cairo")
** R
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
Warning: ignoring .First.lib() for package ‘Cairo’
* DONE (Cairo)

3. Cairo使用

Cairo使用起来非常简单,和基础包grDevices中的函数对应。

  • CairoPNG: 对应grDevices:png()
  • CairoJPEG: 对应grDevices:jpeg()
  • CairoTIFF: 对应grDevices:tiff()
  • CairoSVG: 对应grDevices:svg()
  • CairoPDF: 对应grDevices:pdf()

我常用的图形输出,就是png和svg。

检查Cairo的兼容性:


~ R

> library(Cairo)
> Cairo.capabilities()
   png   jpeg   tiff    pdf    svg     ps    x11    win raster
  TRUE   TRUE  FALSE   TRUE   TRUE   TRUE   TRUE  FALSE   TRUE

下面比较一下 CairoPNG() 和 png() 输出效果。

1). 散点图


x<-rnorm(6000)
y<-rnorm(6000)

# PNG图
png(file="plot4.png",width=640,height=480)
plot(x,y,col="#ff000018",pch=19,cex=2,main = "plot")
dev.off()

CairoPNG(file="Cairo4.png",width=640,height=480)
plot(x,y,col="#ff000018",pch=19,cex=2,main = "Cairo")
dev.off()

# SVG图
svg(file="plot-svg4.svg",width=6,height=6)
plot(x,y,col="#ff000018",pch=19,cex=2,main = "plot-svg")
dev.off()

CairoSVG(file="Cairo-svg4.svg",width=6,height=6)
plot(x,y,col="#ff000018",pch=19,cex=2,main = "Cairo-svg")
dev.off()

以下为PNG图:

plot4

Cairo4

2). 三维截面图


x <- seq(-10, 10, length= 30)
y <- x
f <- function(x,y) { r <- sqrt(x^2+y^2); 10 * sin(r)/r }
z <- outer(x, y, f)
z[is.na(z)] <- 1

# PNG图
png(file="plot2.png",width=640,height=480)
op <- par(bg = "white", mar=c(0,2,3,0)+.1)
persp(x, y, z,
theta = 30, phi = 30,
expand = 0.5,
col = "lightblue",
ltheta = 120,
shade = 0.75,
ticktype = "detailed",
xlab = "X", ylab = "Y", zlab = "Sinc(r)",
main = "Plot"
)
par(op)
dev.off()

CairoPNG(file="Cairo2.png",width=640,height=480)
op <- par(bg = "white", mar=c(0,2,3,0)+.1)
persp(x, y, z,
theta = 30, phi = 30,
expand = 0.5,
col = "lightblue",
ltheta = 120,
shade = 0.75,
ticktype = "detailed",
xlab = "X", ylab = "Y", zlab = "Sinc(r)",
main = "Cairo"
)
par(op)
dev.off()

以下为PNG图:

plot2

Cairo2

3). 文字显示


library(MASS)
data(HairEyeColor)
x <- HairEyeColor[,,1]+HairEyeColor[,,2]

n <- 100
m <- matrix(sample(c(T,F),n^2,replace=T), nr=n, nc=n)

# PNG图
png(file="plot5.png",width=640,height=480)
biplot(corresp(m, nf=2), main="Plot")
dev.off()

CairoPNG(file="Cairo5.png",width=640,height=480)
biplot(corresp(m, nf=2), main="Cairo")
dev.off()

# SVG图
svg(file="plot-svg5.svg",width=6,height=6)
biplot(corresp(m, nf=2), main="Plot-svg")
dev.off()

CairoSVG(file="Cairo-svg5.svg",width=6,height=6)
biplot(corresp(m, nf=2), main="Cairo-svg")
dev.off()

以下为PNG图:

plot5

Cairo5

我们查看两个文件的属性:以png直接生成的图54KB,以CairoPNG生成的图43.8KB。

cairo-demo5

综上的3个例子,我分辨不出太大区别,只是Cairo感觉更淡、更柔和一些。

大家不妨找一些更复杂的图形来尝试着比较。

转载请注明出处:
http://blog.fens.me/r-cairo/

打赏作者

用Highcharts轻松构建交互性图表

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-highcharts/

nodejs-highcharts

前言

在大数据时代,数据可视化技术显得至关重要。一个设计优雅的信息图,能在一个600px*400px的画布上,显示出各种行业的全球市场的几百万条数据。

“如果图像能通过数据驱动,如果图像能动起来,如果图像支持交互,如果还能画股票K线图”,那才我想要的。Highchart是一个基于Javascript的跨浏览器的图形可视化工具,能满足我的需求。

目录

  1. highchart介绍
  2. highchart项目构建
  3. highchart使用
  4. highstock使用
  5. 项目源代码

1. highchart介绍

Highcharts是一个非常流行界面美观的纯Javascript图表库。它主要包括两个部分:Highcharts和Highstock。

Highcharts能够很简单便捷的在web网站或是web应用程序添加有交互性的图表,并且免费提供给个人学习、个人网站和非商业用途使用。目前HighCharts支持的图表类型有曲线图、区域图、柱状图、饼状图、散状点图和综合图表。

Highstock可以为您方便地建立股票或一般的时间轴图表。它包括先进的导航选项,预设的日期范围,日期选择器,滚动和平移等等。

官方网站:http://www.highcharts.com

中文资源:http://www.highcharts.me

2. highchart项目构建

系统环境

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19

初始化项目


~ D:\workspace\javascript>mkdir highchart && cd highchart 

~ D:\workspace\javascript\highchart>bower search highchart
Search results:

    highcharts git://github.com/ewen/bower-highcharts.git
    highcharts.com git://github.com/highslide-software/highcharts.com.git
    angular-highcharts-directive git://github.com/rootux/angular-highcharts-directive.git
    highcharts-components git://github.com/nesbert/highcharts.git
    highcharts-ng git://github.com/pablojim/highcharts-ng.git
    angular-highcharts git://github.com/frapontillo/angular-highcharts.git

~ D:\workspace\javascript\highchart>bower install highcharts.com
bower highcharts.com#*      not-cached git://github.com/highslide-software/highcharts.com.git#*
bower highcharts.com#*         resolve git://github.com/highslide-software/highcharts.com.git#*
bower highcharts.com#*        download https://github.com/highslide-software/highcharts.com/archive/v3.0.7.tar.gz
bower highcharts.com#*         extract archive.tar.gz
bower highcharts.com#*        resolved git://github.com/highslide-software/highcharts.com.git#3.0.7
bower highcharts.com#~3.0.7    install highcharts.com#3.0.7

highcharts.com#3.0.7 bower_components\highcharts.com

我们通过bower来下载highchart库,有关bower的安装和使用,请参考文章:bower解决js的依赖管理

通过Ant工具构建highcharts. Ant是一个用Java开发的自动化构建工具。


~ cd bower_components\highcharts.com

~ ant build
Buildfile: D:\workspace\javascript\highchart\bower_components\highcharts.com\build.xml

set.properties:

clean:
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\js\adapters
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\js\modules
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highcharts\gfx
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highcharts\js\adapters
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highcharts\js\modules
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highstock\gfx
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highstock\js\adapters
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highstock\js\modules
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\tests
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\coverage-report

assemble:

check-crlf:

do-lint:

lint:

minify:

compile:
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/highcharts.src.js
     [echo] Google Closure compiler: 463267 bytes -> 141575 bytes (52163 bytes zipped).
     [echo] Yahoo YUI Compressor: 463267 bytes -> 155872 bytes (57484 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/highstock.src.js
     [echo] Google Closure compiler: 579013 bytes -> 176349 bytes (64166 bytes zipped).
     [echo] Yahoo YUI Compressor: 579013 bytes -> 194135 bytes (70231 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/highcharts-more.src.js
     [echo] Google Closure compiler: 62849 bytes -> 21926 bytes (8265 bytes zipped).
     [echo] Yahoo YUI Compressor: 62849 bytes -> 22532 bytes (8682 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/highcharts-more.src.js
     [echo] Google Closure compiler: 62849 bytes -> 21926 bytes (8265 bytes zipped).
     [echo] Yahoo YUI Compressor: 62849 bytes -> 22532 bytes (8682 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/adapters/mootools-adapter.src.js
     [echo] Google Closure compiler: 8270 bytes -> 2406 bytes (1230 bytes zipped).
     [echo] Yahoo YUI Compressor: 8270 bytes -> 2338 bytes (1189 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/adapters/prototype-adapter.src.js
     [echo] Google Closure compiler: 9270 bytes -> 3330 bytes (1487 bytes zipped).
     [echo] Yahoo YUI Compressor: 9270 bytes -> 3251 bytes (1407 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/adapters/standalone-framework.src.js
     [echo] Google Closure compiler: 12261 bytes -> 5087 bytes (2328 bytes zipped).
     [echo] Yahoo YUI Compressor: 12261 bytes -> 5223 bytes (2318 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/exporting.src.js
     [echo] Google Closure compiler: 18002 bytes -> 7264 bytes (3266 bytes zipped).
     [echo] Yahoo YUI Compressor: 18002 bytes -> 7204 bytes (3332 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/data.src.js
     [echo] Google Closure compiler: 16086 bytes -> 4436 bytes (2048 bytes zipped).
     [echo] Yahoo YUI Compressor: 16086 bytes -> 4486 bytes (2066 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/drilldown.src.js
     [echo] Google Closure compiler: 11421 bytes -> 5553 bytes (2109 bytes zipped).
     [echo] Yahoo YUI Compressor: 11421 bytes -> 5678 bytes (2161 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/funnel.src.js
     [echo] Google Closure compiler: 6824 bytes -> 1974 bytes (1226 bytes zipped).
     [echo] Yahoo YUI Compressor: 6824 bytes -> 1885 bytes (1180 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/no-data-to-display.src.js
     [echo] Google Closure compiler: 3009 bytes -> 1379 bytes (846 bytes zipped).
     [echo] Yahoo YUI Compressor: 3009 bytes -> 1185 bytes (729 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/map.src.js
     [echo] Google Closure compiler: 34651 bytes -> 12719 bytes (5188 bytes zipped).
     [echo] Yahoo YUI Compressor: 34651 bytes -> 12889 bytes (5350 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/heatmap.src.js
     [echo] Google Closure compiler: 1195 bytes -> 579 bytes (511 bytes zipped).
     [echo] Yahoo YUI Compressor: 1195 bytes -> 594 bytes (519 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/annotations.src.js
     [echo] Google Closure compiler: 8776 bytes -> 3398 bytes (1668 bytes zipped).
     [echo] Yahoo YUI Compressor: 8776 bytes -> 3535 bytes (1777 bytes zipped).
     [echo]
     [echo] Minifying build/canvas-tools.src.js
     [echo] Google Closure compiler: 100698 bytes -> 57894 bytes (15848 bytes zipped).
     [echo] Yahoo YUI Compressor: 100698 bytes -> 59764 bytes (16054 bytes zipped).
     [echo]

BUILD SUCCESSFUL
Total time: 1 minute 19 seconds

在新生成的build/dist文件夹中,就能找到highchart.js, highstock.js,这两个用于发布的文件了。

highchart-build

用grunt创建一个静态服务器,grunt的使用说明,请参考文章:grunt让Nodejs规范起来

grunt的运行时环境,新建文件:package.json,Gruntfile.js


~ npm install grunt
~ npm install grunt-contrib-connect

~ vi packages.json
{
    "name": "nodejs-highcharts",
    "version": "0.0.1",
    "private": true,
    "scripts": {
        "start": "node app.js"
    },
    "dependencies": {
    },
    "devDependencies": {
        "grunt": "~0.4.1",
        "grunt-contrib-connect": "~0.4.1"
    }
}

~ vi Gruntfile.js
'use strict';
module.exports = function(grunt) {
    grunt.initConfig({
      connect: {
        server: {
          options: {
            port: 9001,
            base: './',
            keepalive: true
          }
        }
      }
    });

    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.registerTask('server', ['connect']);
};

新建一个测试文件:demo1.html


<!DOCTYPE html>
<html>
<head>
<title>HighCharts</title>
<script src="bower_components/highcharts.com/lib/jquery-1.10.1.js"></script>
<script src="bower_components/highcharts.com/build/dist/highcharts/js/highcharts.js"></script>
</head>
<body>

<div id="container" style="height: 400px"></div>
<script>
$(function () {
Highcharts.setOptions({
colors: ['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4']
});

$('#container').highcharts({
chart: {
type: 'pie'
},

series: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
}]
});
});
</script>
</body>
</html>

由于highcharts是依赖于jQuery的,所以我们也需要引入jquery.js

启动grunt server


~ D:\workspace\javascript\highchart>grunt server
Running "connect:server" (connect) task
Waiting forever...
Started connect web server on 127.0.0.1:9001.

浏览器打开:http://localhost:9001/demo1.html

highchart-pie

3. highchart使用

找到highchart的官网,查看demo:http://www.highcharts.com/demo/,支持好几十种可视化图形。

我最常用的是曲线图:

新建文件

  • line.html:网页文件
  • line.js:脚本文件,进行简单封装
  • line.json:数据文件

安装moment类库,用于时常日期转换

~ bower install moment

json/line.json


[
    {
        "name": "TF1312",
        "data": [
            [20131104091540,4.52],
            [20131104091600,4.51],
            [20131104091740,4.18],
            [20131104091843,4.26],
            [20131104091900,4.23],
            [20131104092053,4.2],
            [20131104092138,4.28],
            [20131104092256,4.83],
            [20131104092307,4.83],
            [20131104092439,4.88],
            [20131104092556,7.1],
            [20131104092623,7.12]
        ]
    },
    {
        "name": "TF1403",
        "data": [
            [20131104091540,4.23],
            [20131104091600,4.22],
            [20131104091740,4.11],
            [20131104091843,4.14],
            [20131104091900,4.13],
            [20131104092052,4.12],
            [20131104092126,4.15],
            [20131104092222,4.33],
            [20131104092306,4.35],
            [20131104092440,4.37],
            [20131104092556,5.1],
            [20131104092600,5.1]
        ]
    },
    {
        "name": "TF1406",
        "data": [
            [20131104091540,3.92],
            [20131104091600,3.92],
            [20131104091700,3.92],
            [20131104091800,3.92],
            [20131104091900,3.92],
            [20131104092052,3.88],
            [20131104092110,3.9 ],
            [20131104092254,3.8],
            [20131104092302,3.89],
            [20131104092400,3.9],
            [20131104092556,4.43],
            [20131104092634,4.44]
        ]
    }
]

line.html


<!DOCTYPE html>
<html>
<head>
<title>HighCharts - SplineChart</title>
<script src="bower_components/highcharts.com/lib/jquery-1.10.1.js"></script>
<script src="bower_components/moment/moment.js"></script>
<script src="bower_components/highcharts.com/build/dist/highcharts/js/highcharts.js"></script>
</head>
<body>
<div id="container" style="height: 400px"></div>
<script src="js/line.js"></script>
<script>
createSpline('#container','json/line.json',true);
</script>
</body>
</html>

js/line.js


/**
 * hightcharts global
 */
Highcharts.setOptions({
    global: {useUTC: false}
});

/**
 * my credits
 */
var defaultCredits = {
    href: 'http://blog.fens.me',
    position: {x: -30, y: -30},
    style: {color: '#191a37', fontWeight: 'bold'},
    text: 'http://blog.fens.me'
}

/**
 * transfer Datetime: 20130101101010 to time
 */
function transferDate(json) {
    for (var i = 0; i < json.length; i++) {
        var obj = json[i].data;
        for (var j = 0; j < obj.length; j++) {
            obj[j][0] = moment(obj[j][0], 'YYYYMMDDHHmmss').toDate().getTime();
        }
    }
    return json;
}

/**
 * Spline Chart Template
 */
function getSplineChart(series) {
    return {
        chart: {
            type: 'spline',
            animation: Highcharts.svg,
            marginRight: 10
        },
        title: {
            text: 'Title'
        },
        credits: defaultCredits,
        xAxis: {
            maxPadding: 0.05, minPadding: 0.05, type: 'datetime', tickWidth: 5,
            labels: {
                formatter: function() {
                    return Highcharts.dateFormat('%H:%M:%S', this.value);
                }
            }
        },
        yAxis: {
            title: {text: 'yAxis'},
            plotLines: [
                {value: 0, width: 1, color: '#808080'}
            ]
        },
        tooltip: {
            formatter: function () {
                return '<b>' + this.series.name + '</b><br/>' +
                    Highcharts.dateFormat('%H:%M:%S', this.x) + '<br/>' +
                    Highcharts.numberFormat(this.y, 2);
            }
        },
        legend: {enabled: true},
        exporting: {enabled: false},
        plotOptions: {
            spline: {
                lineWidth: 4, states: {hover: {lineWidth: 5}},
                marker: {enabled: false},
                pointInterval: 3600000,
                pointStart: Date.UTC(2009, 9, 6, 0, 0, 0)
            }
        },
        series: series
    };
}

/**
 * Create a spline
 */
function createSpline(id,url,transDate){
    $.get(url, function (json) {
        if(transDate||false) json=transferDate(json);
        $(id).highcharts(getSplineChart(json));
    });
}

查看效果图

highchart-line

4. highstock使用

到highchart的官网,查看stock demo:http://www.highcharts.com/stock/demo/,同样支持好多种可视化图形。

选择K线图(candlestick)实验:

新建文件:

  • candlestick.html:网页文件
  • candlestick.js:脚本文件,进行简单封装
  • candlestick.json:数据文件

json/candlestick.json, 部分数据


[
[1162771200000,78.95,80.06,78.43,79.71,15525782],
[1162857600000,80.45,81.00,80.13,80.51,18788494],
[1162944000000,80.02,82.69,79.89,82.45,24699702],
[1163030400000,82.90,84.69,82.12,83.34,32973747],
[1163116800000,83.55,83.60,82.50,83.12,13358721],
[1163376000000,83.22,84.45,82.64,84.35,16105532],
[1163462400000,84.80,85.00,83.90,85.00,21054939],
[1163548800000,85.05,85.90,84.00,84.05,23522893],
[1163635200000,84.87,86.30,84.62,85.61,24796743],
[1163721600000,85.14,85.94,85.00,85.85,16667310],
[1163980800000,85.40,87.00,85.20,86.47,20394365],
[1164067200000,87.42,88.60,87.11,88.60,22274133],
[1164153600000,88.99,90.75,87.85,90.31,24013531],
[1164326400000,89.53,93.08,89.50,91.63,18538136],
[1164585600000,92.51,93.16,89.50,89.54,38411382],
[1164672000000,90.36,91.97,89.91,91.81,37039840]
]

candlestick.html


<!DOCTYPE html>
<html>
<head>
<title>HighStock - Candlestick</title>
<script src="bower_components/highcharts.com/lib/jquery-1.10.1.js"></script>
<script src="bower_components/moment/moment.js"></script>
<script src="bower_components/highcharts.com/build/dist/highstock/js/highstock.js"></script>
</head>
<body>
<div id="container" style="height: 500px"></div>
<script src="js/candlestick.js"></script>
<script>
createCandlestick('#container','json/candlestick.json');
</script>
</body>
</html>

js/candlestick.js


/**
 * my credits
 */
var defaultCredits = {
    href: 'http://blog.fens.me',
    position: {x: -30, y: -30},
    style: {color: '#191a37', fontWeight: 'bold'},
    text: 'http://blog.fens.me'
}
/**
 * Paser Origin Data to Chart
 */
function paserStockData(data,transferDate){
    var ohlc = [],
        volume = [];

    for (var i = 0; i < data.length; i++) {
        if(transferDate||false){
            data[i][0] = moment(data[i][0], 'YYYYMMDDHHmmss').toDate().getTime();
        }

        ohlc.push([
            data[i][0], // the date
            data[i][1], // open
            data[i][2], // high
            data[i][3], // low
            data[i][4] // close
        ]);

        volume.push([
            data[i][0], // the date
            data[i][5] // the volume
        ])
    }

    var groupingUnits = [
        ['week',[1]],
        ['month',[1, 2, 3, 4, 5, 6]]
    ];

    var series=[{
        type: 'candlestick',
        name: 'Stock',
        data: ohlc,
        dataGrouping: {
            units: groupingUnits
        }
    }, {
        type: 'column',
        name: 'Volume',
        data: volume,
        yAxis: 1,
        dataGrouping: {
            units: groupingUnits
        }
    }];
    return series;
}

/**
 * Spline Chart Template
 */
function getCandlestick(series) {
    return {
        rangeSelector: {
            selected: 1
        },
        credits:defaultCredits,
        title: {
            text: 'Title'
        },
        yAxis: [{
            title: {
                text: 'Price'
            },
            height: 200,
            lineWidth: 2
        }, {
            title: {
                text: 'Volume'
            },
            top: 300,
            height: 100,
            offset: 0,
            lineWidth: 2
        }],
        series:series
    }
}

/**
* Create a Candlestick
*/
function createCandlestick(id,url,transDate){
    $.get(url, function (json) {
        json=paserStockData(json,transDate);
        $(id).highcharts('StockChart',getCandlestick(json));
    });
}

查看效果图
highchart-candlestick

5. 项目源代码

项目已经上传到github,感兴趣的同学自行下载。

https://github.com/bsspirit/highcharts-demo

转载请注明出处:
http://blog.fens.me/nodejs-highcharts/

打赏作者

websocket服务器监控

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-websocket-monitor/

monitor

前言
Websocket技术的强大之处在于,打通了浏览器和服务器的双向通信的高速通道。有了如此神器,我们就可以在web上面“为所欲为”了。

在web上面,模拟一个Shell窗口,已经不稀奇了。我们再通过shell获得Linux系统(CPU,IO)信息,时时返回到web端,通过Highcharts生成系统监控图,是不是很高端!?

快动起来吧,你也可以做到的。

目录

  1. 系统架构设计
  2. 通过Shell获得CPU及IO信息
  3. 创建nodejs项目
  4. 实现websocket服务器端
  5. 实现websocket客户端
  6. 用Highcharts生成时时系统监控图

1. 系统架构设计

monitor-architect

我的系统环境:

  • Linux: Ubuntu 12.04.2 LTS 64bit deskop
  • Nodejs: npm 1.2.21, node v0.11.2

~ uname -a
Linux conan-deskop 3.5.0-23-generic #35~precise1-Ubuntu SMP Fri Jan 25 17:13:26 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
~ cat /etc/issue
Ubuntu 12.04.2 LTS \n \l

~ npm -v
1.2.21
~ node -v
v0.11.2

2. 通过Shell获得CPU及IO信息

安装软件包sysstat

sudo apt-get install sysstat

配置sysstat


~ sudo vi /etc/default/sysstat
ENABLED="true"

~ sudo /etc/init.d/sysstat restart

查看CPU的使用情况


~ sar  1
Linux 3.5.0-23-generic (conan-deskop)   2013年08月23日  _x86_64_        (2 CPU)
18时13分39秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
18时13分40秒     all      0.52      0.00      0.52      0.00      0.00     98.96
18时13分41秒     all      0.52      0.00      0.52      0.00      0.00     98.96
18时13分42秒     all      0.00      0.00      0.52      0.00      0.00     99.48
18时13分43秒     all      0.00      0.00      1.03      0.00      0.00     98.97
18时13分44秒     all      0.00      0.00      0.52      0.00      0.00     99.48

我们只关系idle的情况。公式:CPU时间 = 100 – idle

查看内存的情况


~ sar -r 1
Linux 3.5.0-23-generic (conan-deskop)   2013年08月23日  _x86_64_        (2 CPU)
18时14分30秒 kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact
18时14分31秒    749672   1301452     63.45    123460    643808   2205152     53.18    716128    440348
18时14分32秒    749672   1301452     63.45    123460    643808   2205152     53.18    716128    440348
18时14分33秒    749672   1301452     63.45    123460    643808   2205152     53.18    716128    440348
18时14分34秒    749672   1301452     63.45    123460    643808   2205152     53.18    716128    440348

我们只关系memused的情况。公式:内存用量 = 100 – memused

3. 创建nodejs项目

创建一个express3的项目, 还没有装好nodejs环境的同学,请参考:准备Nodejs开发环境Ubuntu


~ cd /home/conan/nodejs
~ express -e nodejs-alert
~ cd nodejs-alert
~ sudo npm install

实现websocket通信,我们还需要安装socket.io


~ sudo npm install socket.io

4. 实现websocket服务器端

修改app.js文件,webscoket的实现用到socket.io,读取系统进程用到child_process。


var express = require('express')
  , path = require('path')
  , app = express()
  , server = require('http').createServer(app)
  , io = require('socket.io').listen(server)
  , worker = require('child_process');

// 其他代码省略
....

5. 实现websocket客户端

1). 首先是view/alert.html


~ vi view/alert.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Socket.io - System Alert</title>
<script src="javascripts/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="javascripts/sysAlert.js"></script>
</head>
<body>
<h1>Socket.io - System Alert</h1>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
<script src="javascripts/highcharts/highcharts.js"></script>
<script src="javascripts/highcharts/modules/exporting.js"></script>
</body>
</html>

2). 下载必须的jquery.min.js和highcharts的整个库
3). 增加 javascripts/sysAlert.js 文件,用于客户端的实现。

  • 调用socket.io实现和服务器的通信
  • 调用Highcharts生成动态图

    // highcharts部分代码
    Highcharts.setOptions({
        global: {
            useUTC: false
        }
    });

    $('#container').highcharts({
        chart: {
            type: 'spline',
            animation: Highcharts.svg,
            marginRight: 10
        },
    
    // 代码省略
    ....
    });

    //// socket.io部分代码
    var socket = io.connect('http://192.168.1.20');
    socket.on('system', function (data) {

    // 代码省略
    ...
    });

下面是生成的 系统监控动态图!

6. 用Highcharts生成时时系统监控图

alert

内存,占用情况大概为84%,保持稳定。
CPU,占用情况。每次CPU有变化时,我都在做矩阵计算。

R语言矩阵计算代码


a<-matrix(1:1000000,ncol=1000)->b
b %*% a

转载请注明出处:
http://blog.fens.me/nodejs-websocket-monitor/

打赏作者