我正在开发R Shiny应用程序,该应用程序允许用户交互地研究线性回归模型的简单原理。我的代码运行得很好。但是,它不是很优雅。在下面找到用于说明的服务器功能(为方便起见,我省略了ui
和个人定义的功能,但是如果您要查看它们,请告诉我):
#### Make Server ####
server = function(input, output) {
#if the users presses submit:
#take the input, format it, and forward it to 'simulation'
#which creates a dataframe(column1 = simulated response, column2 = group)
simulate <- eventReactive(input$submit, {
group1 = as.numeric(c(input$n1, input$mean1, input$sd1))
group2 = as.numeric(c(input$n2, input$mean2, input$sd2))
group3 = as.numeric(c(input$n3, input$mean3, input$sd3))
all_groups = list(group1, group2, group3)
data = simulation(all_groups)
})
#model a linear regression based on the simulated data, print the output
output$model <- renderPrint({
data = simulate()
model = lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = data)
summary(model)
})
#plot density plots for every group in one graph
#add the intercepts/coefficients returned by the linear regression to that graph
output$hist <- renderPlot({
data = simulate()
model = lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = data)
intercept = model[["coefficients"]][["(Intercept)"]]
intercept_g1 = model[["coefficients"]][["group1"]]
intercept_g2 = model[["coefficients"]][["group2"]]
ggplot(data, aes(x=response, fill=group)) +
geom_density(data = subset(data, group="group1"), alpha=.5) +
geom_density(data = subset(data, group="group2"), alpha=.5) +
geom_density(data = subset(data, group="group3"), alpha=.5) +
geom_vline(xintercept=intercept) +
geom_vline(xintercept=intercept_g1) +
geom_vline(xintercept=intercept_g2)
})
#if the user presses 'reset', reset all input panels to their default value
observeEvent(input$reset, {
shinyjs::reset("side-panel")
})
}
困扰我的两个主要问题是:
B renderPlot
和renderPrint
都在其第一行中创建data
变量(并且data
也在eventReacitve
中创建)。当用户点击“提交”按钮时,是否可以一次创建“数据”(此处未显示其实现)?
Both,renderPlot
和renderPrint
都计算线性回归模型。虽然第一个仅需要输出,但是第二个需要一些值,这些值存储在lme变量中(此处为截距)。这里也可以只计算一次模型吗?
[如果您还建议改善代码,但又不解决其他问题,请告诉我。这只是较大项目的一小部分;将为用户添加几个选项,并且高效且易于维护的代码将非常有用!
我意识到这可能不是最详尽或最复杂的答案(并且我没有足够的声誉来简单评论),但是作为一种通用方法,我建议您将所有步骤总结为不同的功能。例如,如果我正确地看到了这一点,则您的renderPlot()
调用仅取决于simulate()
,而其余计算基于simulate()
提供的数据。因此,您可以将其总结为
plot_data <- function(data_input) {
data = data_input
model = lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = data)
intercept = model[["coefficients"]][["(Intercept)"]]
intercept_g1 = model[["coefficients"]][["group1"]]
intercept_g2 = model[["coefficients"]][["group2"]]
ggplot(data, aes(x=response, fill=group)) +
geom_density(data = subset(data, group="group1"), alpha=.5) +
geom_density(data = subset(data, group="group2"), alpha=.5) +
geom_density(data = subset(data, group="group3"), alpha=.5) +
geom_vline(xintercept=intercept) +
geom_vline(xintercept=intercept_g1) +
geom_vline(xintercept=intercept_g2)
}
这将减少您的呼叫到
output$hist <- renderPlot({plot_data(simulate())})
此外,在闪亮的应用程序外部编写这些功能还可以使您在普通的R环境中更轻松地测试和调试它们,而您可以专注于服务器功能中的应用程序。
[我认为您应该在另一个reactive
中计算模型,这样您可以在eventReactive
中设置数据并创建一个get_model
反应式,您可以在其中读取data
和render*
函数您使用此模型。
伪代码可能是这些东西:
server <- function(input, output, session) {
## create a get_model reactive
## thanks to the reactive nature it will cache its values unless data changes
get_model <- reactive({
my_data <- req(simulate()) ## use require to make sure it is well defined
lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = my_data)
})
output$model <- renderPrint({
## you could use validate(need(.)) here to make sure the model is well defined
## validate(need(get_model(), "Model not yet defined! Please simulate some data first!")
summary(get_model()))
})
}
这样,您仅在数据更改时计算模型,在renderPrint
和renderPlot
中仅计算一次,而不计算两次。无论如何,仅在按下按钮时才重新生成数据。这样,您可以充分利用shiny's
内置电抗系统。
我刚刚看到您的绘图函数中也需要data
,所以我只想做这样的事情:
simulate <- eventReactive(input$submit, {
## create data first
group1 <- as.numeric(c(input$n1, input$mean1, input$sd1))
group2 <- as.numeric(c(input$n2, input$mean2, input$sd2))
group3 <- as.numeric(c(input$n3, input$mean3, input$sd3))
all_groups <- list(group1, group2, group3)
data <- simulation(all_groups)
## create model
model <- lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = data)
## return a list with both elements
list(data = data, model = model)
})
然后您可以像这样在renderPrint
中使用它:
output$model <- renderPrint({
summary(simulate()$data))
})
在这样的渲染图中
output$hist <- renderPlot({
data <- simulate()$data
model <- simulate()$model
intercept = model[["coefficients"]][["(Intercept)"]]
intercept_g1 = model[["coefficients"]][["group1"]]
intercept_g2 = model[["coefficients"]][["group2"]]
ggplot(data, aes(x=response, fill=group)) +
geom_density(data = subset(data, group="group1"), alpha=.5) +
geom_density(data = subset(data, group="group2"), alpha=.5) +
geom_density(data = subset(data, group="group3"), alpha=.5) +
geom_vline(xintercept=intercept) +
geom_vline(xintercept=intercept_g1) +
geom_vline(xintercept=intercept_g2)
})