自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基于R shinydashboard的道路交通可視化案例

大數(shù)據(jù) 數(shù)據(jù)可視化
個(gè)作品剛剛獲得“中國(guó)電科杯”城市數(shù)據(jù)創(chuàng)新大賽的城市交通專項(xiàng)獎(jiǎng),現(xiàn)在作為案例分享出來供同行交流討論。虛的就不說了,此文只討論技術(shù)。

[[197973]]

作品概述

這個(gè)作品剛剛獲得“中國(guó)電科杯”城市數(shù)據(jù)創(chuàng)新大賽的城市交通專項(xiàng)獎(jiǎng),現(xiàn)在作為案例分享出來供同行交流討論。虛的就不說了,此文只討論技術(shù)。

先上圖:

實(shí)時(shí)道路交通可視化

實(shí)時(shí)道路擁堵排名

歷史路況時(shí)間序列圖

每日每小時(shí)道況熱力圖

每小時(shí)內(nèi)道況南丁格爾玫瑰圖

每小時(shí)內(nèi)總體路況圓盤圖

各道路每日擁堵時(shí)長(zhǎng)排名

實(shí)時(shí)路況數(shù)據(jù)下載

這個(gè)作品的構(gòu)建過程與設(shè)計(jì)工具如下:

  • 原型設(shè)計(jì):在構(gòu)建dashboard之前要先思考要做哪些分析和可視化,這些圖形如何布局,顏色風(fēng)格如何,交互效果如何做……這些都需要設(shè)計(jì)原型,這里用的是Axure;
  • 數(shù)據(jù)采集:這里主要采集的是深圳市道路交通委員會(huì)網(wǎng)站上的道路交通指數(shù)數(shù)據(jù)。主要是分兩種采集方式:一種是在shinydashboard中內(nèi)嵌的實(shí)時(shí)爬蟲,這個(gè)用R實(shí)現(xiàn);另一種是用Python爬蟲程序在服務(wù)器上持續(xù)采集一個(gè)月的數(shù)據(jù)作為歷史數(shù)據(jù)進(jìn)行數(shù)據(jù)分析。政府網(wǎng)站一般技術(shù)含量都很低,沒什么反爬措施,因此只要不是爬得太猛把他們服務(wù)器搞垮了,爬起來沒有任何難度。
  • 數(shù)據(jù)存儲(chǔ):主要用來存儲(chǔ)歷史數(shù)據(jù),本作品以5分鐘為間隔采集一次,一個(gè)月下來也有百萬行數(shù)據(jù)。這里用到的是MySQL。
  • 數(shù)據(jù)處理、分析和可視化:這步是核心,下文細(xì)說。
  • CSS美化:原生的shinydashboard是很丑的,好在它支持引入CSS,所以可以通過CSS對(duì)顏色、文字大小等進(jìn)行重新設(shè)計(jì),以達(dá)到整體風(fēng)格的統(tǒng)一、美觀。

shiny與shinydashboard的特點(diǎn)

shiny是Rstudio出品的一個(gè)可以在R中構(gòu)建交互式網(wǎng)頁(yè)的引擎,shinydashboard則是基于shiny提供的一套快速搭建dashboard的工具。

先談?wù)剆hiny的優(yōu)缺點(diǎn):

  • 能以較低的學(xué)習(xí)成本實(shí)現(xiàn)交互式網(wǎng)頁(yè)設(shè)計(jì)。數(shù)據(jù)分析師畢竟不是前端工程師,無需去學(xué)習(xí)HTML、CSS和JavaScript那一大堆東西(當(dāng)然懂更好,也沒必要完全區(qū)分開來,至少騰訊就希望招懂前端的數(shù)據(jù)分析師或懂?dāng)?shù)據(jù)分析的前端工程師)也能很快構(gòu)建出一個(gè)交互式網(wǎng)頁(yè)。
  • 開發(fā)原型成本低。一個(gè)綜合能力強(qiáng)的數(shù)據(jù)分析師(比如我),一個(gè)人就能快速地實(shí)現(xiàn)數(shù)據(jù)采集、數(shù)據(jù)處理、數(shù)據(jù)分析和數(shù)據(jù)可視化原型設(shè)計(jì)的全過程,這個(gè)時(shí)間成本遠(yuǎn)遠(yuǎn)小于在一支分工明確的團(tuán)隊(duì)上下游間的磨合和交接。
  • 基本滿足常用的交互式效果。所以作為一個(gè)原型也是夠用了,產(chǎn)品經(jīng)理可以直接提供給開發(fā)人員,分析師也可以直接拿來向領(lǐng)導(dǎo)匯報(bào)。
  • 基本不支持動(dòng)態(tài)效果。至少到現(xiàn)在shiny連個(gè)支持動(dòng)畫的接口都沒有,更別說前端那些炫酷的動(dòng)態(tài)效果,這些都實(shí)現(xiàn)不了。
  • 計(jì)算速度慢。一旦數(shù)據(jù)量大或者數(shù)據(jù)處理過程復(fù)雜或者圖形構(gòu)造復(fù)雜,就會(huì)卡得比較久。不過速度慢是R的天生缺陷。
  • 展示內(nèi)容有限。想在shiny中展示什么,前提條件是shiny有對(duì)應(yīng)的接口,否則白搭,這就是R中可以生成動(dòng)畫但卻無法在shiny中展示的原因。
  • 部署困難。R中有另一個(gè)做dashboard的包flexdashboard,比shiny簡(jiǎn)單而且直接生成一個(gè)html,但是shiny由于是Rstudio的商業(yè)軟件,所以受到的限制很多。拿部署來說,目前有兩種方式:一種是自己搭建局域服務(wù)器,不過這種方式只能內(nèi)部人自己看,無法放在互聯(lián)網(wǎng)上公開;另一種方式就是部署到shinyapps.io網(wǎng)站上去,不過這個(gè)網(wǎng)站本身奇卡無比,十分影響體驗(yàn)。

所用工具包

再談?wù)凴中所用的包,主要分類兩類:數(shù)據(jù)處理和可視化。

數(shù)據(jù)處理包:

  • rvest用來做實(shí)時(shí)爬蟲,每次程序一啟動(dòng)就開始做***的實(shí)時(shí)爬蟲。
  • plyr和dplyr主要做數(shù)據(jù)篩選、排序、聚合計(jì)算等。
  • stringr用來對(duì)字符串分割、轉(zhuǎn)換等。
  • data.table用來讀取大量的歷史數(shù)據(jù)并做一些簡(jiǎn)單的處理。
  • reshape2用來對(duì)數(shù)據(jù)框做變形處理。

可視化包:

  • shiny和shinydashboard用來搭建基本的網(wǎng)頁(yè)結(jié)構(gòu)和內(nèi)容。
  • ggplot2這個(gè)神器應(yīng)該無人不知無人不曉,用來做基本的可視化。
  • ggiraph這也是個(gè)神奇的包,能把ggplot2的圖形轉(zhuǎn)化成交互式圖形,與ggplot2堪稱絕配。
  • dygraphs用來畫交互式的時(shí)間序列折線圖。
  • DT用來呈現(xiàn)交互式表格。

構(gòu)建shinydashboard

一個(gè)shiny程序基本包含兩部分:ui.R和server.R。其中ui.R主要用來設(shè)計(jì)限定網(wǎng)頁(yè)結(jié)構(gòu),比如每一行是什么圖形或內(nèi)容,尺寸大小如何設(shè)定,文字怎么插入,控件的位置和編號(hào)等等——基本上可以概括為:一切關(guān)于外在結(jié)構(gòu)而不涉及內(nèi)在計(jì)算的都在ui.R中設(shè)計(jì)。反之,server.R就主要用來做數(shù)據(jù)處理、計(jì)算和可視化,并把結(jié)果映射至ui.R中,所以它才是核心,代碼也長(zhǎng)得多。

本作品的ui.R核心代碼如下:

  1. dashboardPage( 
  2.   dashboardHeader(title = "深圳道路的數(shù)據(jù)畫像",titleWidth = 220), 
  3.   dashboardSidebar( 
  4.     sidebarMenu( 
  5.     menuItem(iconv("實(shí)時(shí)路況展示",to="UTF-8"), tabName = "realtime_traffic", icon = icon("road")), 
  6.     radioButtons(inputId = "choose_direction", label = "請(qǐng)選擇一個(gè)方向:",selected=1,choiceNames=c("1(東->西 或 北->南)","2(西->東 或 南->北)"),choiceValues=1:2), 
  7.     radioButtons(inputId = "rank_class", label = "請(qǐng)選擇道路排名類別:",selected="擁堵排名",choices=c("擁堵排名","通暢排名")), 
  8.     numericInput(inputId = "rank_num",label = "請(qǐng)輸入道路交通排名數(shù)量:",min = 5,max = 100,step = 1,value=20), 
  9.     menuItem(iconv("道路畫像分析",to="UTF-8"), icon = icon("area-chart"), tabName = "statistics"), 
  10.     selectInput(inputId = "choose_road",label = "請(qǐng)選擇一條道路:",choices = all_roads), 
  11.     radioButtons(inputId = "choose_direction2", label = "請(qǐng)選擇一個(gè)方向:",selected=1,choiceNames=c("1(東->西 或 北->南)","2(西->東 或 南->北)"),choiceValues=1:2), 
  12.     menuItem(iconv("歷史路況回顧",to="UTF-8"), icon = icon("calendar"), tabName = "history"), 
  13.     dateInput(inputId = "choose_date", label = "請(qǐng)選擇4月的一天:",value = "2017-04-01",min = "2017-04-01",max = "2017-04-30"), 
  14.     radioButtons(inputId = "choose_direction3", label = "請(qǐng)選擇一個(gè)方向:",selected="東->西",choices=c("東->西","西->東","北->南","南->北")), 
  15.     radioButtons(inputId = "rank_class2", label = "請(qǐng)選擇道路排名類別:",selected="擁堵排名",choices=c("擁堵排名","通暢排名")), 
  16.     numericInput(inputId = "rank_num2",label = "請(qǐng)輸入道路交通排名數(shù)量:",min = 5,max = 100,step = 1,value=20), 
  17.     menuItem(iconv("實(shí)時(shí)數(shù)據(jù)下載",to="UTF-8"),tabName = "data_download",icon = icon("database")) 
  18.  ),width = 220), 
  19.   dashboardBody( 
  20.     tags$head( 
  21.       tags$link(rel = "stylesheet", type = "text/css", href = "custom.css")), 
  22.     tabItems( 
  23.       tabItem("realtime_traffic"
  24.               fluidRow( 
  25.                 box(ggiraphOutput("map"),width = 12,solidHeader = T,collapsible = T) 
  26.               ), 
  27.               fluidRow( 
  28.                 box(ggiraphOutput("rank1"),width = 6,solidHeader = T,collapsible = T), 
  29.                 box(ggiraphOutput("rank2"),width = 6,solidHeader = T,collapsible = T) 
  30.               ), 
  31.               fluidRow( 
  32.                 box(ggiraphOutput("rank3"),width = 6,solidHeader = T,collapsible = T), 
  33.                 box(ggiraphOutput("rank4"),width = 6,solidHeader = T,collapsible = T) 
  34.               ) 
  35.       ), 
  36.       tabItem("statistics"
  37.               fluidRow( 
  38.                 box(dygraphOutput("ts_history"),width = 12,solidHeader = T,collapsible = T) 
  39.                 ), 
  40.               fluidRow( 
  41.                 box(ggiraphOutput("heat"),width = 12,solidHeader = T,collapsible = T) 
  42.               ), 
  43.               fluidRow( 
  44.                 box(ggiraphOutput("polar_weekdays"),width = 6,solidHeader = T,collapsible = T), 
  45.                 box(ggiraphOutput("polar_holidays"),width = 6,solidHeader = T,collapsible = T) 
  46.               ) 
  47.               ), 
  48.       tabItem("history"
  49.               # fluidRow( 
  50.               #   box(img(src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3245526806,2208748886&fm=21&gp=0.jpg"),width = 12,solidHeader = T) 
  51.               # )), 
  52.               fluidRow( 
  53.                 box(ggiraphOutput("history_bars"),width = 12,solidHeader = T,collapsible = T) 
  54.               ), 
  55.               fluidRow( 
  56.                 box(ggiraphOutput("day_rank"),width = 12,solidHeader = T,collapsible = T) 
  57.               )), 
  58.       tabItem("data_download"
  59.               fluidRow( 
  60.               box( 
  61.                 dataTableOutput("rawdata"),width = 12 
  62.               ) 
  63.               ), 
  64.               fluidRow( 
  65.                 p("數(shù)據(jù)來源:",strong(a("深圳市交通運(yùn)輸委員會(huì)",href="http://sztocc.sztb.gov.cn/roadcongmore.aspx"))) 
  66.               ), 
  67.               downloadButton("downloadCsv""下載實(shí)時(shí)數(shù)據(jù)"
  68.               ) 
  69.       ) 
  70.       )) 

這些代碼基本是在模板的基礎(chǔ)上改,menuItem()是左側(cè)sidebar里的一個(gè)子頁(yè)面,tabName是這個(gè)頁(yè)面的名稱,它來指定后來的各個(gè)布局元素存放在哪個(gè)頁(yè)面里。radioButtons是單選框,numericInput是數(shù)字輸入框,dateInput是日期選擇框,這些控件都有唯一一個(gè)id來標(biāo)識(shí),以input$id來取它們的值傳輸至server.R中。

在dashboardBody中,每個(gè)tabItem代表一個(gè)子頁(yè)面,這跟前面的tabName一一對(duì)應(yīng);里面的fluidRow表示一行,box是基本的容器,可以放圖形或其他輸出內(nèi)容(也可以不加box,不過這樣沒法設(shè)定寬度),每個(gè)box的寬度***為12,同一行的多個(gè)box可以各自設(shè)定總和為12的寬度(這一點(diǎn)比flexdashboard自由)。

注意到代碼里的box的內(nèi)容基本都有OutPut后綴(如ggiraphOutput、dataTableOutput、dygraphOutput等),這是那些包提供的shiny接口函數(shù)——換句話說如果一個(gè)包里有這種類似于以O(shè)utput的shiny接口函數(shù),它產(chǎn)出的內(nèi)容才能放在shiny中,否則不行(這一點(diǎn)限制比不上flexdashboard)。

shinydashboard默認(rèn)的風(fēng)格很丑,好在它支持CSS,tag$head(tag$link())就可以引進(jìn)CSS,不過這個(gè)CSS必須放在項(xiàng)目路徑下的www文件夾中。CSS正常地寫就好,如果遇到失效的,可能是優(yōu)先級(jí)的問題,就多加點(diǎn)前綴。鑒于CSS是前端領(lǐng)域的知識(shí),本文不多說。

加載包與讀取數(shù)據(jù)都可以放在shinyServer()外面執(zhí)行。關(guān)于數(shù)據(jù),要分為兩種:一種是固定不變的,這種按照常規(guī)的數(shù)據(jù)賦值方法即可;另一種是隨著用戶的交互而動(dòng)態(tài)改變的,這種要加個(gè)reactive()函數(shù),比如:

  1. traffic_choosen = reactive(traffic[traffic$direction_id == input$choose_direction, 1: 7]) 
  2. roads_map < - reactive({ 
  3.     roads_map < - join(roads, traffic_choosen(), type="inner"
  4.     roads_map 
  5. }) 

這里的traffic_choosen和roads_map都是根據(jù)用戶在名為direction_id的控件中的選擇值篩選的子集,所以它是動(dòng)態(tài)可變的,都加了一個(gè)reactive();不過要注意加了reactive()后,traffic_choosen和roads_map就不是一個(gè)變量,而是一個(gè)函數(shù),如果要調(diào)用這個(gè)動(dòng)態(tài)變量值,就必須要加括號(hào)。

后面就是給ui.R中的框架填充內(nèi)容,一般都是以output$id來指定。

  1. output$ts_history <- renderDygraph({ 
  2.   df1 <- (traffic_history %>% filter(road == input$choose_road & direction_id==1))[,c("time","index")] 
  3.   df2 <- (traffic_history %>% filter(road == input$choose_road & direction_id==2))[,c("time","index")] 
  4.   df <- full_join(df1,df2,by="time"
  5.   df <- df[!duplicated(df$time),] 
  6.   rownames(df) <- df$time 
  7.   df$time <- NULL 
  8.   colnames(df) <- c("方向1","方向2"
  9.   dygraph(df,main = paste(input$choose_road,"4月內(nèi)歷史交通指數(shù)",sep="")) %>%  
  10.     dyOptions(colors=c("orange","steelblue"),axisLineColor = "#FEFEFE",drawGrid = F) %>% 
  11.     dyLegend(show="onmouseover",labelsSeparateLines = T) %>% 
  12.     dyRangeSelector() %>%  
  13.      dyUnzoom() 
  14. }) 

比如這段代碼就指定了一個(gè)id為ts_history的用dygraph包畫的時(shí)間序列交互圖,而在ui.R中要與其呼應(yīng):

  1. box(dygraphOutput("ts_history"), width=12, solidHeader=T, collapsible=T) 

比如這段代碼就指定了一個(gè)id為ts_history的用dygraph包畫的時(shí)間序列交互圖,而在ui.R中要與其呼應(yīng):

  1. box(dygraphOutput(“ts_history”), width=12, solidHeader=T, collapsible=T) 

注意到畫圖語句外層都有一個(gè)render為前綴的函數(shù)(renderDygraph、renderggiraph、renderDataTable),同樣這也是shiny接口的標(biāo)志,必須以這個(gè)函數(shù)轉(zhuǎn)換之后才能在shiny中傳輸。

server.R中也可以捕獲用戶的在圖形中的交互行為,聯(lián)想到ggiraph交互圖形中總有一個(gè)參數(shù)是data_id,之前以為填什么不重要,但是在這里起作用了——用戶在某處點(diǎn)擊一下,便捕獲到該處的data_id值。

  1. selected_road <- reactive({ 
  2. if( is.null(input$map_selected)){ 
  3.   NA 
  4. else input$map_selected 
  5. }) 

這段代碼的含義就是:如果用戶沒有選中某條道路,select_road就是NA;反之就是用戶選中的那條道路。

通過響應(yīng)捕捉,可以進(jìn)一步強(qiáng)化交互性。

主要圖形解析

下面開始對(duì)前面的幾張圖的設(shè)計(jì)思路與處理方法做個(gè)簡(jiǎn)單的介紹。

實(shí)時(shí)道路交通可視化

這個(gè)圖最為復(fù)雜,它首先以深圳地圖為底圖,然后再疊加路徑圖。這個(gè)路徑圖是100+條道路的經(jīng)緯度坐標(biāo)畫成的。而這些道路的經(jīng)緯度坐標(biāo)獲取難度就比較大:首先在百度地圖上查詢道路起點(diǎn)和終點(diǎn)的坐標(biāo),然后調(diào)用百度地圖API,輸入起點(diǎn)和終點(diǎn)的坐標(biāo),查詢駕車路線,會(huì)返回一串散點(diǎn)的坐標(biāo),在ggplot2中把這些散點(diǎn)連接成path就可以把道路可視化出來;不過這些點(diǎn)的坐標(biāo)有可能不準(zhǔn),所以還需要人工校對(duì)。這些路徑的顏色映射的是rvest采集的該道路實(shí)時(shí)的交通指數(shù)——由于交委網(wǎng)站上在任一時(shí)刻并不總是會(huì)有全量的數(shù)據(jù),因此本圖中只展示有數(shù)據(jù)的道路的交通狀況。從這張圖中可以一眼看出整個(gè)深圳市總體的道路狀況,一眼可看出哪條道路最堵。

實(shí)時(shí)道路排名

這個(gè)比較簡(jiǎn)單,根據(jù)用戶選擇的排名種類和排名數(shù)據(jù)對(duì)實(shí)時(shí)數(shù)據(jù)進(jìn)行篩選、排序,以條形圖的方式展現(xiàn)即可。

歷史路況時(shí)間序列圖

這個(gè)時(shí)間序列圖把一條道路一個(gè)月內(nèi)每5分鐘的交通指數(shù)都可視化出來了。這個(gè)圖形很神奇,橫軸縱軸都可以縮放選擇,下方有縮略圖。

每日每小時(shí)道況熱力圖

這個(gè)熱力圖放在大屏上就像一座墻,倒還是挺壯觀的。這是一個(gè)30*24的熱力圖,每一塊映射每小時(shí)內(nèi)的平均交通指數(shù),用以呈現(xiàn)周期性規(guī)律。

每小時(shí)內(nèi)道況南丁格爾玫瑰圖

這里分為工作日和節(jié)假日來呈現(xiàn)某條道路24小時(shí)內(nèi)的總體交通狀況,其中每一片花瓣映射著各種交通狀況的比例。

每小時(shí)內(nèi)總體路況圓盤圖

這張圖以每天為單位,匯總?cè)織l道路的交通指數(shù),用以研究每日全市總體的交通周期性規(guī)律。

各道路每日擁堵時(shí)長(zhǎng)排名

原本以5min為間隔采集的數(shù)據(jù)都是離散的,無法計(jì)算連續(xù)時(shí)長(zhǎng)。這里借鑒了微積分的思想,以每一個(gè)5min內(nèi)的最終狀態(tài)作為5min內(nèi)的持續(xù)狀態(tài),于是多個(gè)5min累加來代表一天中的擁堵時(shí)長(zhǎng)。

實(shí)時(shí)路況數(shù)據(jù)下載

這個(gè)就是展示一下實(shí)時(shí)數(shù)據(jù)并提供下載,如果你不會(huì)爬蟲,可以在這上面來下載數(shù)據(jù)。

Shiny中的坑

無論是ui.R還是server.R,編碼都強(qiáng)制是UTF-8,因此在輸入中文之前***先將文件存成UTF-8,以免編碼不對(duì)只能重新讀入。同樣,讀入的數(shù)據(jù)如果包含中文,***讀進(jìn)來也要使其編碼為UTF-8,不然顯示會(huì)有亂碼。

彈出的R本地服務(wù)器不夠清晰,甚至在有的機(jī)器上圖形一直無法加載——所以檢查的時(shí)候***還是在瀏覽器中檢查。

關(guān)于下載的控件,不要在彈出的R窗口中執(zhí)行,總是報(bào)錯(cuò);在瀏覽器中試一下就沒問題。

按照shinyapps.io的流程,部署過程可以不出錯(cuò),甚至它還在計(jì)算時(shí)間流量;不過基于shinydashboard做的東西,打開網(wǎng)頁(yè)一看就會(huì)報(bào)錯(cuò)“沒有名為shinydashboard的程楫包”,所以也許用純粹的shiny來做的話可能能發(fā)布成功,但由于框架就是基于shinydashboard的,不能再推翻重來——所以就沒辦法部署。

所以shiny終究只能產(chǎn)出一個(gè)原型,它離真正企業(yè)級(jí)的數(shù)據(jù)可視化系統(tǒng)差距還是不小。因此,如果是專門做數(shù)據(jù)可視化的,前端是必須要前進(jìn)的方向。

責(zé)任編輯:武曉燕 來源: 36大數(shù)據(jù)
相關(guān)推薦

2015-08-20 10:06:36

可視化

2022-09-29 11:16:21

Python數(shù)據(jù)可視化

2017-10-31 09:38:53

大數(shù)據(jù)數(shù)據(jù)可視化Python

2019-08-01 13:28:07

AR智能交通可視化

2020-03-11 14:39:26

數(shù)據(jù)可視化地圖可視化地理信息

2020-09-07 12:42:18

表單可視化開源

2017-10-17 11:58:54

R語言UpSetR可視化

2015-10-29 09:36:48

2023-04-04 08:10:45

SQL數(shù)據(jù)可視化

2024-12-25 16:35:53

2022-10-09 09:14:31

政策人工智能

2017-10-14 13:54:26

數(shù)據(jù)可視化數(shù)據(jù)信息可視化

2022-08-26 09:15:58

Python可視化plotly

2009-04-21 14:26:41

可視化監(jiān)控IT管理摩卡

2025-04-01 08:30:00

Plotly數(shù)據(jù)可視化數(shù)據(jù)分析

2024-04-25 07:00:00

多人協(xié)同可視化協(xié)同編輯

2013-07-31 10:14:15

炎黃盈動(dòng)BPM

2017-10-25 13:04:10

數(shù)據(jù)可視化信息可視化數(shù)據(jù)圖表

2020-07-13 14:35:25

可視化數(shù)據(jù)編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)