R - Highcharter: Drilldown on stacked column graph - r

I've created a stacked column chart in Highcharter using R and I am trying to be able to drilldown into it.
I.e. In the picture attached, I want to be able to drill down in the red section of column CRDT. So far, I can only get it so each color section of CRDT drills into the same information OR each red section drills into the same information. I need a combined filter.
Below is my code that drills "CRDT Red" information for all red sections:
Lvl1Grouping <- aggregate(WIPGate2$Receipt.Qty, by = list(WIPGate$Hold.Code,WIPGate2$Aging),FUN=sum)
Lvl1df <- data_frame(name = Lvl1Grouping$Group.1,
y = Lvl1Grouping$x,
stack = Lvl1Grouping$Group.2,
drilldown = tolower(stack)
)
hc <- highchart() %>%
hc_chart(type = "column") %>%
hc_title(text = "WIP") %>%
hc_xAxis(type = "category") %>%
hc_legend(enabled = FALSE) %>%
hc_plotOptions(column = list(stacking = "normal")) %>%
hc_add_series(name = "Greater than 30 days",data=Lvl1dfLvl1df$stack=="Greater than 30 days",], color = "#D20000") %>%
hc_add_series(name = "Between 20-30 days",data=Lvl1df[Lvl1df$stack=="Between 20-30 days",], color = "#FF7900") %>%
hc_add_series(name = "Between 10-20 days",data=Lvl1df[Lvl1df$stack=="Between 10-20 days",], color = "#F6FC00") %>%
hc_add_series(name = "Less than 10 days",data=Lvl1df[Lvl1df$stack=="Less than 10 days",], color = "#009A00")
hc
Lvl2GroupingCRDT <- WIPGate2[WIPGate2$Hold.Code == "CRDT",]
Lvl2GroupingCRDT4 <- Lvl2GroupingCRDT[Lvl2GroupingCRDT$Aging == "Greater than 30 days",]
Lvl2GroupingCRDT4 <- aggregate(Lvl2GroupingCRDT4$Receipt.Qty, by = list(Lvl2GroupingCRDT4$Customer.Name),FUN=sum)
dfCRDT4 <- data_frame(
name = Lvl2GroupingCRDT4$Group.1,
value = Lvl2GroupingCRDT4$x
)
hc <- hc %>%
hc_drilldown(
allowPointDrilldown = TRUE,
series = list(
list(
id = "greater than 30 days",
name = "CRDT",
data = list_parse2(dfCRDT4)
)
)
)
hc
Current Situation .png

I have figured out the code, however it is not an eloquent solution...
The trick is instead of having a single data frame for the Level 1 information, there needs to be a separate data frame for each part of the stack. This way you can put an ID to it in order to be able to reference.
My code is hundreds of lines in order to splice out the data in the way it needs to be so if anyone has a better solution, please post it!! (my actually code includes 7 other groups besides "CRDT", so imagine "CRDT" lines below * 7!!!
FYI, I have changed some of my dashboard and variables, so they may not be the same as above...
WIPGate2Aging <- WIP_Ops_Filtered()[WIP_Ops_Filtered()$Hold.Code!="",]
WIPGate2G30 <- WIPGate2Aging[WIPGate2Aging$Aging == "Greater than 30 days",]
WIPGate22030 <- WIPGate2Aging[WIPGate2Aging$Aging == "Between 20-30 days",]
WIPGate21020 <- WIPGate2Aging[WIPGate2Aging$Aging == "Between 10-20 days",]
WIPGate2L10 <- WIPGate2Aging[WIPGate2Aging$Aging == "Less than 10 days",]
try(Lvl1GroupingG30 <- aggregate(WIPGate2G30$Receipt.Qty, by = list(WIPGate2G30$Hold.Code),FUN=sum),silent = TRUE)
if (exists("Lvl1GroupingG30")) {} else {Lvl1GroupingG30=data.table(Group.1=numeric(), x=numeric())}
try(Lvl1Grouping2030 <- aggregate(WIPGate22030$Receipt.Qty, by = list(WIPGate22030$Hold.Code),FUN=sum),silent = TRUE)
if (exists("Lvl1Grouping2030")) {} else {Lvl1Grouping2030=data.table(Group.1=numeric(), x=numeric())}
try(Lvl1Grouping1020 <- aggregate(WIPGate21020$Receipt.Qty, by = list(WIPGate21020$Hold.Code),FUN=sum),silent = TRUE)
if (exists("Lvl1Grouping1020")) {} else {Lvl1Grouping1020=data.table(Group.1=numeric(), x=numeric())}
try(Lvl1GroupingL10 <- aggregate(WIPGate2L10$Receipt.Qty, by = list(WIPGate2L10$Hold.Code),FUN=sum),silent = TRUE)
if (exists("Lvl1GroupingL10")) {} else {Lvl1GroupingL10=data.table(Group.1=numeric(), x=numeric())}
Lvl1dfG30 <- data_frame(name = Lvl1GroupingG30$Group.1, y = Lvl1GroupingG30$x, drilldown = tolower((paste(name,"4"))))
Lvl1df2030 <- data_frame(name = Lvl1Grouping2030$Group.1, y = Lvl1Grouping2030$x, drilldown = tolower((paste(name,"3"))))
Lvl1df1020 <- data_frame(name = Lvl1Grouping1020$Group.1, y = Lvl1Grouping1020$x, drilldown = tolower((paste(name,"2"))))
Lvl1dfL10 <- data_frame(name = Lvl1GroupingL10$Group.1, y = Lvl1GroupingL10$x, drilldown = tolower((paste(name,"1"))))
Lvl2GroupingCRDTG30 <- WIPGate2Aging[WIPGate2Aging$Hold.Code == "CRDT" & WIPGate2Aging$Aging == "Greater than 30 days",]
try(Lvl2GroupingCRDTG30b <- aggregate(Lvl2GroupingCRDTG30$Receipt.Qty, by = list(Lvl2GroupingCRDTG30$Customer.Name),FUN=sum),silent = TRUE)
if (exists("Lvl2GroupingCRDTG30b")) {} else {Lvl2GroupingCRDTG30b=data.table(Group.1=numeric(), x=numeric())}
Lvl2GroupingCRDT2030 <- WIPGate2Aging[WIPGate2Aging$Hold.Code == "CRDT" & WIPGate2Aging$Aging == "Between 20-30 days",]
try(Lvl2GroupingCRDT2030b <- aggregate(Lvl2GroupingCRDT2030$Receipt.Qty, by = list(Lvl2GroupingCRDT2030$Customer.Name),FUN=sum),silent = TRUE)
if (exists("Lvl2GroupingCRDT2030b")) {} else {Lvl2GroupingCRDT2030b=data.table(Group.1=numeric(), x=numeric())}
Lvl2GroupingCRDT1020 <- WIPGate2Aging[WIPGate2Aging$Hold.Code == "CRDT" & WIPGate2Aging$Aging == "Between 10-20 days",]
try(Lvl2GroupingCRDT1020b <- aggregate(Lvl2GroupingCRDT1020$Receipt.Qty, by = list(Lvl2GroupingCRDT1020$Customer.Name),FUN=sum),silent = TRUE)
if (exists("Lvl2GroupingCRDT1020b")) {} else {Lvl2GroupingCRDT1020b=data.table(Group.1=numeric(), x=numeric())}
Lvl2GroupingCRDTL10 <- WIPGate2Aging[WIPGate2Aging$Hold.Code == "CRDT" & WIPGate2Aging$Aging == "Less than 10 days",]
try(Lvl2GroupingCRDTL10b <- aggregate(Lvl2GroupingCRDTL10$Receipt.Qty, by = list(Lvl2GroupingCRDTL10$Customer.Name),FUN=sum),silent = TRUE)
if (exists("Lvl2GroupingCRDTL10b")) {} else {Lvl2GroupingCRDTL10b=data.table(Group.1=numeric(), x=numeric())}
dfCRDTG30 <- arrange(data_frame(name = Lvl2GroupingCRDTG30b$Group.1,value = Lvl2GroupingCRDTG30b$x),desc(value))
dfCRDT2030 <- arrange(data_frame(name = Lvl2GroupingCRDT2030b$Group.1,value = Lvl2GroupingCRDT2030b$x),desc(value))
dfCRDT1020 <- arrange(data_frame(name = Lvl2GroupingCRDT1020b$Group.1,value = Lvl2GroupingCRDT1020b$x),desc(value))
dfCRDTL10 <- arrange(data_frame(name = Lvl2GroupingCRDTL10b$Group.1,value = Lvl2GroupingCRDTL10b$x),desc(value))
highchart() %>%
hc_chart(type = "column") %>%
hc_xAxis(type = "category") %>%
hc_yAxis(gridLineWidth = 0) %>%
hc_legend(enabled = TRUE) %>%
hc_plotOptions(column = list(stacking = "normal")) %>%
hc_add_series(name = "Greater than 30 days",data=Lvl1dfG30, color = "#D20000") %>%
hc_add_series(name = "Between 20-30 days",data=Lvl1df2030, color = "#FF7900") %>%
hc_add_series(name = "Between 10-20 days",data=Lvl1df1020, color = "#F6FC00") %>%
hc_add_series(name = "Less than 10 days",data=Lvl1dfL10, color = "#009A00") %>%
hc_drilldown(
allowPointDrilldown = TRUE,
series = list(
list(id = "crdt 4", data = list_parse2(dfCRDTG30), name="Customer"),
list(id = "crdt 3", data = list_parse2(dfCRDT2030), name="Customer"),
list(id = "crdt 2", data = list_parse2(dfCRDT1020), name="Customer"),
list(id = "crdt 1", data = list_parse2(dfCRDTL10), name="Customer")))

Related

Adding Images to animations in plotly (R)

I am trying to create an animation where an image has to move together with the dot that you can see in this image:
I have a dataset about Formula 1 and I want to show the image of the car instead of the dot in the image.
Here you have a summary of my dataset:
And the code of the graph:
prep = data[data$year == 2021,] %>% split(.$date) %>% accumulate(., ~bind_rows(.x,.y))%>%
bind_rows(.id = "frame")
prep2 = data[data$year == 2021,] %>% split(.$date) %>%
bind_rows(.id = "frame")
prep%>%
plot_ly(x = ~name, y = ~points, color = ~factor(name)) %>%
add_lines(frame = ~as.Date(frame, format = '%Y-%m-%d'))%>%
add_markers(data = prep2, frame = ~as.Date(frame, format = '%Y-%m-%d'))%>%
layout(yaxis = list(title = 'Puntos'),showlegend = FALSE,xaxis = list(title = 'Fecha de la carrera',range = c(as.Date(min(data$date[data$year == 2021]), format="%d/%m/%Y"),as.Date(max(data$date[data$year == 2021]), format="%d/%m/%Y"))))%>%
animation_slider(currentvalue = list(prefix = "Carrera "))

R + Highcharter + ShinyDashboard: How to add mouseOver event?

I am trying to make it so that when the client puts the mouse cursor over a value in the line graph, a text is created indicating the information of a column.
This is my code:
DataSource
library(tidyverse)
library(janitor)
library(lubridate)
library(highcharter)
library(shiny)
library(shinydashboard)
massShooting2018 <- read.csv('shootings_2018.csv')
massShooting2019 <- read.csv('shootings_2019.csv')
massShooting2020 <- read.csv('shootings_2020.csv')
massShooting2021 <- read.csv('shootings_2021.csv')
massShooting2022 <- read.csv('shootings_2022.csv')
# Merge datasets
massShootings <- rbind(massShooting2018,
massShooting2019,
massShooting2020,
massShooting2021,
massShooting2022)
# Clean
massShootings.clean <- massShootings %>%
clean_names() %>%
mutate(date = dmy(date))
massShootings.order <- massShootings.clean %>%
group_by(date, state) %>%
summarise(dead = sum(dead),
injured = sum(injured),
total = sum(total),
description, .groups = 'drop')
years <- massShootings.order %>%
sample_frac(1) %>%
select(date) %>%
mutate(date = year(date)) %>%
arrange(date)
hc_my_theme <- hc_theme_merge(hc_theme_flatdark(),
hc_theme(chart = list(backgroundColor = '#242f39'),
subtitle = list(style = list(color = '#a7a5a5'))))
header <- dashboardHeader(title = 'Mass Shootings')
sideBar <- dashboardSidebar(sidebarMenu(menuItem('Description', tabName = 'info', icon = icon('info')),
menuItem('Charts', tabName = 'charts', icon = icon('chart-line')),
menuItem('Contact', tabName = 'contact', icon = icon('address-card'))))
body <- dashboardBody(fluidPage(valueBoxOutput('totals'),
valueBoxOutput('dead'),
valueBoxOutput('injured')),
fluidPage(column(width = 4,
offset = 4,
selectInput('year',
label = 'Year',
choices = unique(years),
selected = 2018,
width = "100%"))),
box(title = "USA-States Map",
status = "primary",
solidHeader = TRUE,
collapsible = TRUE,
highchartOutput('mapPlot')),
box(title = 'Mass shootings in every state over time',
status = "primary",
solidHeader = TRUE,
collapsible = TRUE,
highchartOutput('linePlot')))
ui <- dashboardPage(header,
sideBar,
body)
server <- function(input,
output,
session)
{
df <- reactive({df <- massShootings.order %>%
filter(year(date) == input$year) %>%
group_by(state) %>%
summarise(dead = sum(dead),
injured = sum(injured),
total = sum(total),
description, .groups = 'drop')})
# Map Chart
output$mapPlot <- renderHighchart({
fn <- "function(){
console.log(this.name);
Shiny.onInputChange('mapPlotinput', this.name)
}"
hcmap(map = 'countries/us/custom/us-all-mainland.js',
data = df(),
joinBy = c('name', 'state'),
value = 'total',
borderWidth = 0.05,
nullColor = "#d3d3d3") %>%
hc_title(text = 'Mass Shooting') %>%
hc_colorAxis(stops = color_stops(colors = viridisLite::viridis(10,
begin = 0.1)),
type = "logarithmic") %>%
hc_tooltip(formatter= JS("function () { return this.point.name.bold() +
' <br />' +
' <br /> <b>Dead:</b> ' + this.point.dead +
' <br /> <b>Injured:</b> ' + this.point.injured ;}")) %>%
hc_add_theme(hc_my_theme) %>%
hc_mapNavigation(enabled = TRUE) %>%
hc_credits(enabled = FALSE) %>%
hc_exporting(enabled = TRUE) %>%
hc_plotOptions(series = list(cursor = "pointer",
point = list(events = list(click = JS(fn)))))})
# Stock chart
output$linePlot <- renderHighchart({
nme <- ifelse(is.null(input$mapPlotinput),
"United States of America",
input$mapPlotinput)
dfClick <- massShootings.order %>%
filter(state %in% nme) %>%
filter(year(date) == input$year) %>%
group_by(date) %>%
summarise(dead = sum(dead),
injured = sum(injured),
total = sum(total),
.groups = 'drop')
highchart(type = "stock") %>%
hc_chart("line",
name = "base",
hcaes(x = date)) %>%
hc_add_series(dfClick,
name = "Total",
type = "line",
hcaes(
x = date,
y = total)) %>%
hc_add_series(dfClick,
name = "Dead",
type = "line",
hcaes(
x = date,
y = dead)) %>%
hc_add_series(dfClick,
name = "Injured",
type = "line",
hcaes(
x = date,
y = injured)) %>%
hc_add_theme(hc_theme_538()) %>%
hc_tooltip(
crosshairs = TRUE,
shared = TRUE,
borderWidth = 2,
table = TRUE)})
# valueBox - Total
output$totals <- renderValueBox({dfTotals <- massShootings.order%>%
filter(year(date) == input$year) %>%
group_by(date) %>%
summarise(total = sum(dead, injured))
valueBox(sum(dfTotals$total), 'Total', icon = icon('calculator') ,color = 'light-blue')})
# valueBox - Deads
output$dead <- renderValueBox({dfDeads <- massShootings.order %>%
filter(year(date) == input$year) %>%
group_by(date) %>%
summarise(dead = sum(dead))
valueBox(sum(dfDeads$dead), 'Deads', icon = icon('skull') ,color = 'red')})
# valueBox - Injureds
output$injured <- renderValueBox({dfInjureds <- massShootings.order %>%
filter(year(date) == input$year) %>%
group_by(date) %>%
summarise(injured = sum(injured))
valueBox(sum(dfInjureds$injured), 'Injureds', icon = icon('user-injured') ,color = 'yellow')})
}
shinyApp(ui, server)
So far you can interact with the map which, when clicking on each state, creates a line graph next to it showing the values per day throughout the selected year.
What I am trying to achieve is that when the client places the cursor on the values of the graph line, text is created where the description of what happened on that date is shown, but the truth is that I do not know how to achieve it.
Thank you very much for reading my question and I would appreciate any kind of help to solve this problem

color doesn't match in scatter plot

I'm trying to create a scatter plot using highchart, but the color in the graph and in the labels are different. The color should be defined by the column named "Check_color" beacuse i'm using into Rshiny app, and sometimes i don't have all the options in the graph and the color should be align with the column "Rank". I mean, if only "Yes" is selected all points should be green, if only "No", should be red, etc...
Ando Righ now in the graph "Yes" in green, but in the labels are blue, and the same for the rest. How can i math the colors in the labels? Thanks !!
This is my current code
data = data.table(
CJ(x = seq(as.Date("2019-01-01"), as.Date("2019-01-10"), by = "day"),
group = seq(1,20))
)
data[, value := round(runif(n=200, 0,5),4)]
data = data.table(data %>% mutate(cat=cut(value, breaks=quantile(data[value!=0]$value, seq(0,1,0.1)), labels=seq(1,10))))
colf = colorRampPalette(colors = c("red","yellow", "green"))
cols = colf(10)
data[, color := as.factor(cols[cat])]
data$x = datetime_to_timestamp(data$x)
data = data.table(data %>% group_by(x) %>% mutate(y = (order(order(value))-sum(value<0,na.rm=T))))
data[, name := group]
data$x <- runif(200, 100, 1000) / 10
data$y <- runif(200, 100, 1000) / 10
data$gp_ <- round(runif(200,1,5), digits = 0)
data$Index <- seq(1,200,1)
data$Rank <- ifelse(data$gp_ == 1 , "Yes", ifelse(data$gp_ == 2 , "No",ifelse(data$gp_ == 3 , "Minor Deficiency",ifelse(data$gp_ == 4 , "Major Deficiency",ifelse(data$gp_ == 5 , "Not Applicable","")))))
data <- data[1:71,]
data$Check_color <- ifelse(data$Rank == "Yes" , "#14E632", ifelse(data$Rank == "No" , "#FA0101",ifelse(data$Rank == "Minor Deficiency" , "#FF99FF",ifelse(data$Rank == "Major Deficiency" , "#FF9933",ifelse(data$Rank == "Not Applicable" , "#CACECE","")))))
hc_1 <- data %>%
hchart('scatter', hcaes(x = x, y = y , group = Rank, color = Check_color )) %>%
hc_title(text = "<b>PUBLIC COMPANY D&O COVERAGE HEAT MAP </b>") %>%
hc_chart(
borderColor = "#999999",
borderRadius = 20,
borderWidth = 3) %>%
hc_tooltip(pointFormat = 'Provision ID: {point.Index} <br/>
Provision: {point.Check_color} <br/>
Severity: {point.y:.2f} <br/>
Frequency: {point.x:.2f} ')
hc_1
The problem with the two color scales came from using group = Rank and color = Check_color in the hcaes. Remove color = Check_color, and you get matching color. However, I do not know how to specify the color from there... I tried hc_color() which didn't work. Maybe someone else can complete this answer!
hc_1 <- data %>%
hchart('scatter', hcaes(x = x, y = y , group = Rank )) %>%
hc_title(text = "<b>PUBLIC COMPANY D&O COVERAGE HEAT MAP </b>") %>%
hc_chart(
borderColor = "#999999",
borderRadius = 20,
borderWidth = 3) %>%
hc_tooltip(pointFormat = 'Provision ID: {point.Index} <br/>
Provision: {point.Check_color} <br/>
Severity: {point.y:.2f} <br/>
Frequency: {point.x:.2f} ')
hc_1

HighCharter HCAES method not producing any visualization in R Shiny Dashboard

Attempting to build off of Stack Exchange Question:
R Highcharter: tooltip customization
Have a R module (below). That ingests some data and provides the UI including highcharter visualizations.
consolidatedlogModuleUI <- function(id){
ns <- NS(id)
tagList(
fluidRow(
bs4Card(highchartOutput(ns("fundedbydayChart")),
width = 12,
collapsible = TRUE)
),
fluidRow(
bs4TabCard(title = "Consolidated Log",
elevation = 2,
width = 12,
bs4TabPanel(
tabName = "tab1",
active = TRUE,
DT::dataTableOutput(ns("consolidatedlogTable"))
),
bs4TabPanel(
tabName = "tab2",
active = FALSE,
DT::dataTableOutput(ns("daysummaryTable"))
)
)
)
)
}
#######
# Consolidated Log Server Module
#######
consolidatedlogModule <- function(input,output,session,data){
ns <- session$ns
data$HasGap <- ifelse(data$GAPGrossRevenue > 0, 1, 0)
data$HasESC <- ifelse(data$ESCGrossRevenue > 0, 1, 0)
consolidatedLogVariables <- c("AcctID", "FSR", "DocSentDate", "DocsToLenderDate",
"FundedDate", "HasGap", "HasESC", "LoanRevenue")
logSummary <- data %>%
group_by(FundedMonthGroup) %>%
summarise(TotalCount = n()
, TotalAmount = sum(LoanRevenue)
, TotalGAP = sum(HasGap)
, TotalESC = sum(HasESC))
daySummary <- data %>%
group_by(FundedDayGroup) %>%
summarise(TotalCount = n()
,TotalAmount = sum(LoanRevenue))
### Consolidated Log Table
output$consolidatedlogTable = DT::renderDataTable({
data[consolidatedLogVariables]
}, extensions = "Responsive", rownames = FALSE,
caption = "Current Consolidated Log",
filter = "bottom"
)
output$daysummaryTable = DT::renderDataTable({
daySummary
}, extensions = "Responsive", rownames = FALSE,
caption = "Current Consolidated Log",
filter = "bottom"
)
### Charts
#Fundedbyday Chart
output$fundedbydayChart = renderHighchart({
highchart() %>%
hc_add_theme(hc_theme_ffx()) %>%
hc_title(text = "Loans Funded By Day") %>%
hc_add_series(data = daySummary, mapping = hcaes(x=FundedDayGroup, y=TotalAmount), type = "column", name = "Daily Loan Revenue",
tooltip = list(pointFormat = "Daily Revenue ${point.TotalAmount} across {point.TotalCount} deals")) %>%
hc_tooltip(crosshairs = TRUE)
# highchart() %>%
# hc_add_theme(hc_theme_ffx()) %>%
# hc_title(text = "Loans Funded By Day") %>%
# hc_add_series(daySummary$TotalAmount, type = "column", name = "Daily Loan Revenue",
# tooltip = list(pointFormat = "Daily Revenue ${point.TotalAmount} across {point.TotalCount} deals")) %>%
# hc_tooltip()
#hchart(daySummary, "column", hcaes(daySummary$FundedDayGroup, daySummary$TotalAmount))
})
}
The highChart function that is commented out works correctly in displaying the columns wanted. The Axis is incorrect and the tooltips is unformatted but the data displays.
Using the Non-commented highchart with the HCAES call and other items, the plot is displayed without any data.
Below is code to reproduce the test data set for the daySummary, the dataframe in question.
FundedDayGroup <- as.Date(c('2019-02-01', '2019-02-4', '2019-02-05'))
TotalCount <- c(1,13,18)
TotalAmount <- c(0, 13166, 15625)
daySummary <- data.frame(FundedDayGroup, TotalCount, TotalAmount)
The issue ended up being Highcharter not interpreting the POSIXct format of the dates and needing to cast the date variable using as.Date. Additionally added some logic to handle the xAxis and setting the datetime. Code below
highchart() %>%
hc_add_theme(hc_theme_ffx()) %>%
hc_title(text = "Loans Funded By Day") %>%
hc_add_series(data = daySummary, mapping = hcaes(x=as.Date(FundedDayGroup), y=TotalAmount), type = "column", name = "Daily Loan Revenue",
tooltip = list(pointFormat = "Daily Revenue ${point.TotalAmount} across {point.TotalCount} deals")) %>%
hc_xAxis(type = "datetime", labels=list(rotation = -45, y = 40) ) %>%
hc_yAxis(title=list(text = "Revenue")) %>%
hc_tooltip(crosshairs = TRUE)

R plotly + shiny reactive coupled event - Refresh chart with argument from click on same chart

I've been struggling with this all day, so hopefully somebody can explain a working solution for me/point out the error in my approach.
I have this network I want to visualize.
The goal is to only show the nodes that are directly connected to the reference node.
I want to update this chart when either 1) the reference node in the drop down list is changed or 2) when I click on one of the outlying nodes in the current plot that should be the new reference node.
The first option works, but I can't get 2) to work properly.
In output$selection I have currently commented what I thought should do the job. When I active this however weird looping behaviour happens that I don't understand.
What should I add to get the above described functionality?
Below a reproducible example.
library(plotly)
library(shiny)
library(dplyr)
library(tidyr)
### Selectionlist
varidlist = data.frame(varid = c('VAR1', 'VAR2', 'VAR3'), stringsAsFactors = F)
derivedvaridlist = data.frame(derivedvarid = paste0('DERIVEDVAR', 1:18), stringsAsFactors = F)
chartlist = data.frame(charts = paste0('1.1.', 1:9), stringsAsFactors = F)
selectionOptions = c(varidlist$varid, derivedvaridlist$derivedvarid, chartlist$charts)
ui <- fluidPage(
mainPanel(
fixedRow(selectInput('selectedID', label = 'Select varid',
choices = selectionOptions,
selected = 'VAR1')),
fixedRow(plotlyOutput("network"))
),
verbatimTextOutput("selection")
)
server <- function(input, output, session) {
createGraph <- function(selectedID){
varidlist = data.frame(varid = c('VAR1', 'VAR2', 'VAR3'), stringsAsFactors = F)
derivedvaridlist = data.frame(derivedvarid = paste0('DERIVEDVAR', 1:18), stringsAsFactors = F)
chartlist = data.frame(charts = paste0('1.1.', 1:9), stringsAsFactors = F)
selectionOptions = c(varidlist$varid, derivedvaridlist$derivedvarid, chartlist$charts)
varid_derivedvarid = data.frame(varid = c('VAR1', 'VAR2', 'VAR3'),
derivedvarid = paste0('DERIVEDVAR', 1:18), stringsAsFactors = F)
chart_varidderivedvarid = data.frame(chart = c('1.1.1'),
varidderivedvarid = c('OAP1', 'DERIVEDVAR1', 'DERIVEDVAR2', 'DERIVEDVAR3', 'DERIVEDVAR4'),
stringsAsFactors = F)
# if selectedID is VAR
if(selectedID %in% varidlist$varid){
adjacencyMatrix = varid_derivedvarid %>%
filter(varid == selectedID) %>%
mutate(type = 'derivedvarid') %>%
bind_rows(chart_varidderivedvarid %>%
filter(varidderivedvarid == selectedID) %>%
rename(varid = varidderivedvarid,
derivedvarid = chart) %>%
mutate(type='chart')) %>%
select(derivedvarid, varid, type)
nodeMatrix = adjacencyMatrix %>%
select(derivedvarid, type) %>%
add_row(derivedvarid=selectedID, type='varid')
}
# if selectedID is DERIVEDVAR
if(selectedID %in% derivedvaridlist$derivedvarid){
adjacencyMatrix = varid_derivedvarid %>%
filter(derivedvarid == selectedID) %>%
mutate(type = 'varid') %>%
bind_rows(chart_varidderivedvarid %>%
filter(varidderivedvarid == selectedID) %>%
rename(varid = varidderivedvarid,
derivedvarid = chart) %>%
mutate(type='chart')) %>%
select(derivedvarid, varid, type)
nodeMatrix = adjacencyMatrix %>%
select(varid, type) %>%
add_row(varid=selectedID, type='derivedvarid')
}
# if selectedID is chart
if(selectedID %in% chartlist$charts) {
adjacencyMatrix = chart_varidderivedvarid %>%
filter(chart == selectedID) %>%
mutate(type = '',
type = replace(type, varidderivedvarid %in% varidlist$varid, 'varid'),
type = replace(type, varidderivedvarid %in% derivedvaridlist$derivedvarid, 'derivedvarid')) %>%
select(varidderivedvarid, chart, type)
nodeMatrix = adjacencyMatrix %>%
select(varidderivedvarid, type) %>%
add_row(varidderivedvarid=selectedID, type='chart')
}
# Create all vertices:
nrNodes = dim(adjacencyMatrix)[1]
# Reference node coordinates
x0 = 0
y0 = 0
r = 4
nodes = data.frame(angles = 2*pi / nrNodes * 1:nrNodes,
nodeKey = adjacencyMatrix[, 1]) %>%
mutate(angles = angles + rnorm(n(), mean = 0, sd = .15), # Add noise to angle to avoid overlap in x-coordinate
x = x0 + r * cos(angles),
y = y0 + r * sin(angles)) %>%
add_row(x=x0, y=y0, nodeKey = selectedID)
# Create edges
edges = nodes %>%
select(x, y, nodeKey) %>%
filter(nodeKey != selectedID) %>%
mutate(x0=x0, y0=y0)
edge_shapes <- list()
for(i in 1:dim(edges)[1]) {
edge_shape = list(
type = "line",
line = list(color = "#030303", width = 0.3),
x0 = edges$x0[i],
y0 = edges$y0[i],
x1 = edges$x[i],
y1 = edges$y[i]
)
edge_shapes[[i]] <- edge_shape
}
# Layout for empty background
emptyBackground = list(title = "",
showgrid = FALSE,
showticklabels = FALSE,
zeroline = FALSE)
# Plot plotly
p = plot_ly(nodes, source='networkplot') %>%
add_trace(x = ~x, y = ~y, type = 'scatter',
mode = 'text', text = ~nodeKey,
textposition = 'middle',
hoverinfo='text',
textfont = list(color = '#000000', size = 16)) %>%
layout(title='Network',
showlegend = FALSE,
shapes = edge_shapes,
xaxis = emptyBackground,
yaxis = emptyBackground)
return(p)
}
output$network <- renderPlotly({
selectedID = input$selectedID
createGraph(selectedID)
})
output$selection <- renderPrint({
s <- event_data("plotly_click", source = "networkplot")
if (length(s) == 0) {
"Click on a node to use it as reference node"
} else {
# Get id of clicked node
plotdata = plotly_data(createGraph(input$selectedID))
newvarid = plotdata$nodeKey[s$pointNumber + 1]
# updateSelectInput(session,
# inputId = 'selectedID',
# label = 'Select ID',
# choices = selectionOptions,
# selected = newvarid)
# Get chart coordinates
cat("You selected: \n\n")
# as.list(s)
newvarid
}
})
}
shinyApp(ui, server, options = list(display.mode = "showcase"))
The trick here is to avoid circular reactive events. When using the updateSelectInput function you commented out, you end up in a loop because the updated input triggers the renderPrint function and renderPrint updates the menu.
You can break this behaviour by introducing observe() functions. One way to do this is to stick the updateSelectInput() function into an observeEvent() function that is only triggered if the user clicks on the plot and not if the dropdown menu is used. Any updates coming from input$selectedID are ignored by this function. Please see the full example below. I indicated the part of the code that changed at the bottom.
library(plotly)
library(shiny)
library(dplyr)
library(tidyr)
### Selectionlist
varidlist = data.frame(varid = c('VAR1', 'VAR2', 'VAR3'), stringsAsFactors = F)
derivedvaridlist = data.frame(derivedvarid = paste0('DERIVEDVAR', 1:18), stringsAsFactors = F)
chartlist = data.frame(charts = paste0('1.1.', 1:9), stringsAsFactors = F)
selectionOptions = c(varidlist$varid, derivedvaridlist$derivedvarid, chartlist$charts)
ui <- fluidPage(
mainPanel(
fixedRow(selectInput('selectedID', label = 'Select varid',
choices = selectionOptions,
selected = 'VAR1')),
fixedRow(plotlyOutput("network"))
),
verbatimTextOutput("selection")
)
server <- function(input, output, session) {
createGraph <- function(selectedID){
varidlist = data.frame(varid = c('VAR1', 'VAR2', 'VAR3'), stringsAsFactors = F)
derivedvaridlist = data.frame(derivedvarid = paste0('DERIVEDVAR', 1:18), stringsAsFactors = F)
chartlist = data.frame(charts = paste0('1.1.', 1:9), stringsAsFactors = F)
selectionOptions = c(varidlist$varid, derivedvaridlist$derivedvarid, chartlist$charts)
varid_derivedvarid = data.frame(varid = c('VAR1', 'VAR2', 'VAR3'),
derivedvarid = paste0('DERIVEDVAR', 1:18), stringsAsFactors = F)
chart_varidderivedvarid = data.frame(chart = c('1.1.1'),
varidderivedvarid = c('OAP1', 'DERIVEDVAR1', 'DERIVEDVAR2', 'DERIVEDVAR3', 'DERIVEDVAR4'),
stringsAsFactors = F)
# if selectedID is VAR
if(selectedID %in% varidlist$varid){
adjacencyMatrix = varid_derivedvarid %>%
filter(varid == selectedID) %>%
mutate(type = 'derivedvarid') %>%
bind_rows(chart_varidderivedvarid %>%
filter(varidderivedvarid == selectedID) %>%
rename(varid = varidderivedvarid,
derivedvarid = chart) %>%
mutate(type='chart')) %>%
select(derivedvarid, varid, type)
nodeMatrix = adjacencyMatrix %>%
select(derivedvarid, type) %>%
add_row(derivedvarid=selectedID, type='varid')
}
# if selectedID is DERIVEDVAR
if(selectedID %in% derivedvaridlist$derivedvarid){
adjacencyMatrix = varid_derivedvarid %>%
filter(derivedvarid == selectedID) %>%
mutate(type = 'varid') %>%
bind_rows(chart_varidderivedvarid %>%
filter(varidderivedvarid == selectedID) %>%
rename(varid = varidderivedvarid,
derivedvarid = chart) %>%
mutate(type='chart')) %>%
select(derivedvarid, varid, type)
nodeMatrix = adjacencyMatrix %>%
select(varid, type) %>%
add_row(varid=selectedID, type='derivedvarid')
}
# if selectedID is chart
if(selectedID %in% chartlist$charts) {
adjacencyMatrix = chart_varidderivedvarid %>%
filter(chart == selectedID) %>%
mutate(type = '',
type = replace(type, varidderivedvarid %in% varidlist$varid, 'varid'),
type = replace(type, varidderivedvarid %in% derivedvaridlist$derivedvarid, 'derivedvarid')) %>%
select(varidderivedvarid, chart, type)
nodeMatrix = adjacencyMatrix %>%
select(varidderivedvarid, type) %>%
add_row(varidderivedvarid=selectedID, type='chart')
}
# Create all vertices:
nrNodes = dim(adjacencyMatrix)[1]
# Reference node coordinates
x0 = 0
y0 = 0
r = 4
nodes = data.frame(angles = 2*pi / nrNodes * 1:nrNodes,
nodeKey = adjacencyMatrix[, 1]) %>%
mutate(angles = angles + rnorm(n(), mean = 0, sd = .15), # Add noise to angle to avoid overlap in x-coordinate
x = x0 + r * cos(angles),
y = y0 + r * sin(angles)) %>%
add_row(x=x0, y=y0, nodeKey = selectedID)
# Create edges
edges = nodes %>%
select(x, y, nodeKey) %>%
filter(nodeKey != selectedID) %>%
mutate(x0=x0, y0=y0)
edge_shapes <- list()
for(i in 1:dim(edges)[1]) {
edge_shape = list(
type = "line",
line = list(color = "#030303", width = 0.3),
x0 = edges$x0[i],
y0 = edges$y0[i],
x1 = edges$x[i],
y1 = edges$y[i]
)
edge_shapes[[i]] <- edge_shape
}
# Layout for empty background
emptyBackground = list(title = "",
showgrid = FALSE,
showticklabels = FALSE,
zeroline = FALSE)
# Plot plotly
p = plot_ly(nodes, source='networkplot') %>%
add_trace(x = ~x, y = ~y, type = 'scatter',
mode = 'text', text = ~nodeKey,
textposition = 'middle',
hoverinfo='text',
textfont = list(color = '#000000', size = 16)) %>%
layout(title='Network',
showlegend = FALSE,
shapes = edge_shapes,
xaxis = emptyBackground,
yaxis = emptyBackground)
return(p)
}
###############################################################################################
### Updated part
# Define reactive data
values <- reactiveValues(newvarid = NULL) # ID = "VAR1"
# Observer for change in dropdown menu
# observeEvent(input$selectedID, {
# values$ID = input$selectedID
# })
# Update dropdown menue based on click event
observeEvent(event_data("plotly_click", source = "networkplot"), {
s <- event_data("plotly_click", source = "networkplot")
plotdata = plotly_data(createGraph(input$selectedID))
values$newvarid = plotdata$nodeKey[s$pointNumber + 1]
updateSelectInput(session,
inputId = 'selectedID',
label = 'Select ID',
choices = selectionOptions,
selected = values$newvarid)
})
# Render Plot
output$network <- renderPlotly({
createGraph(input$selectedID)
})
# Render text
output$selection <- renderPrint({
if (is.null(values$newvarid)) {
"Click on a node to use it as reference node"
} else {
# Get chart coordinates
cat("You selected: \n\n")
# as.list(s)
values$newvarid
}
})
}
shinyApp(ui, server, options = list(display.mode = "showcase"))
I am not sure if the reactive values$newvarid is really necessary.

Resources