如何在 shiny 中创建 ggraph 反应式布局
How to create ggraph reactive layout in shiny
所以我一直在研究这个应用程序,查看 BC 的药物检查数据(参见 here),我想让我的 ggraph 图更具交互性,这样人们就可以更容易地看到不同节点之间的连接,通过点击鼠标悬停在它们上面。但是,每当我尝试将 create_layout() 放入反应函数时,我都会得到
Warning: Error in E: Not a graph object
我需要将布局置于反应函数中,以便 renderPrint 可以读取它,以用于我尝试添加的内容。当我将 运行 print(class(layout())) 作为反应式时,它显示的属性与在 renderPlot.运行 中显示的属性相同。
整个代码有点复杂,但可以在此处找到工作版本:https://github.com/alexbetsos/DC_Shiny,我提供了一个更简单的版本,错误如下。不幸的是,有些方面无法简化,但我已经尽力了。
所有与滑块相关的内容都可以忽略 - 它可能不是很好的形式,但效果很好。
请注意,我将节点和边缘作为反应式,因为它们在其他地方使用,如在更大的闪亮项目中。这里 atm 唯一的主要问题是我无法弄清楚如何将布局功能作为反应式。
感谢所有帮助。
为了使数据可重现,并使其他所有功能正常运行,我已将一小部分数据上传到 github,以便滑块仍能正常运行,但是,我对其进行了限制只有 1 种预期物质。
library(readr)
urlfile <- "https://raw.githubusercontent.com/alexbetsos/Whosebughelp/main/test_data.csv"
test_data <- read_csv(url(urlfile))
test_data <- test_data[,-c(1)]
poss.w <- data.frame(ID = c(80,81,82,83),
Days2 = c("Jun 28-\nJul 4\n2021",
"Jul 5-\nJul 11\n2021",
"Jul 12-\nJul 18\n2021",
"Jul 19-\nJul 25\n2021"))
get_id <- c(max(poss.w$ID)-1, max(poss.w$ID))
interest <- c("Fentanyl/Down", "Opioids Minus Fentanyl (Grouped)", "All Opioids (Grouped)", "Methamphetamine",
"Ketamine", "Cocaine", "Crack Cocaine", "MDMA")
test_data <- test_data[test_data$Expected.Substance %in% interest,]
###Creates df for classification and the colour palette####
node_col <- structure(list(ID = 1:36,
Names = c("Caffeine", "Erythritol",
"Uncertain Match", "Fent <5%", "Fentanyl or Analog", "Xylitol",
"Benzodiazepine <5%", "Mannitol", "Uncertain Oil/Carb/Sugar",
"Dimethyl Sulfone", "Soap", "Water", "Methamphetamine", "Acetaminophen",
"para-Fluorofentanyl", "No Cuts\nFentanyl or Analog", "No Cuts\nUncertain Match",
"Propionanilide", "MDMA", "Safrole", "Sucrose", "Phenacetin",
"4-ANPP", "Lactose", "Inositol", "Creatine", "Etizolam",
"No Cuts\nTrichloroisocyanuric Acid",
"Naproxen", "Heroin", "PEG", "Diphenhydramine", "Cocaine", "Glutamine",
"Benzocaine", "Sorbitol"),
Classification = c("Stimulant", "Buff",
"Other or NA", "Opioid", "Opioid", "Buff", "Benzodiazepine",
"Buff", "Other or NA", "Buff", "new_val", "Other or NA", "Stimulant",
"Buff", "Opioid", "Opioid", "Other or NA", "Buff", "Stimulant",
"Other or NA", "Buff", "Buff", "Precursor", "Buff", "Buff", "Other or NA",
"Benzodiazepine", "new_val", "new_val", "Opioid", "Buff", "Other or NA",
"Stimulant", "Buff", "Buff", "Buff")), row.names = c(NA, -36L
), class = "data.frame")
regrouped <- data.frame(ID = seq(2000, 1999+length(unique(node_col$Classification)),by=1),
Names = unique(unique(node_col$Classification)),
Classification = unique(unique(node_col$Classification)))
node_col <- rbind(node_col, regrouped)
####---Libraries & Functions---####
library(tidyverse)
library(igraph)
library(ggraph)
library(tidygraph)
library(shiny)
library(shinyjs)
library(shinydashboard)
library(shinyWidgets)
library(shinythemes)
ui <- navbarPage(title = "BC Drug Checking Visualizations",theme = shinytheme("flatly"),
tabPanel("Instructions",
tabPanel("Drug Checking Data",
sidebarLayout(
sidebarPanel(width = 2,
selectInput("Drug",
"Expected Substance",
choices = interest,
selected = NULL),
selectInput("City",
"City",
choices = unique(test_data$City.Town),
selected = "Vancouver"),
radioButtons("duration",
label = "1 Week or Multiple",
choices = c("1 Week", "Multiple"),
selected = "1 Week"),
checkboxGroupInput("regroup",
label = "Regroup Variables",
choices = regrouped$Classification,
selected = NULL)
),
mainPanel(width = 9,
fluidRow(
uiOutput("myList")),
tabsetPanel(
tabPanel("Network Graph",
fluidRow(tabstyle='padding:0px',
box(width = 12,
offset = 0,
plotOutput("net", width = "100%",
height = "750px",
click = "plot_click",
brush = "plot_brush"))),
fluidRow(verbatimTextOutput("info"))
#Need to add the bar chart & Table back in
)
)
)))))
server <- function(input, output, session) {
#Create reactive value to hold slider info
slidertype <- reactiveValues()
slidertype$type <- "default"
observeEvent(input$duration, {
#When person changes from 1 week to multiple it will change slider
if(input$duration == "1 Week"){
slidertype$type <- "1 Week"
} else if(input$duration == "Multiple"){
slidertype$type <- "Multiple"
} else {
slidertype$type <- "default"
}
})
#Renders the UI for the slider
output$myList <- renderUI({
#Changes based on whether someone selects output
if(slidertype$type == "1 Week"){
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID == max(get_id)]),
force_edges = TRUE,
width = "1200px")
} else if(slidertype$type == "Multiple") {
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID %in% get_id]),
force_edges = TRUE,
width = "1200px")
} else{
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID == max(get_id)]),
force_edges = TRUE, width = "1200px")
}
})
#Create reactive df - requires different filtering
df_react <- reactive({
if(slidertype$type != "Multiple"){
test_data%>%
filter(Expected.Substance == input$Drug & Week.val %in% input$Change & City.Town == input$City)
} else if (slidertype$type == "Multiple") {
test_data %>%
filter(Expected.Substance == input$Drug & Week.val <= input$Change[2] &
Week.val >=input$Change[1] & City.Town == input$City)
}
})
observeEvent(input$City,{
poss_e <- poss.w[poss.w$Days2 <= max(test_data$Week.val[test_data$City.Town == input$City]) & poss.w$Days2 >= min(test_data$Week.val[test_data$City.Town == input$City]),]
if(slidertype$type != "Multiple"){
new_id <- max(poss_e$ID)
updateSliderTextInput(session,inputId = "Change", choices = as.character(poss_e$Days2), selected = as.character(poss_e$Days2[poss_e$ID == max(get_id)]))
} else {
new_id <- c(max(poss_e$ID)-1, max(poss_e$ID))
updateSliderTextInput(session,inputId = "Change", choices = as.character(poss_e$Days2), selected = as.character(poss_e$Days2[poss_e$ID %in% get_id]))
}
})
df_react2 <- reactive({
if(!is.null(input$regroup)){
df_react() %>%
dplyr::rename(Names = value) %>%
left_join(node_col[,c(2:3)]) %>%
mutate(Classification2 = ifelse(Classification %in% input$regroup, Classification, Names)) %>%
rename(value = Classification2)
} else {
df_react()
}
})
#Nodes for the Social Network Visualization
nodes <- reactive({
node <- df_react2() %>%
select(value) %>%
count(value) %>%
dplyr::rename(Names = value, Weight = n) %>%
left_join(node_col) %>%
select(ID, Names, Weight, Classification) %>%
arrange(desc(Weight))
node$Weight[grepl("No Cuts", node$Names)] <- node$Weight[grepl("No Cuts", node$Names)]/2
return(node)
})
#Edges for SN
#The nesting solution was a huge help from a user on Whosebug
#This code doesn't work without it:
edges2 <- reactive({
if(nrow(df_react2()) != 0){
df_react2() %>%
select(ID, value) %>%
nest(data=(value)) %>%
mutate(pairs=map(data, ~as_tibble(t(combn(.$value, 2))), .name_repair=T, .keep)) %>%
unnest(pairs) %>%
select(V1, V2) %>%
group_by(V1, V2) %>%
summarise(amount = n()) %>%
ungroup()
} else {
df_react2()
}
})
the_layout <- reactive({
edges <- edges2()
validate(
need(nrow(edges) >0.9, "Not tested During this Time")
)
colnames(edges) <- c("to", "from", "weight")
edges$from <- nodes()$ID[match(edges$from, nodes()$Names)]
edges$to <- nodes()$ID[match(edges$to, nodes()$Names)]
edges <- select(edges, from, to, weight)
g <- graph_from_data_frame(d = edges, vertices = nodes(), directed = FALSE)
g <- simplify(g, remove.loops = TRUE)
if(input$Drug %in% c(V(g)$Names, "Fentanyl/Down", "All Opioids (Grouped)") &
nrow(edges) >=10){
#Checks if there is just one graph or several
if(is.connected(g) == FALSE){
#if true then, it splits the main graph from the subgraphs
c <- clusters(g); cn <- cbind(V(g), c$membership)
lc <- which(which.max(c$csize)==c$membership);
gs <- induced.subgraph(g, lc)
if(input$Drug == "All Opioids (Grouped)"|input$Drug == "Fentanyl/Down"){
st1 <- layout_as_star(gs, center = V(gs)$Names == "Fentanyl or Analog")
}else{
st1 <- layout_as_star(gs, center = V(gs)$Names == input$Drug)
}
st1 <- norm_coords(st1, xmin = -0.6, xmax = 0.6,
ymin = -0.6, ymax = +0.6,
zmin = -0.6, zmax = +0.6)
#Normalize even and odd rows at different min & max to stagger nodes
st1[seq(2, nrow(st1),2),] <- norm_coords(st1[seq(2, nrow(st1),2),],
xmin = -0.45, xmax = 0.45,
ymin = -0.45, ymax = +0.45,
zmin = -0.45, zmax = +0.45)
lc2 <- which(!which.max(c$csize)==c$membership)
gs2 <- induced.subgraph(g, lc2)
circ <- layout_in_circle(gs2)
circ <- norm_coords(circ, xmin = -0.8, xmax = 0.8,
ymin = -0.8, ymax = +0.8,
zmin = -0.8, zmax = +0.8)
test2 <- rbind(st1,circ)
g <- gs %du% gs2
t_lay <- create_layout(g, test2)
}else{
st1 <- layout_as_star(g, center = V(g)$Names == input$Drug)
st1 <- norm_coords(st1, xmin = -0.8, xmax = 0.8,
ymin = -0.8, ymax = +0.8,
zmin = -0.8, zmax = +0.8)
t_lay <- create_layout(g, st1)
}
#For every other drug sample - still WIP
} else {
t_lay <- create_layout(g, layout = "nicely")
}
})
#Set graph space limits
output$net <- renderPlot({
t_lay <- the_layout()
x_max <- max(t_lay$x)+0.1
x_min <- min(t_lay$x)-0.1
y_min <- min(t_lay$y)-0.1
y_max <- max(t_lay$y)+0.1
par(mar = c(0, 0, 0, 0))
ggraph(t_lay) +
geom_edge_link0(aes(width = E(g)$weight), colour = "grey") + # add edges to the plot
scale_edge_width_continuous(breaks = c(1, 5, 10, 25, 50,100),
label = c(1, 5, 10, 25, 50, 100),
range = c(1,20), name = "Frequency Found Together",
limits = c(0,400),
guide = guide_legend(order = 2,
nrow = 1,
ncol =7)) +
geom_node_point(aes(size = V(g)$Weight, color = V(g)$Classification)) +
coord_cartesian(ylim = c(y_min, y_max), xlim = c(x_min, x_max)) +
geom_node_text(aes(label = V(g)$Names), angle = 30, size = 5) +
scale_size(breaks = c(1,10,20,40, 60,80, 100), label=scales::number,
range = c(1,60), limits = c(1,400), name = "# of Times Drug Found \n in Test Results",
guide = guide_legend(order = 1,
nrow = 4,
ncol = 2,
label.hjust =0.5)) +
labs(caption = "Fent/Benzodiazepine < 5% means substance tested positive on test strip") +
theme(legend.position= "right",
legend.box.background = element_blank(),
legend.direction = "vertical",
legend.key = element_blank(),
legend.background = element_blank(),
legend.text = element_text(size=12, hjust = 0.4, inherit.blank = TRUE),
legend.box.just = "top",
legend.box = "vertical",
legend.justification = "right",
legend.box.spacing = unit(0.5,"cm"),
plot.caption = element_text(size = 14),
legend.title.align = 0.2,
legend.text.align = 0.4,
legend.title=element_text(size=14),
legend.key.width = unit(0.5, "cm"),
legend.key.height = unit(0.2, "cm"),
legend.spacing = unit(0.5, "cm"),
panel.background = element_blank(),
legend.box.margin = margin(t = 0, r = 0, b = 0, l = 0, unit = "cm"),
legend.margin = margin(0,0, 0, 0, unit = "cm"))+
guides(color = guide_legend(override.aes = list(size=10),
nrow = 5,
ncol = 4))
})
#I would like to do something like this
output$info <- renderPrint({
brushedPoints(the_layout(), input$plot_brush, allRows = TRUE)})
}
shinyApp(ui = ui, server = server)```
我通过将 g
的分配从 <-
更改为 <<-
来让闪亮的应用程序运行。下面是修改后的代码。
urlfile <- "https://raw.githubusercontent.com/alexbetsos/Whosebughelp/main/test_data.csv"
test_data <- read_csv(url(urlfile))
test_data <- test_data[,-c(1)]
poss.w <- data.frame(ID = c(80,81,82,83),
Days2 = c("Jun 28-\nJul 4\n2021",
"Jul 5-\nJul 11\n2021",
"Jul 12-\nJul 18\n2021",
"Jul 19-\nJul 25\n2021"))
get_id <- c(max(poss.w$ID)-1, max(poss.w$ID))
interest <- c("Fentanyl/Down", "Opioids Minus Fentanyl (Grouped)", "All Opioids (Grouped)", "Methamphetamine",
"Ketamine", "Cocaine", "Crack Cocaine", "MDMA")
test_data <- test_data[test_data$Expected.Substance %in% interest,]
###Creates df for classification and the colour palette####
node_col <- structure(list(ID = 1:36,
Names = c("Caffeine", "Erythritol",
"Uncertain Match", "Fent <5%", "Fentanyl or Analog", "Xylitol",
"Benzodiazepine <5%", "Mannitol", "Uncertain Oil/Carb/Sugar",
"Dimethyl Sulfone", "Soap", "Water", "Methamphetamine", "Acetaminophen",
"para-Fluorofentanyl", "No Cuts\nFentanyl or Analog", "No Cuts\nUncertain Match",
"Propionanilide", "MDMA", "Safrole", "Sucrose", "Phenacetin",
"4-ANPP", "Lactose", "Inositol", "Creatine", "Etizolam",
"No Cuts\nTrichloroisocyanuric Acid",
"Naproxen", "Heroin", "PEG", "Diphenhydramine", "Cocaine", "Glutamine",
"Benzocaine", "Sorbitol"),
Classification = c("Stimulant", "Buff",
"Other or NA", "Opioid", "Opioid", "Buff", "Benzodiazepine",
"Buff", "Other or NA", "Buff", "new_val", "Other or NA", "Stimulant",
"Buff", "Opioid", "Opioid", "Other or NA", "Buff", "Stimulant",
"Other or NA", "Buff", "Buff", "Precursor", "Buff", "Buff", "Other or NA",
"Benzodiazepine", "new_val", "new_val", "Opioid", "Buff", "Other or NA",
"Stimulant", "Buff", "Buff", "Buff")), row.names = c(NA, -36L
), class = "data.frame")
regrouped <- data.frame(ID = seq(2000, 1999+length(unique(node_col$Classification)),by=1),
Names = unique(unique(node_col$Classification)),
Classification = unique(unique(node_col$Classification)))
node_col <- rbind(node_col, regrouped)
####---Libraries & Functions---####
library(tidyverse)
library(igraph)
library(ggraph)
library(tidygraph)
library(shiny)
library(shinyjs)
library(shinydashboard)
library(shinyWidgets)
library(shinythemes)
ui <- navbarPage(title = "BC Drug Checking Visualizations",theme = shinytheme("flatly"),
tabPanel("Instructions",
tabPanel("Drug Checking Data",
sidebarLayout(
sidebarPanel(width = 2,
selectInput("Drug",
"Expected Substance",
choices = interest,
selected = NULL),
selectInput("City",
"City",
choices = unique(test_data$City.Town),
selected = "Vancouver"),
radioButtons("duration",
label = "1 Week or Multiple",
choices = c("1 Week", "Multiple"),
selected = "1 Week"),
checkboxGroupInput("regroup",
label = "Regroup Variables",
choices = regrouped$Classification,
selected = NULL)
),
mainPanel(width = 9,
fluidRow(
uiOutput("myList")),
tabsetPanel(
tabPanel("Network Graph",
fluidRow(tabstyle='padding:0px',
box(width = 12,
offset = 0,
plotlOutput("net", width = "100%",
height = "750px",
click = "plot_click",
brush = "plot_brush"))),
fluidRow(verbatimTextOutput("info"))
#Need to add the bar chart & Table back in
)
)
)))))
server <- function(input, output, session) {
#Create reactive value to hold slider info
slidertype <- reactiveValues()
slidertype$type <- "default"
observeEvent(input$duration, {
#When person changes from 1 week to multiple it will change slider
if(input$duration == "1 Week"){
slidertype$type <- "1 Week"
} else if(input$duration == "Multiple"){
slidertype$type <- "Multiple"
} else {
slidertype$type <- "default"
}
})
#Renders the UI for the slider
output$myList <- renderUI({
#Changes based on whether someone selects output
if(slidertype$type == "1 Week"){
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID == max(get_id)]),
force_edges = TRUE,
width = "1200px")
} else if(slidertype$type == "Multiple") {
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID %in% get_id]),
force_edges = TRUE,
width = "1200px")
} else{
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID == max(get_id)]),
force_edges = TRUE, width = "1200px")
}
})
#Create reactive df - requires different filtering
df_react <- reactive({
if(slidertype$type != "Multiple"){
test_data %>%
filter(Expected.Substance == input$Drug & Week.val %in% input$Change & City.Town == input$City)
} else if (slidertype$type == "Multiple") {
test_data %>%
filter(Expected.Substance == input$Drug & Week.val <= input$Change[2] &
Week.val >=input$Change[1] & City.Town == input$City)
}
})
observeEvent(input$City,{
poss_e <- poss.w[poss.w$Days2 <= max(test_data$Week.val[test_data$City.Town == input$City]) & poss.w$Days2 >= min(test_data$Week.val[test_data$City.Town == input$City]),]
if(slidertype$type != "Multiple"){
new_id <- max(poss_e$ID)
updateSliderTextInput(session,inputId = "Change", choices = as.character(poss_e$Days2), selected = as.character(poss_e$Days2[poss_e$ID == max(get_id)]))
} else {
new_id <- c(max(poss_e$ID)-1, max(poss_e$ID))
updateSliderTextInput(session,inputId = "Change", choices = as.character(poss_e$Days2), selected = as.character(poss_e$Days2[poss_e$ID %in% get_id]))
}
})
df_react2 <- reactive({
if(!is.null(input$regroup)){
df_react() %>%
dplyr::rename(Names = value) %>%
left_join(node_col[,c(2:3)]) %>%
mutate(Classification2 = ifelse(Classification %in% input$regroup, Classification, Names)) %>%
rename(value = Classification2)
} else {
df_react()
}
})
#Nodes for the Social Network Visualization
nodes <- reactive({
node <- df_react2() %>%
select(value) %>%
count(value) %>%
dplyr::rename(Names = value, Weight = n) %>%
left_join(node_col) %>%
select(ID, Names, Weight, Classification) %>%
arrange(desc(Weight))
node$Weight[grepl("No Cuts", node$Names)] <- node$Weight[grepl("No Cuts", node$Names)]/2
return(node)
})
#Edges for SN
#The nesting solution was a huge help from a user on Whosebug
#This code doesn't work without it:
edges2 <- reactive({
if(nrow(df_react2()) != 0){
df_react2() %>%
select(ID, value) %>%
nest(data=(value)) %>%
mutate(pairs=map(data, ~as_tibble(t(combn(.$value, 2))), .name_repair=T, .keep)) %>%
unnest(pairs) %>%
select(V1, V2) %>%
group_by(V1, V2) %>%
summarise(amount = n()) %>%
ungroup()
} else {
df_react2()
}
})
the_layout <- reactive({
edges <- edges2()
validate(
need(nrow(edges) >0.9, "Not tested During this Time")
)
colnames(edges) <- c("to", "from", "weight")
edges$from <- nodes()$ID[match(edges$from, nodes()$Names)]
edges$to <- nodes()$ID[match(edges$to, nodes()$Names)]
edges <- select(edges, from, to, weight)
g <<- graph_from_data_frame(d = edges, vertices = nodes(), directed = FALSE)
g <<- simplify(g, remove.loops = TRUE)
if(input$Drug %in% c(V(g)$Names, "Fentanyl/Down", "All Opioids (Grouped)") &
nrow(edges) >=10){
#Checks if there is just one graph or several
if(is.connected(g) == FALSE){
#if true then, it splits the main graph from the subgraphs
c <- clusters(g); cn <- cbind(V(g), c$membership)
lc <- which(which.max(c$csize)==c$membership);
gs <- induced.subgraph(g, lc)
if(input$Drug == "All Opioids (Grouped)"|input$Drug == "Fentanyl/Down"){
st1 <- layout_as_star(gs, center = V(gs)$Names == "Fentanyl or Analog")
}else{
st1 <- layout_as_star(gs, center = V(gs)$Names == input$Drug)
}
st1 <- norm_coords(st1, xmin = -0.6, xmax = 0.6,
ymin = -0.6, ymax = +0.6,
zmin = -0.6, zmax = +0.6)
#Normalize even and odd rows at different min & max to stagger nodes
st1[seq(2, nrow(st1),2),] <- norm_coords(st1[seq(2, nrow(st1),2),],
xmin = -0.45, xmax = 0.45,
ymin = -0.45, ymax = +0.45,
zmin = -0.45, zmax = +0.45)
lc2 <- which(!which.max(c$csize)==c$membership)
gs2 <- induced.subgraph(g, lc2)
circ <- layout_in_circle(gs2)
circ <- norm_coords(circ, xmin = -0.8, xmax = 0.8,
ymin = -0.8, ymax = +0.8,
zmin = -0.8, zmax = +0.8)
test2 <- rbind(st1,circ)
g <- gs %du% gs2
t_lay <- create_layout(g, test2)
}else{
st1 <- layout_as_star(g, center = V(g)$Names == input$Drug)
st1 <- norm_coords(st1, xmin = -0.8, xmax = 0.8,
ymin = -0.8, ymax = +0.8,
zmin = -0.8, zmax = +0.8)
t_lay <- create_layout(g, st1)
}
#For every other drug sample - still WIP
} else {
t_lay <- create_layout(g, layout = "nicely")
}
})
#Set graph space limits
output$net <- renderPlot({
t_lay <<- the_layout()
x_max <- max(t_lay$x)+0.1
x_min <- min(t_lay$x)-0.1
y_min <- min(t_lay$y)-0.1
y_max <- max(t_lay$y)+0.1
par(mar = c(0, 0, 0, 0))
ggraph(t_lay) +
geom_edge_link0(aes(width = E(g)$weight), colour = "grey") + # add edges to the plot
scale_edge_width_continuous(breaks = c(1, 5, 10, 25, 50,100),
label = c(1, 5, 10, 25, 50, 100),
range = c(1,20), name = "Frequency Found Together",
limits = c(0,400),
guide = guide_legend(order = 2,
nrow = 1,
ncol =7)) +
geom_node_point(aes(size = V(g)$Weight, color = V(g)$Classification)) +
coord_cartesian(ylim = c(y_min, y_max), xlim = c(x_min, x_max)) +
geom_node_text(aes(label = V(g)$Names), angle = 30, size = 5) +
scale_size(breaks = c(1,10,20,40, 60,80, 100), label=scales::number,
range = c(1,60), limits = c(1,400), name = "# of Times Drug Found \n in Test Results",
guide = guide_legend(order = 1,
nrow = 4,
ncol = 2,
label.hjust =0.5)) +
labs(caption = "Fent/Benzodiazepine < 5% means substance tested positive on test strip") +
theme(legend.position= "right",
legend.box.background = element_blank(),
legend.direction = "vertical",
legend.key = element_blank(),
legend.background = element_blank(),
legend.text = element_text(size=12, hjust = 0.4, inherit.blank = TRUE),
legend.box.just = "top",
legend.box = "vertical",
legend.justification = "right",
legend.box.spacing = unit(0.5,"cm"),
plot.caption = element_text(size = 14),
legend.title.align = 0.2,
legend.text.align = 0.4,
legend.title=element_text(size=14),
legend.key.width = unit(0.5, "cm"),
legend.key.height = unit(0.2, "cm"),
legend.spacing = unit(0.5, "cm"),
panel.background = element_blank(),
legend.box.margin = margin(t = 0, r = 0, b = 0, l = 0, unit = "cm"),
legend.margin = margin(0,0, 0, 0, unit = "cm"))+
guides(color = guide_legend(override.aes = list(size=10),
nrow = 5,
ncol = 4))
})
#I would like to do something like this
output$info <- renderPrint({
brushedPoints(the_layout(), input$plot_brush, allRows = TRUE)})
}
shinyApp(ui = ui, server = server)
所以我一直在研究这个应用程序,查看 BC 的药物检查数据(参见 here),我想让我的 ggraph 图更具交互性,这样人们就可以更容易地看到不同节点之间的连接,通过点击鼠标悬停在它们上面。但是,每当我尝试将 create_layout() 放入反应函数时,我都会得到
Warning: Error in E: Not a graph object
我需要将布局置于反应函数中,以便 renderPrint 可以读取它,以用于我尝试添加的内容。当我将 运行 print(class(layout())) 作为反应式时,它显示的属性与在 renderPlot.运行 中显示的属性相同。
整个代码有点复杂,但可以在此处找到工作版本:https://github.com/alexbetsos/DC_Shiny,我提供了一个更简单的版本,错误如下。不幸的是,有些方面无法简化,但我已经尽力了。
所有与滑块相关的内容都可以忽略 - 它可能不是很好的形式,但效果很好。
请注意,我将节点和边缘作为反应式,因为它们在其他地方使用,如在更大的闪亮项目中。这里 atm 唯一的主要问题是我无法弄清楚如何将布局功能作为反应式。
感谢所有帮助。
为了使数据可重现,并使其他所有功能正常运行,我已将一小部分数据上传到 github,以便滑块仍能正常运行,但是,我对其进行了限制只有 1 种预期物质。
library(readr)
urlfile <- "https://raw.githubusercontent.com/alexbetsos/Whosebughelp/main/test_data.csv"
test_data <- read_csv(url(urlfile))
test_data <- test_data[,-c(1)]
poss.w <- data.frame(ID = c(80,81,82,83),
Days2 = c("Jun 28-\nJul 4\n2021",
"Jul 5-\nJul 11\n2021",
"Jul 12-\nJul 18\n2021",
"Jul 19-\nJul 25\n2021"))
get_id <- c(max(poss.w$ID)-1, max(poss.w$ID))
interest <- c("Fentanyl/Down", "Opioids Minus Fentanyl (Grouped)", "All Opioids (Grouped)", "Methamphetamine",
"Ketamine", "Cocaine", "Crack Cocaine", "MDMA")
test_data <- test_data[test_data$Expected.Substance %in% interest,]
###Creates df for classification and the colour palette####
node_col <- structure(list(ID = 1:36,
Names = c("Caffeine", "Erythritol",
"Uncertain Match", "Fent <5%", "Fentanyl or Analog", "Xylitol",
"Benzodiazepine <5%", "Mannitol", "Uncertain Oil/Carb/Sugar",
"Dimethyl Sulfone", "Soap", "Water", "Methamphetamine", "Acetaminophen",
"para-Fluorofentanyl", "No Cuts\nFentanyl or Analog", "No Cuts\nUncertain Match",
"Propionanilide", "MDMA", "Safrole", "Sucrose", "Phenacetin",
"4-ANPP", "Lactose", "Inositol", "Creatine", "Etizolam",
"No Cuts\nTrichloroisocyanuric Acid",
"Naproxen", "Heroin", "PEG", "Diphenhydramine", "Cocaine", "Glutamine",
"Benzocaine", "Sorbitol"),
Classification = c("Stimulant", "Buff",
"Other or NA", "Opioid", "Opioid", "Buff", "Benzodiazepine",
"Buff", "Other or NA", "Buff", "new_val", "Other or NA", "Stimulant",
"Buff", "Opioid", "Opioid", "Other or NA", "Buff", "Stimulant",
"Other or NA", "Buff", "Buff", "Precursor", "Buff", "Buff", "Other or NA",
"Benzodiazepine", "new_val", "new_val", "Opioid", "Buff", "Other or NA",
"Stimulant", "Buff", "Buff", "Buff")), row.names = c(NA, -36L
), class = "data.frame")
regrouped <- data.frame(ID = seq(2000, 1999+length(unique(node_col$Classification)),by=1),
Names = unique(unique(node_col$Classification)),
Classification = unique(unique(node_col$Classification)))
node_col <- rbind(node_col, regrouped)
####---Libraries & Functions---####
library(tidyverse)
library(igraph)
library(ggraph)
library(tidygraph)
library(shiny)
library(shinyjs)
library(shinydashboard)
library(shinyWidgets)
library(shinythemes)
ui <- navbarPage(title = "BC Drug Checking Visualizations",theme = shinytheme("flatly"),
tabPanel("Instructions",
tabPanel("Drug Checking Data",
sidebarLayout(
sidebarPanel(width = 2,
selectInput("Drug",
"Expected Substance",
choices = interest,
selected = NULL),
selectInput("City",
"City",
choices = unique(test_data$City.Town),
selected = "Vancouver"),
radioButtons("duration",
label = "1 Week or Multiple",
choices = c("1 Week", "Multiple"),
selected = "1 Week"),
checkboxGroupInput("regroup",
label = "Regroup Variables",
choices = regrouped$Classification,
selected = NULL)
),
mainPanel(width = 9,
fluidRow(
uiOutput("myList")),
tabsetPanel(
tabPanel("Network Graph",
fluidRow(tabstyle='padding:0px',
box(width = 12,
offset = 0,
plotOutput("net", width = "100%",
height = "750px",
click = "plot_click",
brush = "plot_brush"))),
fluidRow(verbatimTextOutput("info"))
#Need to add the bar chart & Table back in
)
)
)))))
server <- function(input, output, session) {
#Create reactive value to hold slider info
slidertype <- reactiveValues()
slidertype$type <- "default"
observeEvent(input$duration, {
#When person changes from 1 week to multiple it will change slider
if(input$duration == "1 Week"){
slidertype$type <- "1 Week"
} else if(input$duration == "Multiple"){
slidertype$type <- "Multiple"
} else {
slidertype$type <- "default"
}
})
#Renders the UI for the slider
output$myList <- renderUI({
#Changes based on whether someone selects output
if(slidertype$type == "1 Week"){
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID == max(get_id)]),
force_edges = TRUE,
width = "1200px")
} else if(slidertype$type == "Multiple") {
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID %in% get_id]),
force_edges = TRUE,
width = "1200px")
} else{
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID == max(get_id)]),
force_edges = TRUE, width = "1200px")
}
})
#Create reactive df - requires different filtering
df_react <- reactive({
if(slidertype$type != "Multiple"){
test_data%>%
filter(Expected.Substance == input$Drug & Week.val %in% input$Change & City.Town == input$City)
} else if (slidertype$type == "Multiple") {
test_data %>%
filter(Expected.Substance == input$Drug & Week.val <= input$Change[2] &
Week.val >=input$Change[1] & City.Town == input$City)
}
})
observeEvent(input$City,{
poss_e <- poss.w[poss.w$Days2 <= max(test_data$Week.val[test_data$City.Town == input$City]) & poss.w$Days2 >= min(test_data$Week.val[test_data$City.Town == input$City]),]
if(slidertype$type != "Multiple"){
new_id <- max(poss_e$ID)
updateSliderTextInput(session,inputId = "Change", choices = as.character(poss_e$Days2), selected = as.character(poss_e$Days2[poss_e$ID == max(get_id)]))
} else {
new_id <- c(max(poss_e$ID)-1, max(poss_e$ID))
updateSliderTextInput(session,inputId = "Change", choices = as.character(poss_e$Days2), selected = as.character(poss_e$Days2[poss_e$ID %in% get_id]))
}
})
df_react2 <- reactive({
if(!is.null(input$regroup)){
df_react() %>%
dplyr::rename(Names = value) %>%
left_join(node_col[,c(2:3)]) %>%
mutate(Classification2 = ifelse(Classification %in% input$regroup, Classification, Names)) %>%
rename(value = Classification2)
} else {
df_react()
}
})
#Nodes for the Social Network Visualization
nodes <- reactive({
node <- df_react2() %>%
select(value) %>%
count(value) %>%
dplyr::rename(Names = value, Weight = n) %>%
left_join(node_col) %>%
select(ID, Names, Weight, Classification) %>%
arrange(desc(Weight))
node$Weight[grepl("No Cuts", node$Names)] <- node$Weight[grepl("No Cuts", node$Names)]/2
return(node)
})
#Edges for SN
#The nesting solution was a huge help from a user on Whosebug
#This code doesn't work without it:
edges2 <- reactive({
if(nrow(df_react2()) != 0){
df_react2() %>%
select(ID, value) %>%
nest(data=(value)) %>%
mutate(pairs=map(data, ~as_tibble(t(combn(.$value, 2))), .name_repair=T, .keep)) %>%
unnest(pairs) %>%
select(V1, V2) %>%
group_by(V1, V2) %>%
summarise(amount = n()) %>%
ungroup()
} else {
df_react2()
}
})
the_layout <- reactive({
edges <- edges2()
validate(
need(nrow(edges) >0.9, "Not tested During this Time")
)
colnames(edges) <- c("to", "from", "weight")
edges$from <- nodes()$ID[match(edges$from, nodes()$Names)]
edges$to <- nodes()$ID[match(edges$to, nodes()$Names)]
edges <- select(edges, from, to, weight)
g <- graph_from_data_frame(d = edges, vertices = nodes(), directed = FALSE)
g <- simplify(g, remove.loops = TRUE)
if(input$Drug %in% c(V(g)$Names, "Fentanyl/Down", "All Opioids (Grouped)") &
nrow(edges) >=10){
#Checks if there is just one graph or several
if(is.connected(g) == FALSE){
#if true then, it splits the main graph from the subgraphs
c <- clusters(g); cn <- cbind(V(g), c$membership)
lc <- which(which.max(c$csize)==c$membership);
gs <- induced.subgraph(g, lc)
if(input$Drug == "All Opioids (Grouped)"|input$Drug == "Fentanyl/Down"){
st1 <- layout_as_star(gs, center = V(gs)$Names == "Fentanyl or Analog")
}else{
st1 <- layout_as_star(gs, center = V(gs)$Names == input$Drug)
}
st1 <- norm_coords(st1, xmin = -0.6, xmax = 0.6,
ymin = -0.6, ymax = +0.6,
zmin = -0.6, zmax = +0.6)
#Normalize even and odd rows at different min & max to stagger nodes
st1[seq(2, nrow(st1),2),] <- norm_coords(st1[seq(2, nrow(st1),2),],
xmin = -0.45, xmax = 0.45,
ymin = -0.45, ymax = +0.45,
zmin = -0.45, zmax = +0.45)
lc2 <- which(!which.max(c$csize)==c$membership)
gs2 <- induced.subgraph(g, lc2)
circ <- layout_in_circle(gs2)
circ <- norm_coords(circ, xmin = -0.8, xmax = 0.8,
ymin = -0.8, ymax = +0.8,
zmin = -0.8, zmax = +0.8)
test2 <- rbind(st1,circ)
g <- gs %du% gs2
t_lay <- create_layout(g, test2)
}else{
st1 <- layout_as_star(g, center = V(g)$Names == input$Drug)
st1 <- norm_coords(st1, xmin = -0.8, xmax = 0.8,
ymin = -0.8, ymax = +0.8,
zmin = -0.8, zmax = +0.8)
t_lay <- create_layout(g, st1)
}
#For every other drug sample - still WIP
} else {
t_lay <- create_layout(g, layout = "nicely")
}
})
#Set graph space limits
output$net <- renderPlot({
t_lay <- the_layout()
x_max <- max(t_lay$x)+0.1
x_min <- min(t_lay$x)-0.1
y_min <- min(t_lay$y)-0.1
y_max <- max(t_lay$y)+0.1
par(mar = c(0, 0, 0, 0))
ggraph(t_lay) +
geom_edge_link0(aes(width = E(g)$weight), colour = "grey") + # add edges to the plot
scale_edge_width_continuous(breaks = c(1, 5, 10, 25, 50,100),
label = c(1, 5, 10, 25, 50, 100),
range = c(1,20), name = "Frequency Found Together",
limits = c(0,400),
guide = guide_legend(order = 2,
nrow = 1,
ncol =7)) +
geom_node_point(aes(size = V(g)$Weight, color = V(g)$Classification)) +
coord_cartesian(ylim = c(y_min, y_max), xlim = c(x_min, x_max)) +
geom_node_text(aes(label = V(g)$Names), angle = 30, size = 5) +
scale_size(breaks = c(1,10,20,40, 60,80, 100), label=scales::number,
range = c(1,60), limits = c(1,400), name = "# of Times Drug Found \n in Test Results",
guide = guide_legend(order = 1,
nrow = 4,
ncol = 2,
label.hjust =0.5)) +
labs(caption = "Fent/Benzodiazepine < 5% means substance tested positive on test strip") +
theme(legend.position= "right",
legend.box.background = element_blank(),
legend.direction = "vertical",
legend.key = element_blank(),
legend.background = element_blank(),
legend.text = element_text(size=12, hjust = 0.4, inherit.blank = TRUE),
legend.box.just = "top",
legend.box = "vertical",
legend.justification = "right",
legend.box.spacing = unit(0.5,"cm"),
plot.caption = element_text(size = 14),
legend.title.align = 0.2,
legend.text.align = 0.4,
legend.title=element_text(size=14),
legend.key.width = unit(0.5, "cm"),
legend.key.height = unit(0.2, "cm"),
legend.spacing = unit(0.5, "cm"),
panel.background = element_blank(),
legend.box.margin = margin(t = 0, r = 0, b = 0, l = 0, unit = "cm"),
legend.margin = margin(0,0, 0, 0, unit = "cm"))+
guides(color = guide_legend(override.aes = list(size=10),
nrow = 5,
ncol = 4))
})
#I would like to do something like this
output$info <- renderPrint({
brushedPoints(the_layout(), input$plot_brush, allRows = TRUE)})
}
shinyApp(ui = ui, server = server)```
我通过将 g
的分配从 <-
更改为 <<-
来让闪亮的应用程序运行。下面是修改后的代码。
urlfile <- "https://raw.githubusercontent.com/alexbetsos/Whosebughelp/main/test_data.csv"
test_data <- read_csv(url(urlfile))
test_data <- test_data[,-c(1)]
poss.w <- data.frame(ID = c(80,81,82,83),
Days2 = c("Jun 28-\nJul 4\n2021",
"Jul 5-\nJul 11\n2021",
"Jul 12-\nJul 18\n2021",
"Jul 19-\nJul 25\n2021"))
get_id <- c(max(poss.w$ID)-1, max(poss.w$ID))
interest <- c("Fentanyl/Down", "Opioids Minus Fentanyl (Grouped)", "All Opioids (Grouped)", "Methamphetamine",
"Ketamine", "Cocaine", "Crack Cocaine", "MDMA")
test_data <- test_data[test_data$Expected.Substance %in% interest,]
###Creates df for classification and the colour palette####
node_col <- structure(list(ID = 1:36,
Names = c("Caffeine", "Erythritol",
"Uncertain Match", "Fent <5%", "Fentanyl or Analog", "Xylitol",
"Benzodiazepine <5%", "Mannitol", "Uncertain Oil/Carb/Sugar",
"Dimethyl Sulfone", "Soap", "Water", "Methamphetamine", "Acetaminophen",
"para-Fluorofentanyl", "No Cuts\nFentanyl or Analog", "No Cuts\nUncertain Match",
"Propionanilide", "MDMA", "Safrole", "Sucrose", "Phenacetin",
"4-ANPP", "Lactose", "Inositol", "Creatine", "Etizolam",
"No Cuts\nTrichloroisocyanuric Acid",
"Naproxen", "Heroin", "PEG", "Diphenhydramine", "Cocaine", "Glutamine",
"Benzocaine", "Sorbitol"),
Classification = c("Stimulant", "Buff",
"Other or NA", "Opioid", "Opioid", "Buff", "Benzodiazepine",
"Buff", "Other or NA", "Buff", "new_val", "Other or NA", "Stimulant",
"Buff", "Opioid", "Opioid", "Other or NA", "Buff", "Stimulant",
"Other or NA", "Buff", "Buff", "Precursor", "Buff", "Buff", "Other or NA",
"Benzodiazepine", "new_val", "new_val", "Opioid", "Buff", "Other or NA",
"Stimulant", "Buff", "Buff", "Buff")), row.names = c(NA, -36L
), class = "data.frame")
regrouped <- data.frame(ID = seq(2000, 1999+length(unique(node_col$Classification)),by=1),
Names = unique(unique(node_col$Classification)),
Classification = unique(unique(node_col$Classification)))
node_col <- rbind(node_col, regrouped)
####---Libraries & Functions---####
library(tidyverse)
library(igraph)
library(ggraph)
library(tidygraph)
library(shiny)
library(shinyjs)
library(shinydashboard)
library(shinyWidgets)
library(shinythemes)
ui <- navbarPage(title = "BC Drug Checking Visualizations",theme = shinytheme("flatly"),
tabPanel("Instructions",
tabPanel("Drug Checking Data",
sidebarLayout(
sidebarPanel(width = 2,
selectInput("Drug",
"Expected Substance",
choices = interest,
selected = NULL),
selectInput("City",
"City",
choices = unique(test_data$City.Town),
selected = "Vancouver"),
radioButtons("duration",
label = "1 Week or Multiple",
choices = c("1 Week", "Multiple"),
selected = "1 Week"),
checkboxGroupInput("regroup",
label = "Regroup Variables",
choices = regrouped$Classification,
selected = NULL)
),
mainPanel(width = 9,
fluidRow(
uiOutput("myList")),
tabsetPanel(
tabPanel("Network Graph",
fluidRow(tabstyle='padding:0px',
box(width = 12,
offset = 0,
plotlOutput("net", width = "100%",
height = "750px",
click = "plot_click",
brush = "plot_brush"))),
fluidRow(verbatimTextOutput("info"))
#Need to add the bar chart & Table back in
)
)
)))))
server <- function(input, output, session) {
#Create reactive value to hold slider info
slidertype <- reactiveValues()
slidertype$type <- "default"
observeEvent(input$duration, {
#When person changes from 1 week to multiple it will change slider
if(input$duration == "1 Week"){
slidertype$type <- "1 Week"
} else if(input$duration == "Multiple"){
slidertype$type <- "Multiple"
} else {
slidertype$type <- "default"
}
})
#Renders the UI for the slider
output$myList <- renderUI({
#Changes based on whether someone selects output
if(slidertype$type == "1 Week"){
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID == max(get_id)]),
force_edges = TRUE,
width = "1200px")
} else if(slidertype$type == "Multiple") {
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID %in% get_id]),
force_edges = TRUE,
width = "1200px")
} else{
sliderTextInput("Change",
label = NULL,
choices = as.character(poss.w$Days2),
selected = as.character(poss.w$Days2[poss.w$ID == max(get_id)]),
force_edges = TRUE, width = "1200px")
}
})
#Create reactive df - requires different filtering
df_react <- reactive({
if(slidertype$type != "Multiple"){
test_data %>%
filter(Expected.Substance == input$Drug & Week.val %in% input$Change & City.Town == input$City)
} else if (slidertype$type == "Multiple") {
test_data %>%
filter(Expected.Substance == input$Drug & Week.val <= input$Change[2] &
Week.val >=input$Change[1] & City.Town == input$City)
}
})
observeEvent(input$City,{
poss_e <- poss.w[poss.w$Days2 <= max(test_data$Week.val[test_data$City.Town == input$City]) & poss.w$Days2 >= min(test_data$Week.val[test_data$City.Town == input$City]),]
if(slidertype$type != "Multiple"){
new_id <- max(poss_e$ID)
updateSliderTextInput(session,inputId = "Change", choices = as.character(poss_e$Days2), selected = as.character(poss_e$Days2[poss_e$ID == max(get_id)]))
} else {
new_id <- c(max(poss_e$ID)-1, max(poss_e$ID))
updateSliderTextInput(session,inputId = "Change", choices = as.character(poss_e$Days2), selected = as.character(poss_e$Days2[poss_e$ID %in% get_id]))
}
})
df_react2 <- reactive({
if(!is.null(input$regroup)){
df_react() %>%
dplyr::rename(Names = value) %>%
left_join(node_col[,c(2:3)]) %>%
mutate(Classification2 = ifelse(Classification %in% input$regroup, Classification, Names)) %>%
rename(value = Classification2)
} else {
df_react()
}
})
#Nodes for the Social Network Visualization
nodes <- reactive({
node <- df_react2() %>%
select(value) %>%
count(value) %>%
dplyr::rename(Names = value, Weight = n) %>%
left_join(node_col) %>%
select(ID, Names, Weight, Classification) %>%
arrange(desc(Weight))
node$Weight[grepl("No Cuts", node$Names)] <- node$Weight[grepl("No Cuts", node$Names)]/2
return(node)
})
#Edges for SN
#The nesting solution was a huge help from a user on Whosebug
#This code doesn't work without it:
edges2 <- reactive({
if(nrow(df_react2()) != 0){
df_react2() %>%
select(ID, value) %>%
nest(data=(value)) %>%
mutate(pairs=map(data, ~as_tibble(t(combn(.$value, 2))), .name_repair=T, .keep)) %>%
unnest(pairs) %>%
select(V1, V2) %>%
group_by(V1, V2) %>%
summarise(amount = n()) %>%
ungroup()
} else {
df_react2()
}
})
the_layout <- reactive({
edges <- edges2()
validate(
need(nrow(edges) >0.9, "Not tested During this Time")
)
colnames(edges) <- c("to", "from", "weight")
edges$from <- nodes()$ID[match(edges$from, nodes()$Names)]
edges$to <- nodes()$ID[match(edges$to, nodes()$Names)]
edges <- select(edges, from, to, weight)
g <<- graph_from_data_frame(d = edges, vertices = nodes(), directed = FALSE)
g <<- simplify(g, remove.loops = TRUE)
if(input$Drug %in% c(V(g)$Names, "Fentanyl/Down", "All Opioids (Grouped)") &
nrow(edges) >=10){
#Checks if there is just one graph or several
if(is.connected(g) == FALSE){
#if true then, it splits the main graph from the subgraphs
c <- clusters(g); cn <- cbind(V(g), c$membership)
lc <- which(which.max(c$csize)==c$membership);
gs <- induced.subgraph(g, lc)
if(input$Drug == "All Opioids (Grouped)"|input$Drug == "Fentanyl/Down"){
st1 <- layout_as_star(gs, center = V(gs)$Names == "Fentanyl or Analog")
}else{
st1 <- layout_as_star(gs, center = V(gs)$Names == input$Drug)
}
st1 <- norm_coords(st1, xmin = -0.6, xmax = 0.6,
ymin = -0.6, ymax = +0.6,
zmin = -0.6, zmax = +0.6)
#Normalize even and odd rows at different min & max to stagger nodes
st1[seq(2, nrow(st1),2),] <- norm_coords(st1[seq(2, nrow(st1),2),],
xmin = -0.45, xmax = 0.45,
ymin = -0.45, ymax = +0.45,
zmin = -0.45, zmax = +0.45)
lc2 <- which(!which.max(c$csize)==c$membership)
gs2 <- induced.subgraph(g, lc2)
circ <- layout_in_circle(gs2)
circ <- norm_coords(circ, xmin = -0.8, xmax = 0.8,
ymin = -0.8, ymax = +0.8,
zmin = -0.8, zmax = +0.8)
test2 <- rbind(st1,circ)
g <- gs %du% gs2
t_lay <- create_layout(g, test2)
}else{
st1 <- layout_as_star(g, center = V(g)$Names == input$Drug)
st1 <- norm_coords(st1, xmin = -0.8, xmax = 0.8,
ymin = -0.8, ymax = +0.8,
zmin = -0.8, zmax = +0.8)
t_lay <- create_layout(g, st1)
}
#For every other drug sample - still WIP
} else {
t_lay <- create_layout(g, layout = "nicely")
}
})
#Set graph space limits
output$net <- renderPlot({
t_lay <<- the_layout()
x_max <- max(t_lay$x)+0.1
x_min <- min(t_lay$x)-0.1
y_min <- min(t_lay$y)-0.1
y_max <- max(t_lay$y)+0.1
par(mar = c(0, 0, 0, 0))
ggraph(t_lay) +
geom_edge_link0(aes(width = E(g)$weight), colour = "grey") + # add edges to the plot
scale_edge_width_continuous(breaks = c(1, 5, 10, 25, 50,100),
label = c(1, 5, 10, 25, 50, 100),
range = c(1,20), name = "Frequency Found Together",
limits = c(0,400),
guide = guide_legend(order = 2,
nrow = 1,
ncol =7)) +
geom_node_point(aes(size = V(g)$Weight, color = V(g)$Classification)) +
coord_cartesian(ylim = c(y_min, y_max), xlim = c(x_min, x_max)) +
geom_node_text(aes(label = V(g)$Names), angle = 30, size = 5) +
scale_size(breaks = c(1,10,20,40, 60,80, 100), label=scales::number,
range = c(1,60), limits = c(1,400), name = "# of Times Drug Found \n in Test Results",
guide = guide_legend(order = 1,
nrow = 4,
ncol = 2,
label.hjust =0.5)) +
labs(caption = "Fent/Benzodiazepine < 5% means substance tested positive on test strip") +
theme(legend.position= "right",
legend.box.background = element_blank(),
legend.direction = "vertical",
legend.key = element_blank(),
legend.background = element_blank(),
legend.text = element_text(size=12, hjust = 0.4, inherit.blank = TRUE),
legend.box.just = "top",
legend.box = "vertical",
legend.justification = "right",
legend.box.spacing = unit(0.5,"cm"),
plot.caption = element_text(size = 14),
legend.title.align = 0.2,
legend.text.align = 0.4,
legend.title=element_text(size=14),
legend.key.width = unit(0.5, "cm"),
legend.key.height = unit(0.2, "cm"),
legend.spacing = unit(0.5, "cm"),
panel.background = element_blank(),
legend.box.margin = margin(t = 0, r = 0, b = 0, l = 0, unit = "cm"),
legend.margin = margin(0,0, 0, 0, unit = "cm"))+
guides(color = guide_legend(override.aes = list(size=10),
nrow = 5,
ncol = 4))
})
#I would like to do something like this
output$info <- renderPrint({
brushedPoints(the_layout(), input$plot_brush, allRows = TRUE)})
}
shinyApp(ui = ui, server = server)