stats_noob
stats_noob

Reputation: 5907

R: "synchronizing" plots in R

I am working with the R programming language. I wrote the following code to make an interactive time series "slider"

 library(dplyr)
    library(ggplot2)
    library(shiny)
    library(plotly)
    library(htmltools)
    
    library(dplyr)
#generate data
set.seed(123)

var = rnorm(731, 100,25)
date= seq(as.Date("2014/1/1"), as.Date("2016/1/1"),by="day")
data = data.frame(var,date)

vals <- 90:100
combine <- vector('list', length(vals))
count <- 0
for (i in vals) {
 
  data$var_i = i
  data$new_var_i = ifelse(data$var >i,1,0)
 
  #percent of observations greater than i (each month)
  aggregate_i = data %>%
    mutate(date = as.Date(date)) %>%
    group_by(month = format(date, "%Y-%m")) %>%
    summarise( mean = mean(new_var_i))
 
  #combine files together
 
  aggregate_i$var = i
  aggregate_i$var = as.factor(aggregate_i$var)
 
  count <- count + 1
  combine[[count]] <- aggregate_i
 
}

result_1 <- bind_rows(combine)
result_1$group = "group_a"
result_1$group = as.factor(result_1$group)

######

var = rnorm(731, 85,25)
date= seq(as.Date("2014/1/1"), as.Date("2016/1/1"),by="day")
data = data.frame(var,date)

vals <- 90:100
combine <- vector('list', length(vals))
count <- 0
for (i in vals) {
 
  data$var_i = i
  data$new_var_i = ifelse(data$var >i,1,0)
 
  #percent of observations greater than i (each month)
  aggregate_i = data %>%
    mutate(date = as.Date(date)) %>%
    group_by(month = format(date, "%Y-%m")) %>%
    summarise( mean = mean(new_var_i))
 
  #combine files together
 
  aggregate_i$var = i
  aggregate_i$var = as.factor(aggregate_i$var)
 
  count <- count + 1
  combine[[count]] <- aggregate_i
 
}

result_2 <- bind_rows(combine)
result_2$group = "group_b"
result_2$group = as.factor(result_2$group)

#combine all files

final = rbind(result_1, result_2)

gg <-ggplot(final, aes(frame = var, color = group)) + geom_line(aes(x=month, y=mean, group=1))+ theme(axis.text.x = element_text(angle=90)) + ggtitle("title")

gg = ggplotly(gg)

enter image description here

From here, I want to make 3 additional graphs corresponding to the value of "var" where the slider is currently at. I have illustrated this below (for var = 90):

enter image description here

Here is the R code required to make these 3 additional graphs (for var = 90):

#### filter for var = 90

a90 = final %>% 
  filter(var == 90)

a90 = a90 %>%
    group_by(group) %>%
    summarise( avg = mean(mean))

##bar plot

plot<-ggplot(data=a90, aes(x=group, y=avg)) +
  geom_bar(stat="identity") + ggtitle("bar plot")

bar_plotly <- ggplotly(plot)


#pie chart 
 Pie = ggplot(a90, aes(x="", y=(1-avg), fill=group)) +
      geom_bar(stat="identity", width=1) +
      coord_polar("y", start=0) +ggtitle( "Pie Chart") + scale_fill_brewer(palette="Blues")+
  theme_minimal()

#for some reason this does not work
pie_plotly = ggplotly(Pie)



#table

fig <- plot_ly(
  type = 'table',
  columnwidth = c(100, 100),
  columnorder = c(0, 1),
  header = list(
    values = c("average","group"),
    align = c("center", "center"),
    line = list(width = 1, color = 'black'),
    fill = list(color = c("grey", "grey")),
    font = list(family = "Arial", size = 14, color = "white")
  ),
  cells = list(
    values = rbind(a90$avg, a90$group),
    align = c("center", "center"),
    line = list(color = "black", width = 1),
    font = list(family = "Arial", size = 12, color = c("black"))
  ))

fig

I tried to put all the graphs together but this did not work:

doc <- htmltools::tagList(
  div(gg, style = "float:left;width:50%;"),
  div(bar_plotly,style = "float:left;width:50%;"),
  div(fig, style = "float:left;width:50%;"))

htmltools::save_html(html = doc, file = "final.html")

Error in as.vector(x, "character") : 
  cannot coerce type 'environment' to vector of type 'character'

Can someone please tell me if this is possible to update the other graphs as the user slides the slider for the first graph?

Thanks

> sessionInfo()
R version 4.0.3 (2020-10-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19041)

Matrix products: default

locale:
[1] LC_COLLATE=English_Canada.1252  LC_CTYPE=English_Canada.1252    LC_MONETARY=English_Canada.1252
[4] LC_NUMERIC=C                    LC_TIME=English_Canada.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] shiny_1.6.0       htmltools_0.5.1.1 dplyr_1.0.3       plotly_4.9.3      ggplot2_3.3.3    

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.6         later_1.1.0.1      pillar_1.4.7       compiler_4.0.3     RColorBrewer_1.1-2 tools_4.0.3       
 [7] digest_0.6.27      jsonlite_1.7.2     lifecycle_0.2.0    tibble_3.0.5       gtable_0.3.0       viridisLite_0.3.0 
[13] pkgconfig_2.0.3    rlang_0.4.10       cli_2.2.0          rstudioapi_0.13    crosstalk_1.1.1    yaml_2.2.1        
[19] fastmap_1.1.0      withr_2.4.1        httr_1.4.2         generics_0.1.0     vctrs_0.3.6        htmlwidgets_1.5.3 
[25] grid_4.0.3         tidyselect_1.1.0   glue_1.4.2         data.table_1.13.6  R6_2.5.0           fansi_0.4.2       
[31] purrr_0.3.4        tidyr_1.1.2        farver_2.0.3       magrittr_2.0.1     promises_1.1.1     scales_1.1.1      
[37] ellipsis_0.3.1     assertthat_0.2.1   xtable_1.8-4       mime_0.9           colorspace_2.0-0   httpuv_1.5.5      
[43] labeling_0.4.2     lazyeval_0.2.2     munsell_0.5.0      crayon_1.3.4 

Upvotes: 0

Views: 502

Answers (1)

kikoralston
kikoralston

Reputation: 1236

According to plotly's tutorial, the way to combine different plots is to use subplot. By doing this, it also links the slider to all plots if you defined the frame argument in all plots (see this other answer).

Also, for these more complicated plots, I think it is always safer to work directly in the plotly API instead of converting a ggplot

# create summary dataframe for bar chart and pie chart
df.summary <- final %>% group_by(group, var) %>%
  summarise( avg = mean(mean)) %>% ungroup()

# create line chart
gg <- final %>% 
  plot_ly(x = ~month, y = ~mean, frame=~var, color=~group, type = 'scatter', mode = 'lines', colors = 'Set1') %>% 
  layout(showlegend = F)

# create bar chart
bar_plotly <- df.summary %>% 
  plot_ly(x = ~group, y = ~avg, frame=~var, color=~group, ids=groups, type = 'bar', colors = 'Set1') %>% 
  layout(showlegend = F, yaxis = list(range = c(0, 0.7)))

# create pie chart
Pie <- df.summary %>% 
  plot_ly(values = ~avg, frame=~var, ids=groups, type = 'pie', domain = list(x = c(0.6, 1), y = c(0, 0.4)), colors = 'Set1') %>% 
  layout(showlegend = F)

# combine all of them into one interactive plot
subplot(gg, subplot(bar_plotly, Pie, nrows = 2), nrows = 1)

enter image description here

According to plotly, not all traces fully support animations. So the transitions in the pie chart will not be smooth.

Also, I don't think you can link the table with the current plotly API.

Another option would be to create a shiny application, where you could have more control.

Upvotes: 2

Related Questions