I have some data as shown in the sample df below. There are data for 3 US states (CA, TX, NY) plus the Total, and for each of these there is an actual act, forecast fct, and predicted pred value.
I want to create a Shiny app in which the user can select which states to observe on the plot. To do this I'm using checkboxGroupInput. I have a reactive function named DF in the server portion of my code that subsets df based on user selected states. I then have a reactive function named gl that creates all the geom_line() statements I need to create a ggplot. The reason I'm doing this is because I want to keep the color the same for each state and use linetype to distinguish actual, forecast, or predicted for them.
Lastly, I try to create the plot, but this is where the problem begins. I get no error message, but no plot displays when I run the app. Is there a way to fix this, or a better way to accomplish what I want? Below is the code I have and a plot of what I would like to show if the user had selected all states and Total.
library(shiny)
library(ggplot2)
library(scales)
library(lubridate)
df <- data.frame(Date=seq.Date(as.Date('2017-01-01'), as.Date('2017-05-01'), by='month'),
CAact=rnorm(5, 10, 2), TXact=rnorm(5, 10, 2), NYact=rnorm(5, 10, 2),Totalact=rnorm(5, 30, 2),
CAfct=rnorm(5, 10, 2), TXfct=rnorm(5, 10, 2), NYfct=rnorm(5, 10, 2), Totalfct=rnorm(5, 30, 2),
CApred=rnorm(5, 10, 2), TXpred=rnorm(5, 10, 2), NYpred=rnorm(5, 10, 2), Totalpred=rnorm(5, 30, 2)
)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput('states', 'Select Regions',
choices=c('CA','TX','NY','Total'),
selected=c('CA','TX','NY','Total')
)
),
mainPanel(
plotOutput('portfolio')
)
)
)
server <- function(input, output){
#Function to subset df based on user selected states
DF <- reactive({
df <- df[,c('Date', names(df)[grep(paste(input$states, collapse='|'), names(df))])]
return(df)
})
#Generate all the 'geom_line' statements to create ggplot
gl <- reactive({
gl <- character()
lt <- 1
for(i in 2:length(DF())){
col <- substr(names(DF())[i], 1, 2)
if(grepl('Total', names(DF())[i])){
col <- 'Total'
}
if(grepl('fct', names(DF())[i])){
lt <- 5
} else if(grepl('pred', names(DF())[i])){
lt <- 4
}
line <- paste0("geom_line(aes(y=", names(DF())[i], ", color='", col, "'), linetype=", lt, ", size=1.25) + ")
gl <- paste0(gl, line)
}
})
#Create ggplot (not working)
output$portfolio <- renderPlot({
paste0("ggplot(data=DF(), aes(Date)) + ", gl(), "labs(x='', y='Balances ($B)')")
})
}
shinyApp(ui, server)
The error is occurring because of
paste0("ggplot(data=TP, aes(Date)) + ", gl, "labs(x='', y='Balances ($B)')")
You need to refer to gl as gl() since it is a reactive object. After fixing this, there is another error in
DF <- reactive({
df <- df[,c('Date', names(df)[grep(paste(input$states, collapse='|'), names(df))])]
return(tpreg)
})
As there is no tpreg object in that function. Changed it to return(df).
Then nothing displays in the plot. I wanted to help fix the rest but I've never seen anyone paste() together a plot so I'm not sure that works...
UPDATE:
Ok.
library(shiny)
library(ggplot2)
library(scales)
library(lubridate)
library(tidyr)
df <- data.frame(Date=seq.Date(as.Date('2017-01-01'), as.Date('2017-05-01'), by='month'),
CAact=rnorm(5, 10, 2), TXact=rnorm(5, 10, 2), NYact=rnorm(5, 10, 2),Totalact=rnorm(5, 30, 2),
CAfct=rnorm(5, 10, 2), TXfct=rnorm(5, 10, 2), NYfct=rnorm(5, 10, 2), Totalfct=rnorm(5, 30, 2),
CApred=rnorm(5, 10, 2), TXpred=rnorm(5, 10, 2), NYpred=rnorm(5, 10, 2), Totalpred=rnorm(5, 30, 2)
)
df <- gather(df, Variable, Value, -Date)
df$State <- gsub('act|fct|pred', '', df$Variable)
df$Variable <- gsub('CA|NY|TX|Total', '', df$Variable)
df$State <- factor(df$State, levels = c('CA','NY','TX','Total'))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput('states', 'Select Regions',
choices=c('CA','TX','NY','Total'),
selected=c('CA','TX','NY','Total')
)
),
mainPanel(
plotOutput('portfolio')
)
)
)
server <- function(input, output){
#Function to subset df based on user selected states
color.groups <- c(CA = 'green', TX = 'blue', NY = 'red', Total = 'black')
line.types <- c(pred = 1, act = 4, fct = 5)
#Generate all the 'geom_line' statements to create ggplot
#Create ggplot (not working)
output$portfolio <- renderPlot({
sub <- subset(df, subset = State %in% input$states)
ggplot(sub, aes(x = Date, y = Value, col = State))+
geom_line(aes(linetype = Variable))+
scale_color_manual(values = color.groups)+
scale_linetype_manual(values = line.types)
})
}
shinyApp(ui, server)
Related
In the below code, I render two separate plots using the autoplot() function. The first plot (transPlot1) allows the user to transform the data series via slider input, whereas the 2nd plot shows the original untransformed series (transPlot2) and is completely static. I'd like to show the two data series in the same plot, with the first series shown on the primary x-axis (on the left, and its x-axis values vary depending on the slider input) and the 2nd series shown on the secondary x-axis (on the right, and it always remains fixed as this shows the original data before transformation). I'd like to place the two series on the same plot so the user can see the effect on the transformation on the shape of the data. This is the sort of thing that was easy to do in XLS but I have migrated to R.
Any recommendations for how to do this?
Code:
library(feasts)
library(fabletools)
library(ggplot2)
library(shiny)
library(tsibble)
DF <- data.frame(
Month = c(1:12),
StateX = c(59,77,45,42,32,26,27,21,19,22,24,10)
)
DF1 <- DF %>% as_tsibble(index = Month)
library(shiny)
ui <- fluidPage(
sliderInput("lambda","Transformation lambda:",min=-2,max=2,value=0.5,step = 0.1),
plotOutput("transPlot1"),
plotOutput("transPlot2"),
)
server <- function(input, output) {
output$transPlot1 <- renderPlot({
DF1 %>%
autoplot(box_cox(StateX, input$lambda)) +
labs(y = "",
title = latex2exp::TeX(paste0(
"Transformed units reaching StateX",
round(input$lambda,2))))
})
output$transPlot2 <- renderPlot({
autoplot(DF1, StateX) +
labs(y = "")
})
}
shinyApp(ui, server)
One option to achieve your desired result would be to add your untransformed series to the autoplot via a geom_line and to add a secondary scale. As usual when adding a secondary scale to a ggplot this requires some transformation of the data to be displayed on the secondary axis. To this end I added a reactive to do all the transformations, including the Box-Cox transformation and the transformation needed for the secondary scale. For the last part I use scales::rescale.
library(feasts)
#> Loading required package: fabletools
library(fabletools)
library(ggplot2)
library(shiny)
library(tsibble)
library(dplyr)
library(scales)
DF <- data.frame(
Month = c(1:12),
StateX = c(59, 77, 45, 42, 32, 26, 27, 21, 19, 22, 24, 10)
)
DF1 <- DF %>% as_tsibble(index = Month)
library(shiny)
ui <- fluidPage(
sliderInput("lambda", "Transformation lambda:", min = -2, max = 2, value = 0.5, step = 0.1),
plotOutput("transPlot1")
)
server <- function(input, output) {
DF1_trans <- reactive({
DF1 %>%
mutate(
state_x_box = box_cox(StateX, input$lambda),
state_x_raw = scales::rescale(StateX, to = range(state_x_box))
)
})
output$transPlot1 <- renderPlot({
to_range <- range(DF1_trans()$StateX)
DF1_trans() %>%
autoplot(state_x_box) +
geom_line(data = DF1_trans(), aes(Month, state_x_raw, color = "Untransformed Series")) +
scale_y_continuous(
sec.axis = sec_axis(
name = "Untransformed",
trans = ~ scales::rescale(.x, to = to_range))) +
labs(
y = "",
title = latex2exp::TeX(paste0(
"Transformed units reaching StateX",
round(input$lambda, 2)
))
)
})
}
shinyApp(ui, server)
#>
#> Listening on http://127.0.0.1:3492
I am creating two interactive plots in R Shiny and while I can get one plot to show up and work, the second plot keeps giving me the "Warning: Error in [.data.frame: undefined columns selected" and will not appear.
I have looked at many solutions online and none so far have been able to help me or fix my issue.
I am having a hard time seeing how my columns are undefined, but I am also relatively new to R Shiny and could be easily overlooking something, so I was hoping someone could help me figure this out.
Here is my code:
library(shiny)
library(dplyr)
library(readr)
library(ggplot2)
library(tidyverse)
age <- c(1, 4, 7,10, 15)
v_m_1 <- c(10, 14, 17, 20, 25)
v_m_2 <- c(9, 13, 16, 19, 24)
sex <- c("F", "M","U", "F", "M")
P_v_rn <- c(0.11, 0.51, 0.61, 0.91, 1)
C_v_rn <- c(11.1, 15.1, 16.1, 19.1, 20.1)
P_v_rk <- c(0.11, 0.51, 0.61, 0.91, 1)
B_v_rk <- c("Low", "Medium", "Medium", "High", "High")
df_test <- data.frame(age, v_m_1, v_m_2, sex, P_v_rn, C_v_rn, P_v_rk, B_v_rk)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Test"),
# Sidebar with a slider input for number of bins
verticalLayout(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "xvar",
label = "Choose X variable", #All variables are numeric
c("Age" = 1),
selected = 1),
selectInput(inputId = "yvar",
label = "Choose bone variable", #All variables are numeric
c("v_m_1" = 2,
"v_m_2" = 3),
selected = 2),
checkboxInput(inputId = "regression",
label = "Fit LOESS - By Sex",
value = FALSE)),
mainPanel(
plotOutput('dataplot1')
)
),
tags$hr(),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "xvar_name",
label = "Choose X variable", #All variables are numeric
c("Age" = 1),
selected = 1),
selectInput(inputId = "yvar_name",
label = "Choose Y variable", #The first variable option is numeric, the rest are factors
c("P_v_rk" = 7,
"B_v_rk" = 8),
selected = 7),
selectInput(inputId = "zvar_name",
label = "Choose Z variable", #All variables are numeric
c("C_v_rn" = 6,
"P_v_rn" = 5),
selected = 6)),
# Show a plot of the generated distribution
mainPanel(
plotOutput('dataplot2')
)
),
tags$hr(),
))
# Define server logic required to draw a scatterplot
server <- function(input, output) {
df <- df_test %>%
select(age, v_m_1, v_m_2, sex, P_v_rn, C_v_rn, P_v_rk, B_v_rk)
df$B_v_rk <- as.factor(df$B_v_rk)
#Growth Curve
output$dataplot1 <- renderPlot({
xvar <- as.numeric(input$xvar)
yvar <- as.numeric(input$yvar)
Sex <- as.factor(df$sex)
p <- ggplot() +
aes(x = df[ ,xvar],
y = df[ ,yvar],
col = sex) +
geom_point(alpha = 0.5, aes(size = 1.5)) + # 50% transparent
labs(x = names(df[xvar]),
y = names(df[yvar])) +
theme_classic()
if(input$regression) {
# add a line to the plot
p <- p + geom_smooth()
}
p # The plot ('p') is the "return value" of the renderPlot function
})
#Environmental metrics
output$dataplot2 <- renderPlot({
xvar_name <- input$xvar_name
yvar_name <- input$yvar_name
zvar_name <- input$zvar_name
#Color palette for ggplots as blue color range was difficult for me
fun_color_range <- colorRampPalette(c("yellow", "red"))
my_colors <- fun_color_range(20)
p2 <- ggplot() +
aes(x = df[ ,xvar_name],
y = df[ ,yvar_name],
col = df[ ,zvar_name]) +
geom_point(alpha = 0.5, aes(size = 1.5)) + # 50% transparent
scale_colour_gradientn(colors = my_colors) +
labs(x = names(df[xvar_name]),
y = names(df[yvar_name])) +
theme_classic()
p2 # The plot ('p2') is the "return value" of the renderPlot function
})
}
# Run the application
shinyApp(ui = ui, server = server)
Again the first plot works fine, it is the second plot that is producing an error code.
I guess I am confused as the code for the first plot works fine but it won't work for the second plot.
For reference, this is the layout I want, except I want another plot in the error code location.
My guess is that the bug is in the line with names(df[xvar_name]). If df is a data frame, this will throw the error you quoted. To subset a data frame with indices or column names you either use double brackets (df[[...]]) or a comma (df[ ..., ... ]). I think you meant names(df[ , xvar_name ]). This error is repeated on the line below as well.
In general, to identify the place where the problem occurs, use browser() in your code.
I am trying to use ggplot2 with a reactive object in shiny.
So, I understand that reactive gives a reactive object that needs to be changed to be a value, but I tried to assign it to a different object both in the reactive part and in the renderPlot part and got no results...
So, if I don't assign it to a different variable, I get the message:
ggplot2 doesn't know how to deal with data of class
reactiveExpr/reactive
I know there are at least 2 questions (here and here) that talk about this problem. I did do my homework and try to use those answers and it didn't work. As far as I can tell, those answers suggest to assign the reactive object to another variable, which I did, and got the message:
Error: object is not a matrix
The answers delineated there do not really explain how to fix the problem in general, they just provide the code to fix that particular example (one answer reads " I think this particular problem should be solved with the following code:" but doesn't point out what part of the code solves the issue).
Can you please explain, how to fix this in this example in the more general case?
here is a Minimal example:
server.R
library(shiny)
shinyServer(function(input, output) {
library(ggplot2)
library(lme4)
model1 <- lmList((conc) ~ time | Subject, data = Indometh)
newpoints <- reactive({data.frame("Subject" = c(1, 4, 2, 5, 6, 3),
"conc" = predict(model1, newdata = data.frame(time=input$sliderTime)),
"time" = rep(input$sliderTime, 6))
})
output$myPlot <- renderPlot({
newpoints2 <- newpoints()
g <- ggplot(Indometh, aes(time, (conc), color= Subject)) + geom_point() +
stat_smooth(method="lm",fullrange=TRUE, fill=NA)
newg <- g + geom_point(data = newpoints2, mapping =
aes(x = time, y = (conc), color= factor(Subject)))
print(newg)
})
})
and the ui.R file:
library(shiny)
shinyUI(fluidPage(
# Application title
titlePanel("Concentration of Indomethacin"),
sidebarLayout(
sidebarPanel(
sliderInput("slidertime",
"Time:",
min = 8,
max = 15,
value = 8)),
mainPanel(
plotOutput("myPlot")
)
)
))
The problem with your example is that you are calling the input with the wrong name it is not sliderTime it is slidertime i change the server to this and it works perfect.
How I fix it? just use browser() inside the reactive expression and found that input$sliderTime was NULL so I check the name.
library(shiny)
shinyServer(function(input, output) {
library(ggplot2)
library(lme4)
model1 <- lmList((conc) ~ time | Subject, data = Indometh)
newpoints <- reactive({
data.frame("Subject" = c(1, 4, 2, 5, 6, 3),
"conc" = predict(model1, newdata = data.frame(time=input$slidertime)),
"time" = rep(input$slidertime, 6))
})
output$myPlot <- renderPlot({
newpoints2 <- newpoints()
g <- ggplot(Indometh, aes(time, (conc), color= Subject)) + geom_point() +
stat_smooth(method="lm",fullrange=TRUE, fill=NA)
newg <- g + geom_point(data = newpoints2, mapping =
aes(x = time, y = (conc), color= factor(Subject)))
print(newg)
})
})
I am trying to create a Shiny app to explore a data frame with 4 variables/columns (A, B, C, D) and 10,000 rows. There is an input field where users must select 2 of the 4 variables/columns. Once they have done so, then a scatterplot is shown on the right. The scatterplot is a Plotly object with hexagon binning summarizing the values of the 10,000 rows between the two user-selected variables/columns.
At this point, the user can select a "Go!" button, which causes an orange dot corresponding to the first row of those 2 variables/columns to be superimposed onto the Plotly object. The user can sequentially select "Go!" and then the orange dot corresponding to the second, third, fourth, etc. row will be superimposed onto the Plotly object. The name of the row ID is output above the scatterplot matrix.
For the most part, the app is working. There are only 2 things I am trying to improve upon:
1) I would like the user to be able to select new pairs in the input field. This works for the most part. However, there is one specific situation where this will cause the app to close suddenly. It happens after an orange point has been overlaid onto the scatterplot. If the user then erases the two input pairs, the app suddenly closes. I would like the user to be able to erase both input pair values and input two new pair values without the app closing even after orange points have been plotted to the scatterplot.
2) I notice that the output of the row ID lags somewhat after the orange dot is plotted. I wonder why this happens since I output the row ID before plotting the orange dot in the script. I would prefer for there to be less of a lag, but am uncertain how to approach that.
Any suggestions on how to solve either of these two issues would be greatly appreciated! My MWE showing this issue is below.
library(plotly)
library(GGally)
library(hexbin)
library(htmlwidgets)
library(tidyr)
library(shiny)
library(dplyr)
library(data.table)
library(ggplot2)
library(tibble)
myPairs <- c("A", "B", "C", "D")
ui <- shinyUI(fluidPage(
titlePanel("title panel"),
sidebarLayout(position = "left",
sidebarPanel(
selectizeInput("selPair", "Pairs:", choices = myPairs, multiple = TRUE, options = list(maxItems = 2)),
actionButton("goButton", "Go!"),
width = 3
),
mainPanel(
verbatimTextOutput("info"),
plotlyOutput("scatMatPlot")
)
)
))
server <- shinyServer(function(input, output, session) {
# Create data and subsets of data based on user selection of pairs
dat <- data.frame(ID = paste0("ID", 1:10000), A = rnorm(10000), B = rnorm(10000), C = rnorm(10000), D = rnorm(10000))
pairNum <- reactive(input$selPair)
group1 <- reactive(pairNum()[1])
group2 <- reactive(pairNum()[2])
sampleIndex <- reactive(which(colnames(dat) %in% c(group1(), group2())))
# Create data subset based on two letters user chooses
datSel <- eventReactive(sampleIndex(), {
datSel <- dat[, c(1, sampleIndex())]
datSel$ID <- as.character(datSel$ID)
datSel <- as.data.frame(datSel)
datSel
})
sampleIndex1 <- reactive(which(colnames(datSel()) %in% c(group1())))
sampleIndex2 <- reactive(which(colnames(datSel()) %in% c(group2())))
# Create background Plotly graph with hex binning all 100 rows of the two user-selected columns
ggPS <- eventReactive(datSel(), {
minVal = min(datSel()[,-1])
maxVal = max(datSel()[,-1])
maxRange = c(minVal, maxVal)
xbins=7
buffer = (maxRange[2]-maxRange[1])/xbins/2
x = unlist(datSel()[,(sampleIndex1())])
y = unlist(datSel()[,(sampleIndex2())])
h <- hexbin(x=x, y=y, xbins=xbins, shape=1, IDs=TRUE, xbnds=maxRange, ybnds=maxRange)
hexdf <- data.frame (hcell2xy (h), hexID = h#cell, counts = h#count)
attr(hexdf, "cID") <- h#cID
p <- ggplot(hexdf, aes(x=x, y=y, fill = counts, hexID=hexID)) + geom_hex(stat="identity") + geom_abline(intercept = 0, color = "red", size = 0.25) + coord_cartesian(xlim = c(maxRange[1]-1*buffer, maxRange[2]+buffer), ylim = c(maxRange[1]-1*buffer, maxRange[2]+buffer)) + coord_equal(ratio=1) + labs(x = colnames(datSel()[sampleIndex1()]), y = colnames(datSel()[sampleIndex2()]))
ggPS <- ggplotly(p)
ggPS})
# Output hex bin plot created just above
output$scatMatPlot <- renderPlotly({
# Each time user pushes Go! button, the next row of the data frame is selected
datInput <- eventReactive(input$goButton, {
g <- datSel()$ID[input$goButton]
# Output ID of selected row
output$info <- renderPrint({
g
})
# Get x and y values of seleced row
currGene <- datSel()[which(datSel()$ID==g),]
currGene1 <- unname(unlist(currGene[,sampleIndex1()]))
currGene2 <- unname(unlist(currGene[,sampleIndex2()]))
c(currGene1, currGene2)
})
# Send x and y values of selected row into onRender() function
observe({
session$sendCustomMessage(type = "points", datInput())
})
# Use onRender() function to draw x and y values of seleced row as orange point
ggPS() %>% onRender("
function(el, x, data) {
noPoint = x.data.length;
Shiny.addCustomMessageHandler('points', function(drawPoints) {
if (x.data.length > noPoint){
Plotly.deleteTraces(el.id, x.data.length-1);
}
var Traces = [];
var trace = {
x: drawPoints.slice(0, drawPoints.length/2),
y: drawPoints.slice(drawPoints.length/2, drawPoints.length),
mode: 'markers',
marker: {
color: 'orange',
size: 7
},
hoverinfo: 'none'
};
Traces.push(trace);
Plotly.addTraces(el.id, Traces);
});}")
})
})
shinyApp(ui, server)
As #HubertL mentioned, it's better to avoid nesting reactive functions. Your app will probably run more smoothely if you change that.
About your first problem, req and validate are probably the best way to go. These functions check if the user inputs are valid and deal with the invalid ones.
I've adjusted your code a bit following these sugetions, but you still can change it more. If you take a closer look to ggPS you may notice that it only uses datSel() so you could turn it into a function.
library(plotly)
library(GGally)
library(hexbin)
library(htmlwidgets)
library(tidyr)
library(shiny)
library(dplyr)
library(data.table)
library(ggplot2)
library(tibble)
myPairs <- c("A", "B", "C", "D")
ui <- shinyUI(fluidPage(
titlePanel("title panel"),
sidebarLayout(
position = "left",
sidebarPanel(
selectizeInput("selPair", "Pairs:", choices = myPairs, multiple = TRUE,
options = list(maxItems = 2)),
actionButton("goButton", "Go!"),
width = 3
),
mainPanel(
verbatimTextOutput("info"),
plotlyOutput("scatMatPlot")
)
)
))
server <- shinyServer(function(input, output, session) {
# Create data and subsets of data based on user selection of pairs
dat <- data.frame(
ID = paste0("ID", 1:10000), A = rnorm(10000),
B = rnorm(10000), C = rnorm(10000), D = rnorm(10000),
stringsAsFactors = FALSE
)
# Create data subset based on two letters user chooses
datSel <- eventReactive(input$selPair, {
validate(need(length(input$selPair) == 2, "Select a pair."))
dat[c("ID", input$selPair)]
}, ignoreNULL = FALSE)
# Create background Plotly graph with hex binning all 100 rows of the two user-selected columns
ggPS <- eventReactive(datSel(), {
minVal = min(datSel()[,-1])
maxVal = max(datSel()[,-1])
maxRange = c(minVal, maxVal)
xbins=7
buffer = (maxRange[2]-maxRange[1])/xbins/2
x = unlist(datSel()[input$selPair[1]])
y = unlist(datSel()[input$selPair[2]])
h <- hexbin(x=x, y=y, xbins=xbins, shape=1, IDs=TRUE,
xbnds=maxRange, ybnds=maxRange)
hexdf <- data.frame (hcell2xy (h), hexID = h#cell, counts = h#count)
attr(hexdf, "cID") <- h#cID
p <- ggplot(hexdf, aes(x=x, y=y, fill = counts, hexID=hexID)) +
geom_hex(stat="identity") + geom_abline(intercept = 0, color = "red", size = 0.25) +
coord_cartesian(xlim = c(maxRange[1]-1*buffer, maxRange[2]+buffer),
ylim = c(maxRange[1]-1*buffer, maxRange[2]+buffer)) +
coord_equal(ratio = 1) +
labs(x = input$selPair[1], y = input$selPair[2])
ggPS <- ggplotly(p)
ggPS
})
# Output ID of selected row
output$info <- renderPrint({ datSel()$ID[req(input$goButton)] })
# Output hex bin plot created just above
output$scatMatPlot <- renderPlotly({
# Use onRender() function to draw x and y values of seleced row as orange point
ggPS() %>% onRender("
function(el, x, data) {
noPoint = x.data.length;
Shiny.addCustomMessageHandler('points', function(drawPoints) {
if (x.data.length > noPoint){
Plotly.deleteTraces(el.id, x.data.length-1);
}
var Traces = [];
var trace = {
x: drawPoints.slice(0, drawPoints.length/2),
y: drawPoints.slice(drawPoints.length/2, drawPoints.length),
mode: 'markers',
marker: {
color: 'orange',
size: 7
},
hoverinfo: 'none'
};
Traces.push(trace);
Plotly.addTraces(el.id, Traces);
});}")
})
observe({
# Get x and y values of seleced row
currGene <- datSel()[input$goButton, -1]
# Send x and y values of selected row into onRender() function
session$sendCustomMessage(type = "points", unname(unlist(currGene)))
})
})
shinyApp(ui, server)
I have the following simplified example of a Shiny app using plotly graphic.
# Function, library, data
PlotResponseRate <- function(EntryData)
{
PlotData <- as.data.frame(apply(X = EntryData, MARGIN = 2,
function(x) round(length(which(!is.na(x)))/length(x)*100)))
colnames(PlotData) <- "TheData"
PlotData$TheLabel <- factor(str_wrap(colnames(EntryData), width = 30),
levels = unique(str_wrap(colnames(EntryData), width = 30)))
PlotData$TheLabel <- gsub(pattern = "\n", replacement = "<br>", PlotData$TheLabel)
Graphe <- ggplot(data = PlotData, aes(x = TheLabel, y = TheData)) +
geom_bar(stat = "identity", fill = "red", width = 0.8) +
coord_flip() +
labs(title = "Response rate")
}
library(stringr)
library(ggplot2)
library(plotly)
a <- c(1, 2, 2, 2, NA, 1, 2, 2, 1)
b <- c(2, 1, 2, NA, 2, NA, 1, NA, 1)
df <- data.frame(a, b)
colnames(df) <- c("This Is A Long Answer To A Long Question Label For The First Question",
"This Is A Long Answer To A Long Question Label For The Second Question")
# The Shiny app
Interface <-
{
fluidPage(
sliderInput(inputId = "Num",
label = "Choose the questions",
value = c(1:2), min = 1, max = 2, step = 1),
plotlyOutput("Myplot")
)
}
Serveur <- function(input, output)
{
output$Myplot <- renderPlotly({
Plot1 <- PlotResponseRate(EntryData = df[c(input$Num[1]:input$Num[2])])
ggplotly(Plot1)
})
}
shinyApp(ui = Interface, server = Serveur)
The main feature I want is modifying the structure of the plot. Therefore, I add this line of code in renderPlotly after the conversion in a plotly graphic.
ggplotly(Plot1)
Plot1$x$layout$margin$l <- 180
Or when I add this line, I have an error "no applicable method for 'ggplotly' applied to an object of class ”c('double', 'numeric')" and despite efforts I cannot debug. Any idea ?
I precise that it works fine in R command line :
Handle long labels in plotly
According to the comments above, the correct code is the following.
Serveur <- function(input, output)
{
output$Myplot <- renderPlotly({
Plot1 <- PlotResponseRate(EntryData = df[c(input$Num[1]:input$Num[2])])
Plot1 <- plotly_build(Plot1)
Plot1$x$layout$margin$l <- 180
Plot1 <- ggplotly(Plot1)
})
}