企业级仪表板
设计原则
在开发企业级仪表板时需要牢记的几个原则:
尽可能多地将仪表板的计算推回数据库-仪表板加载和响应的时间将成为其设计中最重要的方面。对于仪表板,加载和响应的预期时间是几秒钟。
为最终用户提供“思路”路径-这些路径通常通过从仪表板内向下钻取的方式提供。这些路径允许终端用户快速回答他们当前的问题。
做出数据驱动的输入-很容易“硬编码”输入中可用的值,例如下拉列表。如果可能的值随着时间的推移而变化,这可能会成为一个问题。另一个辅助原则是,当值可用时,总是使用查询表来获取值列表。通过对大列进行分组和汇总来获得可能值的列表并不理想。
保护数据库凭据—通常,服务帐户可用于访问数据库的报表和指示板。在这种情况下,了解如何正确地保护服务帐户的凭证是很重要的。
例子
在GitHub上可以找到一个仪表板的工作示例,它将作为本文的基础。项目存储库包含代码和所有支持文件:卡塔尔世界杯欧洲预选赛赛程表
从存储库下载一个包含所有文件的压缩文件:https://github.com/sol-eng/db-dashboard/archive/master.zip
可以在shinyapps.io中找到该应用的实时版本:DB仪表板
使用shinydashboard
的shinydashboard包有三个重要的优势:
提供一个开箱即用的框架来在Shiny中创建仪表板。这节省了大量的时间,因为开发人员不需要使用“base”Shiny手动创建仪表板特性。
具有仪表板友好的标记结构。这允许开发人员快速入门。在
dashboardPage ()
标签,dashboardHeader ()
,dashboardSidebar ()
而且dashboardBody ()
可以添加到轻松布局一个新的仪表板。这是惯于使用手机。没有任何额外的代码,仪表板布局将自动适应更小的屏幕。
用R创建仪表板的另一个选项是flexdashboard.它将支持本文中讨卡塔尔世界杯欧洲预选赛赛程表论的所有特性,只有一个例外:动态选项卡,它是示例中驱动钻取的技术的基础。
简单的例子
如果你是新来的shinydashboard
,请随意复制并粘贴以下代码,在您的环境中看到一个非常简单的仪表板:
library(shinydashboard) library(闪亮)ui <- dashboardPage(dashboardHeader(title = "Quick Example"), dashboardSidebar(textInput("text", "text")), dashboardBody(valueBox(100, "基本示例"),tableOutput("mtcars"))服务器<-函数(输入,输出){输出$mtcars <- renderTable(头部(mtcars))} shinyApp(ui,服务器)
连接字符串和凭据
在许多情况下,应用程序是针对一个数据库开发的,并在生产环境中针对不同的数据库运行。如果连接字符串是“硬编码”的,这可能会带来挑战。
这个网站提供了两篇文章来帮助解决凭证和代码的可移植性问题:
使用以下命令填充闪亮输入purrr
通常的首选是用户输入中显示的值,例如下拉列表,是“人类可读的”。选择的实际值应该是唯一标识符,以便相关查询返回正确的信息。
本节将介绍两种情况和方法,将选项列表格式化为Shiny可以使用的格式。
下拉从数据库填充
理想情况下,数据库中有一个查找表,这样查询执行起来就很简单。
要将键与值分隔开,可以使用map ()
功能purrr
可以使用包。在下面的示例中,收集了airlines表中的所有记录,并创建了一个名称列表,map ()
然后用于将载波代码插入到每个名称节点。
airline_list <- tbl(con, "airlines") %>% collect %>% split(.$name) %>% #用于标签map(~.$carrier) #用于键的字段
的selectInput ()
下拉菜单可以读取结果airline_list
变量列表。
dashboardSidebar(selectInput(inputId = "airline", label = "airline:", choices = airline_list, selected = "DL", selectize = FALSE)
从向量填充的列表
有时可能的值是静态的,并且足够小,所以它们都可以放入一个向量中。
一个常见的例子是月的名字。给出给终端用户的值将是月份的名称,但当做出选择时,这个月的数字将被传递给Shiny。
为此,调用一个函数set_names ()
可以用来添加标题,将以“Shiny friendly”的方式显示在输入中
month_list <- as.list(1:12) %>% set_names(month.name) month_list$ ' All Year ' <- 99
的selectInput ()
列表菜单能够读取结果month_list
变量列表。
selectInput(inputId = "month", label = "month:", choices = month_list, selected = 99, size = 13, selectize = FALSE)
使用创建基本查询dplyr
在大多数情况下,仪表板中的所有绘图和表共享一个公共基本查询。例如,它们都将显示同一个月的数据。使用dplyr
构建基查询有以下优点:
- 简化代码,因为它防止了过滤器和连接的重复。
dplyr
“惰性”允许在构建基本查询时不执行它,直到它用于获取给定绘图或表的数据。- 抽象SQL语法的转换。如果数据库供应商更改,则指示板将不需要更改,或只需要更改极少的更改。
- 这种方法的模块化特性允许只添加一些简单的,容易理解的,
dplyr
获取需要显示在图表或表上的数据切片或聚合的步骤。
因为基本查询很可能必须基于当前的输入选择进行组装,然后是Shiny反应性()
函数是必须使用的,而不是常规的函数()
.这是因为输入$……
变量只能在Shiny反应性函数中计算。
Base_flights <- reactive({res <- flights %>% filter(carrier == input$airline) %>% left_join(airlines, by = "carrier") %>% rename(airline = name) %>% left_join(airports, by = c("origin" = "faa")) %>% rename(origin_name = name) %>% select(-lat, -lon, -alt, -tz, -dst) %>% left_join(airports, by = c("dest" = "faa")) %>% rename(dest_name = name) if (input$month != 99) res <- filter(res, month == input$month) res})
然后,闪亮的输出
函数以基本查询开始(base_flights
),完成dplyr
以动词形式添加步骤,并直接通过管道传输到绘图或显示函数。需要注意的是,在将结果数据集发送给Shiny之前收集()
或把()
需要使用功能。
输出$per_day <- renderValueBox({base_flights() %>% #------基础查询group_by(天,月)%>% #——完成步骤计数()%>% summary (avg = mean(n)) %>% pull() %>% round() %>% prettyNum(大。mark = ",") %>% valueBox(#—Pipe right into a Value Box subtitle = "Average Flights per day", color = "blue")})
使用r2d3
用于交互和下钻
“向下钻取”是向终端用户提供“思路”路径的一种很好的方法。
在Shiny应用或仪表板中,R对象包含了图表或表格,它需要通过一种方式将被点击的值传递给Shiny。做到这一点的最佳方法是在给定的情节中使用Shiny的JavaScript。这将激活应用程序内部的一个反应功能。
被称为htmlwidgets被广泛使用。它们是D3/JavaScript绘图上的一组包的包装器。可能会有可用的时间htmlwidgets
package的不足之处,要么是没有与Shiny集成,要么是没有提供仪表板所需的精确可视化。
本文,包r2d3就会被使用。这个包允许我们自定义构建D3可视化从头开始,以获得最大的灵活性和与Shiny的最佳集成。一篇关于如何集成Shiny的更深入的文章r2d3
这里是可用的:使用r2d3与Shiny.
两个现成的r2d3
情节
本文中使用的示例仪表板包含两个“闪亮就绪”的D3脚本。一种是柱状图,另一种是柱状图。它们的开发方式使您可以轻松复制整个脚本并在您自己的仪表板中使用它。
- col_plot.js——需要一个
data.frame
或宠物猫
具有以下名称和数据类型:x
-期望类别的值。例如,如果它表示一个月,那么它将包含这个月的数字。y
—期望列的高度值。标签
-期望类别的标题。它将显示给最终用户。例如,如果它表示一个月,那么它将包含这个月的名称。
- bar_plot.js——需要一个
data.frame
或宠物猫
具有以下名称和数据类型:x
—期望条的宽度值。y
-期望类别的值。例如,如果它表示一个月,那么它将包含这个月的数字。标签
-期望类别的标题。它将显示给最终用户。例如,如果它表示一个月,那么它将包含这个月的名称。
多亏了r2d3
,这些地块可以很容易地渲染。这个代码片段展示了组合使用基本查询的技术是多么简单,然后将完成转换直接管道到r2d3 ()
函数。
输出$top_airports <- renderD3({#下面的代码运行在数据库base_flights() %>% group_by(dest, dest_name) %>% tally() %>% collect() %>% arrange(desc(n)) %>% head(10) %>% arrange(dest_name) %>% mutate(dest_name = str_sub(dest_name, 1,30)) %>% rename(x = dest, #确保重命名y = n, #变量到什么标签= dest_name # D3脚本希望)%>% r2d3("bar_plot.js")})
处理来自情节的单击事件
点击事件的理想结果是激活一个Shiny输入
.这允许应用在点击或情节识别的其他事件被触发时执行反应功能。
D3绘图,可在示例的GitHub库中获得,已经包含必要的Shiny JS代码,以便在点击时触发反应函数:
col_plot.js——创建一个
输入col_clicked美元
在Shiny应用里。bar_plot.js——创建一个
输入bar_clicked美元
在Shiny应用里。
在应用程序中,包含一个observeEvent ()
函数,它将捕获D3图返回的值:
observeEvent(输入bar_clicked美元 , { # ----- 函数的代码 -------- })
故障诊断提示-如果什么都没有发生,当一个酒吧被点击,请确认安装闪亮的
软件包版本为1.1.0或更高。
创建钻取报告
使用appendTab ()
创建向下钻取报告
计划是在最终用户每次单击工具条时显示一个新的向下钻取报告。为了防止不必要地拉出相同的数据,代码将“智能”到足够简单地切换到一个现有的标签,如果相同的栏以前被点击过。此开关还可以防止对数据库的不必要访问。
新的,很酷的,appendTab ()
函数用于动态创建一个新的Shiny标签,其中有一个数据表DT包,其中包含所选内容的前100行。一个简单的向量,叫做tab_list
,用于跟踪所有现有的详细信息选项卡。的updateTabsetPanel ()
命令功能用于切换到新创建或以前创建的选项卡。
的observeEvent ()
函数“捕获”D3代码执行的事件。它监视bar_clicked
闪亮的输入。
observeEvent(input$bar_clicked, {airport <- input$bar_clicked month <- input$month tab_title <- paste(input$airline, "-", airport, if (month != 99) {paste("-", month.name[as.integer(month)])}) if (!(tab_title %in% tab_list)) {appendTab(inputId = "tabs", tabPanel(tab_title, DT::renderDataTable(#这个函数返回一个data.frame, #机场的前100条记录get_details(机场=机场))))tab_list <<- c(tab_list, tab_title)} updateTabsetPanel(session, "tabs", selected = tab_title)})
使用以下命令删除所有选项卡removeTab ()
而且purrr
动态地创建新选项卡会使仪表板变得混乱。这样一个简单的actionLink ()
按钮可以添加到dashboardSidebar ()
为了删除除主仪表板标签外的所有标签。
#这段代码运行在ui dashboardSidebar(actionLink("remove", "remove detail tabs"))
的observeEvent ()
函数再次用于在链接被单击时捕获。的走()
命令从purrr
然后用于遍历?中的每个标签标题tab_list
矢量和继续执行闪亮removeTab ()
命令。之后,选项卡列表变量被重置。由于环境作用域的关系,请确保使用double less (<<-
)重置变量时,因此它知道要重置在observeEvent ()
函数。
#这段代码运行在服务器observeEvent(输入$remove,{#使用purrr的walk命令循环通过每个#面板标签并删除它们tab_list %>% walk(~removeTab("tabs", .x)) tab_list <<- NULL})