简体   繁体   English

使用来自ggplot和Shiny R的geom_histogram主动更新y轴

[英]Updating y-axis Reactively with geom_histogram from ggplot and Shiny R

So I am trying to tackle the following but I may have started down the wrong road. 因此,我试图解决以下问题,但我可能走错了路。

As these sample sizes increase, I need to update the y-limits so the highest bar in geom_histogram() doesn't go off the top. 随着样本数量的增加,我需要更新y限制,以便geom_histogram()中的最高条不会超出顶部。 The especially happens if the st. 特别是如果圣。 dev. 开发。 is set near 0. 设置为0附近。

This is literally my second day working with Shiny and reactive applications so I feel I've gotten myself into a pickle. 从字面上看,这是我使用Shiny和反应式应用程序的第二天,所以我觉得自己陷入了困境。

I think I need to save the ggplot() objects and then update their ylimit reactively with the value of the largest bar from the last histogram. 我想我需要保存ggplot()对象,然后使用最后一个直方图中最大条形的值来动态更新其ylimit。 Just not sure if I can do that the way this thing is set up now. 只是不确定我是否可以按照现在设置的方式进行操作。

(I am realizing I had a similar problem over 2 years ago) (我意识到两年前我遇到了类似的问题)

ggplot2 Force y-axis to start at origin and float y-axis upper limit ggplot2强制y轴从原点开始并浮动y轴上限

This is different because it is the height of a histogram that needs to tell the y-axis to increase, not the largest data value. 这是不同的,因为直方图的高度需要告诉y轴增加,而不是最大数据值。 Also, because Shiny. 另外,因为有光泽。

My server.R function looks like 我的server.R函数看起来像

    library(shiny)
library(ggplot2)
library(extrafont)


# Define server logic for random distribution application
function(input, output, session) {


    data <- reactive({
            set.seed(123)

             switch(input$dist, 
                    norm = rnorm(input$n, 
                                 sd = input$stDev),
                    unif = runif(input$n,-4,4),
                    lnorm = rlnorm(input$n)
                    )
                 })

    height="100%"

    plotType <- function(blah, maxVal, stDev, n, type) {

      roundUp <- function(x) 10^ceiling(log10(x)+0.001)
      maxX<- roundUp(maxVal)
      breakVal<-max(floor(maxX/10),1)

      switch(type,
             norm =  ggplot(as.data.frame(blah), aes(x=blah))+
               geom_histogram(binwidth = 0.2,
                              boundary = 0, 
                              colour = "black") +
               scale_y_continuous(limits = c(0, maxX),
                                  breaks = seq(0, maxX, breakVal), 
                                  expand = c(0, 0)) +
               scale_x_continuous(breaks = seq(-4, 4, 1),
                                  expand = c(0, 0)) +
               theme_set(theme_bw(base_size = 40) +
               ylab("Frequency")+
               xlab("")+
               coord_cartesian(xlim=c(-4, 4))+
               ggtitle(paste("n = ",n, "St Dev =", stDev,"  Normal Distribution ", sep = ' ')),

             unif =  ggplot(as.data.frame(blah), aes(x=blah))+
               geom_histogram(binwidth=0.1, boundary =0,colour = "black")+
               scale_y_continuous(limits = c(0,roundUp(maxVal*(3/stDev))),
                                  breaks=seq(0,roundUp(maxVal*(3/stDev)), roundUp(maxVal*(3/stDev))/10),
                                  expand = c(0, 0))+
               scale_x_continuous(breaks=seq(-4,4,1),expand = c(0, 0))+
               theme_set(theme_bw(base_size = 40))+
               ylab("Frequency")+xlab("")+
               coord_cartesian(xlim=c(-4,4))+
               ggtitle(paste("n = ",n, "     Uniform Distribution ", sep = ' ')),


             lnorm = ggplot(as.data.frame(blah), aes(x=blah))+
               geom_histogram(binwidth=0.2, boundary =0,colour = "black")+
               scale_y_continuous(limits = c(0,maxX),
                                  breaks=seq(0,maxX, breakVal),
                                  expand = c(0, 0))+
               scale_x_continuous(breaks=seq(0,8,1),expand = c(0, 0))+
               theme_set(theme_bw(base_size = 40))+
               ylab("Frequency")+xlab("")+
               coord_cartesian(xlim=c(0,8))+
                 ggtitle(paste("n = ",n, "     Log-Normal Distribution ", sep = ' '))
      )

    }

    observe({ 
      updateSliderInput(session, "n", 
                        step = input$stepSize,
                        max=input$maxN)
             })
    plot.dat <- reactiveValues(main=NULL, layer1=NULL)

     #plotType(data, maxVal, stDev, n, type)
    output$plot <- renderPlot({ 
                                plotType(data(),

                                switch(input$dist,
                                       norm = max((input$n)/7,1),
                                       unif = max((input$n)/50,1),
                                       lnorm =max((input$n)/8,1)
                                          ), 

                                input$stDev, 
                                input$n,
                                input$dist) })


  # Generate a summary of the data
  output$summary <- renderTable(
    as.array(round(summary(data())[c(1,4,6)],5)),
    colnames=FALSE
  )

  output$stDev <- renderTable(
    as.array(sd(data())),
    colnames=FALSE
  )

  # Generate an HTML table view of the data
  output$table <- renderTable({
    data.frame(x=data())
  })

}

And my ui.R looks like 我的ui.R看起来像

  library(shiny)
library(shinythemes)
library(DT)


# Define UI for random distribution application 
shinyUI(fluidPage(theme = shinytheme("slate"),

  # Application title
  headerPanel("Michael's Shiny App"),

  # Sidebar with controls to select the random distribution type
  # and number of observations to generate. Note the use of the
  # br() element to introduce extra vertical spacing
  sidebarLayout(
    sidebarPanel(
      tags$head(tags$style("#plot{height:90vh !important;}")),
      radioButtons("dist", "Distribution:",
                   c("Standard Normal" = "norm",
                     "Uniform" = "unif",
                     "Log-normal" = "lnorm")),
      br(),

      numericInput("stepSize", "Step", 1, min = 1, max = NA, step = NA,
                   width = NULL),
      numericInput("maxN", "Max Sample Size", 50, min = NA, max = NA, step = NA,
                   width = NULL),

      br(),

        sliderInput("n", 
                  "Number of observations:", 
                  value = 0,
                  min = 1, 
                  max = 120000,
                  step = 5000,
                  animate=animationOptions(interval=1200, loop=T)),

      sliderInput("stDev", 
                  "Standard Deviation:", 
                  value = 1,
                  min = 0, 
                  max = 3,
                  step = 0.1,
                  animate=animationOptions(interval=1200, loop=T)),

      p("Summary Statistics"),         
      tabPanel("Summary", tableOutput("summary")),
      p("Sample St. Dev."),
      tabPanel("Standard Dev", tableOutput("stDev")),
      width =2
    ),

    # Show a tabset that includes a plot, summary, and table view
    # of the generated distribution
    mainPanel(
      tabsetPanel(type = "tabs", 
                  tabPanel("Plot", plotOutput("plot")), 
                  tabPanel("Table", tableOutput("table"))
      ))

  )))

The whole thing has a lot of redundancy. 整个事情有很多冗余。 What I want to do, is once the biggest bar on the histogram gets close to the upper y-limit, I want the ylimit to jump to the next power of 10. 我想做的是,一旦直方图上的最大条形图接近y上限,我希望ylimit跳到下一个10的幂。

Any suggestions are greatly appreciated. 任何建议,不胜感激。

Update Loosely, the solution that I ended up using is as follows: In the renderPlot() function, you need to save the ggplot object. 松散地更新 ,我最终使用的解决方案如下:在renderPlot()函数中,您需要保存ggplot对象。 Then as mentioned below, access the ymax value (still within renderPlot()), 然后,如下所述,访问ymax值(仍在renderPlot()内),

 ggplot_build(norm)$layout$panel_ranges[[1]]$y.range[[2]]

and then use that to update the y-axis. 然后使用它来更新y轴。 I used the following function to make the axis limit "nice". 我使用以下函数使轴限制为“ nice”。

roundUpNice <- function(x, nice=c(1,2,4,5,6,8,10)) {
        10^floor(log10(x)) * nice[[which(x <= 10^floor(log10(x)) * nice)[[1]]]]
      }

Then updating the y-axis. 然后更新y轴。 (still within renderplot()) (仍在renderplot()中)

   ymaxX = roundUpNice(ggplot_build(norm)$layout$panel_ranges[[1]]$y.range[[2]])


  norm+scale_y_continuous(limits = c(0, max(ymaxX, 20)), 
                        expand=c(0,0))

First, store the histogram (default axes). 首先,存储直方图(默认轴)。

p1 <- ggplot(...) + geom_histogram() 

Then, Use ggplot_build(p1) to access the heights of the histogram bars . 然后,使用ggplot_build(p1) 访问直方图栏的高度 For example, 例如,

 set.seed(1)
 df <- data.frame(x=rnorm(10000))
 library(ggplot2)
 p1 <- ggplot(df, aes(x=x)) + geom_histogram()
 bar_max <- max(ggplot_build(p1)[['data']][[1]]$ymax) # where 1 is index 1st layer
 bar_max # returns 1042

You will need a function to tell you what the next power of 10 is, for example: 您将需要一个函数来告诉您10的下一个幂是什么,例如:

nextPowerOfTen <- function(x) as.integer(floor(log10(x) + 1))
# example: nextPowerOfTen(999) # returns 3 (10^3=1000)

You will want to check whether the bar_max is within some margin (based on your preference) of the next power of 10. If an adjustment is triggered, you can simply do p1 + scale_y_continuous(limits=c(0,y_max_new)) . 您将要检查bar_max是否在下一个10次方的余量内(基于您的偏好)。如果触发了调整,则只需执行p1 + scale_y_continuous(limits=c(0,y_max_new))

I found the answer hidden in the "scale_y_continuous()" portion of your code. 我发现答案隐藏在代码的“ scale_y_continuous()”部分中。 The app was very close, but in some cases, the data maxed out the y-axis, which made it appear like it was running further than the axis limits as you said. 该应用程序非常接近,但是在某些情况下,数据使y轴最大化,从而使其看起来运行得比您说的轴限制更远。

To fix this problem, the expand argument within the scale_y_continuous section needs to be set to "c(0.05, 0)", instead of "c(0, 0)". 若要解决此问题,必须将scale_y_continuous节中的expand参数设置为“ c(0.05,0)”,而不是“ c(0,0)”。

First, I've replicated an example of the graph run-off you were describing by setting the sample size to 50 and standard deviation to 0.3 within your app. 首先,我通过在您的应用程序中将样本大小设置为50并将标准差设置为0.3,来复制了您描述的图形流失的示例。 After running the original code with "expand=c(0, 0)", we can see we get the following graph: 使用“ expand = c(0,0)”运行原始代码后,我们可以看到以下图形:

在此处输入图片说明

This problem is fixed by changing the argument to "expand=c(0.05, 0)", as shown here: 通过将参数更改为“ expand = c(0.05,0)”可以解决此问题,如下所示:

在此处输入图片说明

For copies of the fixed scripts, see below. 有关固定脚本的副本,请参见下文。

Part 1 -- server.R 第1部分-server.R

library(shiny)
library(ggplot2)
library(extrafont)


# Define server logic for random distribution application
function(input, output, session) {

  data <- reactive({
    set.seed(123)

    switch(input$dist, 
           norm = rnorm(input$n, 
                        sd = input$stDev),
           unif = runif(input$n,-4,4),
           lnorm = rlnorm(input$n)
    )
  })

  height="100%"

  plotType <- function(blah, maxVal, stDev, n, type){

    roundUp <- function(x){10^ceiling(log10(x)+0.001)}
    maxX<- roundUp(maxVal)
    breakVal<-max(floor(maxX/10),1)

    switch(type,
           norm=ggplot(as.data.frame(blah), aes(x=blah)) +
             geom_histogram(binwidth = 0.2,
                            boundary = 0, 
                            colour = "black") +
             scale_y_continuous(limits = c(0, maxX),
                                breaks = seq(0, maxX, breakVal), 
                                expand = c(0.05, 0)) +
             scale_x_continuous(breaks = seq(-4, 4, 1),
                                expand = c(0, 0)) +
             theme_set(theme_bw(base_size = 40)) +
             ylab("Frequency") +
             xlab("") +
             coord_cartesian(xlim=c(-4, 4))+
             ggtitle(paste("n = ",n, "St Dev =", stDev,
                           "  Normal Distribution ", sep = ' ')),
           unif=ggplot(as.data.frame(blah), aes(x=blah)) +
             geom_histogram(binwidth=0.1, boundary=0, colour="black")+
             scale_y_continuous(
               limits = c(0,roundUp(maxVal*(3/stDev))),
               breaks=seq(0,roundUp(maxVal*(3/stDev)),
                                    roundUp(maxVal*(3/stDev))/10),      
               expand = c(0.05, 0))+
               scale_x_continuous(breaks=seq(-4,4,1),expand=c(0, 0)) +
                     theme_set(theme_bw(base_size = 40))+
                     ylab("Frequency")+xlab("")+
                     coord_cartesian(xlim=c(-4,4))+
                     ggtitle(paste("n = ",n,
                             "     Uniform Distribution ", sep = ' ')),
           lnorm=ggplot(as.data.frame(blah), aes(x=blah))+
              geom_histogram(binwidth=0.2,boundary=0, colour="black") +
                     scale_y_continuous(limits=c(o,maxX),
                                        breaks=seq(0,maxX, breakVal),
                                        expand = c(0.05, 0)) +
                     scale_x_continuous(breaks=seq(0,8,1),
                                        expand = c(0, 0)) +
                     theme_set(theme_bw(base_size = 40)) +
                     ylab("Frequency") +
                     xlab("") +
                     coord_cartesian(xlim=c(0,8)) +
                     ggtitle(paste("n = ",n,
                                   "     Log-Normal Distribution ",
                                   sep = ' '))
    )

}

observe({ 
  updateSliderInput(session, "n", 
                    step = input$stepSize,
                    max=input$maxN)
})
plot.dat <- reactiveValues(main=NULL, layer1=NULL)

#plotType(data, maxVal, stDev, n, type)
output$plot <- renderPlot({ 
  plotType(data(),

           switch(input$dist,
                  norm = max((input$n)/7,1),
                  unif = max((input$n)/50,1),
                  lnorm =max((input$n)/8,1)
           ), 

           input$stDev, 
           input$n,
           input$dist) })


# Generate a summary of the data
output$summary <- renderTable(
  as.array(round(summary(data())[c(1,4,6)],5)),
  colnames=FALSE
)

output$stDev <- renderTable(
  as.array(sd(data())),
  colnames=FALSE
)

# Generate an HTML table view of the data
output$table <- renderTable({
  data.frame(x=data())
})

}

Part 2 -- ui.R 第2部分-ui.R

library(shiny)
library(shinythemes)
library(DT)

# Define UI for random distribution application 
shinyUI(fluidPage(theme = shinytheme("slate"),

        # Application title
        headerPanel("Michael's Shiny App"),

        # Sidebar with controls to select the random distribution type
        # and number of observations to generate. Note the use of the
        # br() element to introduce extra vertical spacing
        sidebarLayout(
          sidebarPanel(
            tags$head(tags$style("#plot{height:90vh !important;}")),
                      radioButtons("dist", "Distribution:",
                                   c("Standard Normal" = "norm",
                                   "Uniform" = "unif",
                                   "Log-normal" = "lnorm")),
          br(),
          numericInput("stepSize", "Step", 1, 
                       min = 1, max = NA, step = NA, width = NULL),
          numericInput("maxN", "Max Sample Size", 50, 
                       min = NA, max = NA, step = NA,width = NULL),
          br(),

          sliderInput("n", "Number of observations:", value = 0,
                      min = 1, max = 120000, step = 5000,
                      animate=animationOptions(interval=1200, loop=T)),
          sliderInput("stDev","Standard Deviation:",value = 1,
                      min = 0,max = 3,step = 0.1,
                      animate=animationOptions(interval=1200, loop=T)),

          p("Summary Statistics"),         
          tabPanel("Summary", tableOutput("summary")),
          p("Sample St. Dev."),
          tabPanel("Standard Dev", tableOutput("stDev")),
                   width =2),

          # Show a tabset that includes a plot, summary, and table view
          # of the generated distribution
          mainPanel(tabsetPanel(type = "tabs", 
                    tabPanel("Plot", plotOutput("plot")), 
                    tabPanel("Table", tableOutput("table"))
                   ))

)))

Update Loosely, the solution that I ended up using is as follows: In the renderPlot() function, you need to save the ggplot object. 松散地更新 ,我最终使用的解决方案如下:在renderPlot()函数中,您需要保存ggplot对象。 Then as mentioned below, access the ymax value (still within renderPlot()), 然后,如下所述,访问ymax值(仍在renderPlot()内),

 ggplot_build(p1)$layout$panel_ranges[[1]]$y.range[[2]]

and then use that to update the y-axis. 然后使用它来更新y轴。 I used the following function to make the axis limit "nice". 我使用以下函数使轴限制为“ nice”。

roundUpNice <- function(x, nice=c(1,2,4,5,6,8,10)) {
        if(length(x) != 1) stop("'x' must be of length 1")
        10^floor(log10(x)) * nice[[which(x <= 10^floor(log10(x)) * nice)[[1]]]]
      }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM