|
| 1 | +library(shiny) |
| 2 | +library(plotly) |
| 3 | +library(dplyr) |
| 4 | + |
| 5 | +data(sales, package = "plotlyBook") |
| 6 | +categories <- unique(sales$category) |
| 7 | +sub_categories <- unique(sales$sub_category) |
| 8 | +ids <- unique(sales$id) |
| 9 | + |
| 10 | +ui <- fluidPage( |
| 11 | + uiOutput("history"), |
| 12 | + plotlyOutput("bars", height = 200), |
| 13 | + plotlyOutput("lines", height = 300) |
| 14 | +) |
| 15 | + |
| 16 | +server <- function(input, output, session) { |
| 17 | + # These reactive values keep track of the drilldown state |
| 18 | + # (NULL means inactive) |
| 19 | + drills <- reactiveValues( |
| 20 | + category = NULL, |
| 21 | + sub_category = NULL, |
| 22 | + id = NULL |
| 23 | + ) |
| 24 | + # filter the data based on active drill-downs |
| 25 | + # also create a column, value, which keeps track of which |
| 26 | + # variable we're interested in |
| 27 | + sales_data <- reactive({ |
| 28 | + if (!length(drills$category)) { |
| 29 | + return(mutate(sales, value = category)) |
| 30 | + } |
| 31 | + sales <- filter(sales, category %in% drills$category) |
| 32 | + if (!length(drills$sub_category)) { |
| 33 | + return(mutate(sales, value = sub_category)) |
| 34 | + } |
| 35 | + sales <- filter(sales, sub_category %in% drills$sub_category) |
| 36 | + mutate(sales, value = id) |
| 37 | + }) |
| 38 | + |
| 39 | + # bar chart of sales by 'current level of category' |
| 40 | + output$bars <- renderPlotly({ |
| 41 | + d <- count(sales_data(), value, wt = sales) |
| 42 | + |
| 43 | + p <- plot_ly(d, x = ~value, y = ~n, source = "bars") %>% |
| 44 | + layout( |
| 45 | + yaxis = list(title = "Total Sales"), |
| 46 | + xaxis = list(title = "") |
| 47 | + ) |
| 48 | + |
| 49 | + if (!length(drills$sub_category)) { |
| 50 | + add_bars(p, color = ~value) |
| 51 | + } else if (!length(drills$id)) { |
| 52 | + add_bars(p) %>% |
| 53 | + layout( |
| 54 | + hovermode = "x", |
| 55 | + xaxis = list(showticklabels = FALSE) |
| 56 | + ) |
| 57 | + } else { |
| 58 | + # add a visual cue of which ID is selected |
| 59 | + add_bars(p) %>% |
| 60 | + filter(value %in% drills$id) %>% |
| 61 | + add_bars(color = I("black")) %>% |
| 62 | + layout( |
| 63 | + hovermode = "x", xaxis = list(showticklabels = FALSE), |
| 64 | + showlegend = FALSE, barmode = "overlay" |
| 65 | + ) |
| 66 | + } |
| 67 | + }) |
| 68 | + |
| 69 | + # time-series chart of the sales |
| 70 | + output$lines <- renderPlotly({ |
| 71 | + p <- if (!length(drills$sub_category)) { |
| 72 | + sales_data() %>% |
| 73 | + count(order_date, value, wt = sales) %>% |
| 74 | + plot_ly(x = ~order_date, y = ~n) %>% |
| 75 | + add_lines(color = ~value) |
| 76 | + } else if (!length(drills$id)) { |
| 77 | + sales_data() %>% |
| 78 | + count(order_date, wt = sales) %>% |
| 79 | + plot_ly(x = ~order_date, y = ~n) %>% |
| 80 | + add_lines() |
| 81 | + } else { |
| 82 | + sales_data() %>% |
| 83 | + filter(id %in% drills$id) %>% |
| 84 | + select(-value) %>% |
| 85 | + plot_ly() %>% |
| 86 | + add_table() |
| 87 | + } |
| 88 | + p %>% |
| 89 | + layout( |
| 90 | + yaxis = list(title = "Total Sales"), |
| 91 | + xaxis = list(title = "") |
| 92 | + ) |
| 93 | + }) |
| 94 | + |
| 95 | + # control the state of the drilldown by clicking the bar graph |
| 96 | + observeEvent(event_data("plotly_click", source = "bars"), { |
| 97 | + x <- event_data("plotly_click", source = "bars")$x |
| 98 | + if (!length(x)) return() |
| 99 | + |
| 100 | + if (!length(drills$category)) { |
| 101 | + drills$category <- x |
| 102 | + } else if (!length(drills$sub_category)) { |
| 103 | + drills$sub_category <- x |
| 104 | + } else { |
| 105 | + drills$id <- x |
| 106 | + } |
| 107 | + }) |
| 108 | + |
| 109 | + # populate a `selectInput()` for each active drilldown |
| 110 | + output$history <- renderUI({ |
| 111 | + if (!length(drills$category)) return("Click the bar chart to drilldown") |
| 112 | + categoryInput <- selectInput( |
| 113 | + "category", "Category", |
| 114 | + choices = categories, selected = drills$category |
| 115 | + ) |
| 116 | + if (!length(drills$sub_category)) return(categoryInput) |
| 117 | + sd <- filter(sales, category %in% drills$category) |
| 118 | + subCategoryInput <- selectInput( |
| 119 | + "sub_category", "Sub-category", |
| 120 | + choices = unique(sd$sub_category), |
| 121 | + selected = drills$sub_category |
| 122 | + ) |
| 123 | + if (!length(drills$id)) { |
| 124 | + return(fluidRow( |
| 125 | + column(3, categoryInput), |
| 126 | + column(3, subCategoryInput) |
| 127 | + )) |
| 128 | + } |
| 129 | + sd <- filter(sd, sub_category %in% drills$sub_category) |
| 130 | + idInput <- selectInput( |
| 131 | + "id", "Product ID", |
| 132 | + choices = unique(sd$id), selected = drills$id |
| 133 | + ) |
| 134 | + fluidRow( |
| 135 | + column(3, categoryInput), |
| 136 | + column(3, subCategoryInput), |
| 137 | + column(3, idInput) |
| 138 | + ) |
| 139 | + }) |
| 140 | + |
| 141 | + # control the state of the drilldown via the `selectInput()`s |
| 142 | + observeEvent(input$category, { |
| 143 | + drills$category <- input$category |
| 144 | + drills$sub_category <- NULL |
| 145 | + drills$id <- NULL |
| 146 | + }) |
| 147 | + observeEvent(input$sub_category, { |
| 148 | + drills$sub_category <- input$sub_category |
| 149 | + drills$id <- NULL |
| 150 | + }) |
| 151 | + observeEvent(input$id, { |
| 152 | + drills$id <- input$id |
| 153 | + }) |
| 154 | +} |
| 155 | + |
| 156 | +shinyApp(ui, server) |
0 commit comments