作者介绍 知春里@伟仔 某不知名数据科学家。 持续写《数据分析》和《数据产品》系列文章,欢迎关注。 01 简单介绍Shiny “Tableau我知道,那Shiny是啥?它两有啥关系?” 今天伟仔带大家了解一个可以跟Tableau掰一掰手腕的可视化工具,而且它是开源的,用它不需要花钱!是不是来兴致了?那我们一起看看吧~ 各位同学对Tableau应该会比较地了解,至少有所耳闻,它跟Power-BI都是常年占据Gartner Magic Quadrant第一象限的BI工具。Tableau可以连接多种类型的数据源、制作精美的单图表和配置生动的仪表盘,在可视化分析的各环节都有着十分优秀的表现。 而了解Shiny的同学应该会比较少,它是一个能够方便构建交互式网页应用的R包,也称为R语言基于Web框架的可视化的开源应用(你没看错,就是经常跟Python同出没的R语言,今天它跟Tableau一伙了)。今天为了能让大家更好的理解Shiny的功能,文章将通过引入Tableau作为对比工具的方式,来看看实现数据可视化看板配置的3个主要节点:接入数据源、制作单图表、配置多图表仪表盘,Shiny会有怎样的表现?它会不会成为你未来做可视化工具的选择? 02 哪些是Shiny也能做的? 2.1可接入多样的数据源 Tableau可以连接到两类数据源。一类是本地数据文件,例如:Excel、CSV、TXT、JSON、PDF等;另一类是服务器,例如:Mysql、SQL Server、Oracle,还涵盖了目前国内外各大主流云厂商提供的远程服务器。下图所示: 图1 Tableau2020.02数据源界面截图 可以看出,Tableau在数据源连接上提供了很丰富的选择。而作为R语言的应用,上面图中Tableau可接入的数据源,Shiny通过加载包的形式也一样可以对接(例如,通过openExcel、RMysql,RODBC等包)。甚至,在某些地方会有更具优势,例如,CAD文件,类似这样的文件在Tableau中是无法加载的,而Shiny可以。 2.2可绘制丰富的图表类型 还是先说Tableau,如下图所示,可绘制条形图、柱状图、曲线图、饼图、区域图(地图)这些基础图形,以及漏斗图,金字塔图、盒须图、桑基图这样复杂图形。 图2 Tableau可选图表 同样,Shiny也可以生成相应的图形,不仅如此,一些更复杂的高级图形也可以通过调用R包,设置参数来实现,比较好用的包有ggplot3和plotly。 ggplot3可绘制的图形有: 图3 R Shiny载入ggplot3可绘制图形 plotly包可绘制的图形有: 图4 R Shiny载入plotly可绘制图形 看到“黄的、蓝色、绿的、粉的、紫的”,“形形色色”的图形,有没有觉得Shiny也不错?可能你会问,“好看是好看,是怎么做的呢?会不会很麻烦?”,那我们再来看看Shiny是怎么绘制图表,以及跟Tableau有何差别吧。 03 Shiny制作图表和仪表盘 3.1制作单图表 为了让同学们更好的了解Shiny的制图特点,我们依然拿Tableau做对比,通过实例来说明,数据网络上比较“著名”的样例数据:Titanic数据集。如果没有这个数据文件的同学可以在以下网盘链接中下载: 我们将创建一个条形图来说明这个实例,条形图的X轴是“乘客类型”维度数据,Y轴是“平均年龄”度量数据。在Tableau导入Titanic的数据,然后创建新的工作簿,操作如下: 图5 Tableau配置柱状图 绘制得到图形如下: 图6 R Shiny绘制柱状图 可见,Tableau绘制出的柱状图,用Shiny也做到了,但是他们的实现过程会有比较大的区别,甚至可以说不是一回事。Tableau通过维度和度量拖拽完成图形制作,这个过程中用户的每一步操作“所见即所得”,适合无编程背景的业务人员。Shiny则是通过编写代码来生成图形,基本逻辑是: 首先,我们导入Titanic数据集; 然后,做数据转化,在这个例子中就是以“乘客类型”为条件对“身高”数据做均值聚合; 最后,完成Shiny两个最基础的参数Ui和Sever设置,生成图形。 看起有点复杂,但一共加起来差不多是10多行的代码,这有过编程经历的人来说,尤其是应对大的项目,这点代码量一点都不多。那我们再看看配置仪表盘会是怎么样的一个情况。 3.2 配置仪表盘 仪表盘是对主题信息的综合展示,也通常是可视化数据产品的交付形式。我们依然通过一个对比实例来说明Shiny配置仪表盘的情况。我们在实例的仪表板中,配置以下3个图表: 条形图:每个舱位类型的旅客的平均年龄; 饼状图:不同性别的人数占比; 直方图:不同年龄的人数占比。 另外还要配置2个筛选器,筛选器的作用范围包含以上仪表盘内的所有图表: 年龄筛选器:可以滑动选择乘客年龄范围; 性别筛选器:可以选择男性、女性或者全部人员。 基于这样的需求,Tableau配置得到的仪表盘如下图: 实现同样仪表盘的配置,通过加载Shiny包及执行以下的代码: library(shiny) library(dplyr) library(ggplot2) library(titanic)titanic <- titanic::titanic_train ui <- fluidPage( sidebarPanel( width = 2, sliderInput( inputId='ageslider', label='Age range', min=min(titanic$Age, na.rm=T), max=max(titanic$Age, na.rm=T), value=c(min(titanic$Age, na.rm=T), max(titanic$Age, na.rm=T)) ), selectInput( inputId='genderselect', label='Gender', choices=c('Male', 'Female'), selected=c('Male', 'Female'), multiple = TRUE ) ), mainPanel( fluidRow( column(8, plotOutput('bar')), column(4, plotOutput('pie')) ), fluidRow( column(12, plotOutput('hist')) ) ) ) server <- function(input, output) { output$bar <- renderPlot({ pclass_age_data <- titanic %>% filter(Sex %in% tolower(input$genderselect)) %>% select(Pclass, Age) %>% filter(Age >= input$ageslider) %>% filter(Age <= input$ageslider) %>% group_by(Pclass) %>% summarise(Age = round(mean(Age, na.rm=TRUE), 2)) ggplot(data=pclass_age_data, aes(x=Pclass, y=Age)) + geom_bar(stat='identity', fill='#4179ab') + geom_text(aes(label=Age), vjust=-0.3, size=5) + ggtitle('Avg. age per passenger class') + theme_void() + theme(plot.title=element_text(size=20), panel.background=element_blank()) }) output$pie <- renderPlot({ pie_data <- titanic %>% filter(Sex %in% tolower(input$genderselect)) %>% filter(Age >= input$ageslider) %>% filter(Age <= input$ageslider) %>% select(Sex) %>% group_by(Sex) %>% count() %>% mutate(Midpoint = cumsum(n) - n / 2) ggplot(pie_data, aes(x='', y=n, fill=Sex)) + geom_bar(width=1, stat='identity', color='white') + coord_polar('y', start=0) + scale_fill_manual(values=c('#f34a53', '#4179ab')) + ggtitle('Gender pie chart') + theme_void() + theme(plot.title=element_text(size=20)) }) output$hist <- renderPlot({ hist_data <- titanic %>% filter(Age >= input$ageslider) %>% filter(Age <= input$ageslider) %>% filter(Sex %in% tolower(input$genderselect)) %>% select(Age) ggplot(hist_data, aes(x=Age)) + geom_histogram(bins=20, color='#62aca8', fill='#1e9a95') + ggtitle('Age histogram') + theme_void() + theme(plot.title=element_text(size=20)) }) } shinyApp(ui=ui, server=server) 得到Shiny配置仪表盘如下: 图8 R Shiny绘制仪表盘 在配置的过程,Tableau只将各个图标拖入配置面板内进行组合,可能更耗费时间的是否如何布局和格式调整上。而要实现这样的效果,Shiny要做的事情就要多一些,因为它依然是通过代码来实现,而且这次好像没有办法通过十几行代码就可以搞定。确实是这样,Tableau在操作上有着更好的用户体验,但是,我们也应该看到,此时Shiny也一样实现了Tableau样式的仪表盘的配置。 “万物皆平衡”,Tableau在便捷性和交互性上讨取你欢心的同时,它在某些方面就会有损失,而这些损失很可能就变成了Shiny的优点,Shiny可以满足更深层次的个性化需求,可以有更复杂的看板配置和更个性的视觉风格,例如,Shiny可以在仪表盘上实现用户输入交互。 3.3 用户输入交互 图9 R Shiny用户输入交互功能 上图展示的是Shiny的用户输入交互功能,这是一个比较有意思的点。虽然你可以通过Tableau比较容易地制图和配置看板,但是,Tableau目前还没有比较好的办法可以让用户通过输入信息来在仪表板上实现信息的过滤,Tableau中比较接近的做法是使用筛选框、下拉框、模糊查询或者滑动条。 这个功能有什么用呢?这确实会是我们运用仪表盘做日常分析时有可能用到的一个功能。比如,你给业务同学配置好的仪表板,现在业务同学需要根据一份人员名单,在仪表盘分析这些名单上人员的相关数据。这个需求,如果用Tableau,他可能就需要在仪表盘上就着名单对筛选器里的人员选项挨个勾选,而在Shiny中用户可以通过组件直接上传名单文件就可以实现人员匹配和过滤后的信息展示。 另外,通过Shiny的文本输入组件,用户可以仪表盘中直接上传文件进行展示,可以通过列选择器对信息做列维度的过滤,而tableau目前能支持的是只读式仪表盘配置。 不仅如此,Shiny还可以使用密码本文组件,这满足用户的另一个需求,就是权限控制,这涉及到账户安全和数据安全。而如果想要用Tableau实现权限控制,那就需要购买Server版本,而你和你的公司可能会比较介意它高昂的价格。 可见,相比Tableau,Shiny提供了一个类似于系统软件一样的交互方式,可以让我们在配置完仪表盘的后续的分析工作变得更加容易。所以,要是遇到类似这样的场景,就会让你觉得,前期多花点时间写写代码,也是值得的。 04 写在最后 从数据源、图表制作、仪表盘配置这三项可视化基本流程来看,Tableau能做的,Shiny也都实现了。当然,Shiny毕竟是R语言的一个应用,它需要通过编程实现,虽然这个过程不算复杂,依然不能像Tableau那样有灵敏的数据透视和良好的操作体验,这些也正是Tableau这类BI产品的精髓所在。但是换个角度看,Shiny对业务人员或者业务分析师可能会有学习门槛,对于数据分析师,学习R,掌握Shiny数据可视化应该是不成问题的。 除此之外,我们在做BI工具选型时都会面临比一个较现实的问题,不管是Tableau、Power-BI、Clik-Sense、Quick-BI,还是帆软等等,这些软件都不是免费的。而且,完成部署后,后续收费只有可能逐年增加,几乎不会有减少,这对于BATJM、世界五百强这样的公司来说,这些费用可能不足挂齿,即便这样,这也始终是别人的产品,你能得到它们提供的服务,也始终会有被牵制的地方(脑子里是否出现华为被美国制裁的画面?没那么严重啦!)。因此,如果你现在已经开始数据分析工作,也具备有一定的编程能力,并且正在寻找一个开源的数据可视化解决方案,那可以先试着深入了解R数据可视化和Shiny吧,它应该能给你提供一些靠谱的灵感。 这仅是一位曾经有过用R做数据分析经历的数据产品经理给到的一点小小的建议,希望对你能有帮助。
链接:https://pan.baidu.com/s/1d_BXvYxx4Obpu4mieSGaSg 提取码:4h2x
library(shiny)
library(ggplot2)
ui <- fluidPage(
plotOutput('bar')
)
server <- function(input, output) {
output$bar <- renderPlot({
pclass_age_data <- titanic %>%
select(Pclass, Age) %>%
group_by(Pclass) %>%
summarise(Age = round(mean(Age, na.rm=TRUE), 2))
ggplot(data=pclass_age_data, aes(x=Pclass, y=Age)) +
geom_bar(stat='identity', fill='steelblue') +
geom_text(aes(label=Age), vjust=-0.3, size=5)
})
}shinyApp(ui=ui, server=server)
发表评论 取消回复