R Shiny plot for time series data, ggplot, producing NAs only - r

I have problems with my ggplot in Shiny. I am new to Shiny, so there are probably some rookie mistakes in the code. But I receive the following warnings:
Listening on http://127.0.0.1:4278`
Warning: Removed 93 rows containing non-finite values (stat_smooth).
Warning: Removed 93 rows containing missing values (geom_point).
Warning: Removed 1 rows containing missing values (geom_text).
The R code:
library(shiny)
library(ggplot2)
ggplot_df <- data.frame("start_ts"=c(1555279200,1555280100,1555281000,1555281900,1555282800),
"V1"=c(6.857970e-04,7.144347e-05,1.398045e-06,2.997632e-05,2.035446e-06),
"sum"=c(20,21,22,15,23))
# Small test data set with 5 observations... 93 in original one
# Define UI for application
ui <- fluidPage(sliderInput("time", "Time:",
min = as.POSIXct("00:00",format="%H:%M", tz=""),
max = as.POSIXct("24:00",format="%H:%M", tz=""),
value = c(
as.POSIXct("00:00",format="%H:%M")
), timeFormat = "%H:%M", step=60*15, timezone = "",
animate=
animationOptions(interval=300, loop=TRUE)),
plotOutput("plot")
)
# Define server logic required
server <- function(input, output) {
output$plot<-renderPlot({
ggplot_df$start_ts <-as.POSIXct(ggplot_df$start_ts, format="%H:%M", tz="",origin="1970-01-01")
ggplot_df<-ggplot_df[ggplot_df$start_ts==input$time,]
ggplot(ggplot_df,aes(x=sum,y=V1))+geom_point() +
theme_bw() +
geom_smooth(method = "lm", se = FALSE) +
ylim(0,3) +
xlim(0,max(ggplot_df$sum)) +
annotate('text', max(ggplot_df$sum)-10,3,
label = paste("~R^{2}==",round(cor(ggplot_df$sum, ggplot_df$V1), digits=2)),parse = TRUE,size=4)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Note that exactly the same thing is happening even if I define the time zone.
ggplot_df is a data frame with 93 rows. What have I done wrong? The plot I receive is empty, no points, etc, as shown below:

The problem is that the POSIXct column is a datetime, but the slider input is only a time. Is date important here is it only time of day which is of interest? The code below makes some plots, although I can't tell what the desired end result is, so it may not be quite right
ui <- fluidPage(sliderInput("time", "Time:",
min = as.POSIXct("2019-04-14 00:00",format="%Y-%m-%d %H:%M", tz=""),
max = as.POSIXct("2019-04-15 24:00",format="%Y-%m-%d %H:%M", tz=""),
value = c(
as.POSIXct("2019-04-14 00:00")
), timeFormat = "%Y-%m-%d %H:%M", step=60*15, timezone = "",
animate=
animationOptions(interval=300, loop=TRUE)),
plotOutput("plot")
)
# Define server logic required
server <- function(input, output) {
output$plot<-renderPlot({
ggplot_df$start_ts <-as.POSIXct(ggplot_df$start_ts, tz="",origin="1970-01-01")
ggplot_df<-ggplot_df[ggplot_df$start_ts==input$time,]
ggplot(ggplot_df,aes(x=sum,y=V1))+geom_point() +
theme_bw() +
geom_smooth(method = "lm", se = FALSE) +
ylim(0,3) +
xlim(0,max(ggplot_df$sum)) +
annotate('text', max(ggplot_df$sum)-10,3,
label = paste("~R^{2}==",round(cor(ggplot_df$sum, ggplot_df$V1), digits=2)),parse = TRUE,size=4)
})
}

Related

Using Shiny to create multiple plots, one of which requires parameters estimated using the data

I am clearly relative new to R-Shiny and I am getting something done... Right now I am running in a bit of a problem that I do not seem to be able to overcome.
I am reading data for some stock, computing the returns and I would like to do two plots: one of the returns and one of the histogram (density like) + the plot of the estimated normal and skewed normal.
The code works outside of Shiny and it is not even difficult at all, really.
I also checked and my Shiny interface works as well. Plot 1 works.. If I replaced the part related to plot 2 with the same code for plot 1, I get the two plots. The problem is that something, somewhere, in creating the second plot fails. I am using functions and maximum likelihood estimates. I keep getting error messages along the lines of "operation not allowed without an active reactive context" . I modified almost everything I could, but 2 days later, nothing seems to work Is there anyone who could help me ? Please...
Here is the code I am using:
library(tidyquant)
library(timetk)
library(moments)
library(stats4)
library(shiny)
library(ggplot2)
library(dplyr)
stocknames <-c("AAPL","TSLA", "GME", "GOOG", "AMGN")
ui <- fluidPage(
titlePanel("Fitting a skewed normal to stock returns"),
sidebarLayout(position="left",
sidebarPanel("Choose a stock from the list",
selectInput("tick", label = "", choices=stocknames)
),
mainPanel("main panel", fluidRow(
splitLayout(style="border:1px solid silver:", cellWidths = c(400,400),
plotOutput("plot1"),
plotOutput("plot2")
)
)
)
)
)
server <- function(input, output) {
stockDatax <- reactive({
tq_get(input$tick, get="stock.prices", from ="2015-01-01")
})
daily_returns <- reactive({
stockDatax() %>%
tq_transmute(select = adjusted, # this specifies which column to select
mutate_fun = periodReturn, # This specifies what to do with that column
period = "daily")
})
plt1 <- reactive({daily_returns() %>%
ggplot(aes(x = date, y = daily.returns )) +
geom_line() +
theme_classic() +
labs(x = "Date", y = "daily returns") +
ggtitle(paste("Daily Returns for ", input$tick)) +
scale_x_date(date_breaks = "years", date_labels = "%Y") +
scale_y_continuous(breaks = seq(-0.5,0.6,0.05),
labels = scales::percent)
})
# This is where the part related to plot 2 begins and where the error(s) lie...
x<- reactive({pull(daily_returns,2)})
n <- reactive({length(x()) })
# need this function for log-likelihood function immediately below.
csi0 <-function(x){
y<- log(2*dnorm(x))
return(y)
}
# define likelihood function for skewed normal
ll<-function(mu, sigma, alpha){
n()*log(sigma)+ sum((x()-mu)^2/sigma^2) - sum(csi0(alpha*(x()-mu)/sigma)) # negative log-likelihood
}
# This uses mle to find the mle solutions numerically.
est<-mle(minuslogl=ll, start=list(mu=0, sigma=1, alpha=-0.1))
summary(est)
# extract the solutions to use them individually in what follows
m <- reactive({ unname(est#coef[1]) })
s <- reactive({ unname(est#coef[2]) })
a <- reactive({ unname(est#coef[3]) })
#
# symbolic probability density function that uses the parameters just estimated above.
dsknorm <- function(x){
y <- 2/s*dnorm((x-m())/s())*pnorm(a()*(x-m())/s())
return(y)
}
# doing the same for the normal distribution.
# MLE for normal distribution
m1 <- reactive({mean(x()) })
s1 <- reactive({sd(x()) })
dnorms <- function(x){
y <- dnorm(x, m1(), s1())
return(y)
}
grid_x <- reactive({ seq(min(x())-.03, max(x())+0.03, .005) })
ylistskdf <-reactive({sapply(grid_x(), dsknorm)})
ylistnormdf <-reactive({sapply(grid_x(), dnorms)})
# create a dataframe to use for plotting
dataToPlot <- reactive({data.frame(grid_x(), ylistnormdf(), ylistskdf()) })
plt2 <- reactive({ggplot(data=dataToPlot(), aes(x=grid_x())) +
geom_line(aes(y=ylistskdf(),colour="skewed_normal")) +
geom_line(aes(y=ylistnormdf(),colour="normal")) +
scale_color_manual(name="distribution", values=c(skewed_normal="blue", normal="red")) +
daily_returns() %>%
ggplot(aes(x = daily.returns)) +geom_density()+
theme_classic() +
labs(x = "Daily returns") +
ggtitle(paste("Daily Returns for", ticker)) +
scale_x_continuous(breaks = seq(min(x)-.03, max(x)+0.03, .02),
labels = scales::percent)
})
output$plot1 <-renderPlot({plt1()})
output$plot2 <-renderPlot({plt2()})
}
shinyApp(ui, server)
Any help would be greatly appreciated. I can handle Shiny for easier plots, but here with the parameters to use coming from estimation using the original data, I am really out of my league.
This is a working solution. I have not attempted to use req and the like and leave that as an exercise for the reader. Why does OP's code fail?
OP is attempting to access reactive elements within functions for example in this line: n()*log(sigma)+ sum((x()-mu)^2/sigma^2) - sum(csi0(alpha*(x()-mu)/sigma)) # negative log-likelihood.
Here, the function ll attempts to access reactive object x() which is not compatible with the shiny reactivity philosophy. Similar ideas can be seen in functions dsknorm and dnorms. Converting these to reactive elements/objects fixes the issue. However, it is still necessary to use req and friends to "await" processes. Otherwise, you get the error "Missing Value where true/false is needed".
NOTE I have commented out some code from plot2 that is not following ggplot2 best practices. This is the code in question. We cannot add a data.frame object to a ggplot2 object.
scale_color_manual(name="distribution", values=c(skewed_normal="blue", normal="red")) +
# daily_returns() %>%
# ggplot(aes(x = daily.returns)) +geom_density()+
# theme_classic() +
# labs(x = "Daily returns") +
# ggtitle(paste("Daily Returns for", ticker)) +
# scale_x_continuous(breaks = seq(min(x)-.03, max(x)+0.03, .02),
# labels = scales::percent)
# install.packages(c("tidyquant", "timetk", "moments", "stats4"))
library(tidyquant)
library(timetk)
library(moments)
library(stats4)
library(shiny)
library(ggplot2)
library(dplyr)
stocknames <-c("AAPL","TSLA", "GME", "GOOG", "AMGN")
ui <- fluidPage(
titlePanel("Fitting a skewed normal to stock returns"),
sidebarLayout(position="left",
sidebarPanel("Choose a stock from the list",
selectInput("tick", label = "", choices=stocknames)
),
mainPanel("main panel", fluidRow(
splitLayout(style="border:1px solid silver:", cellWidths = c(400,400),
plotOutput("plot1"),
plotOutput("plot2")
)
)
)
)
)
server <- function(input, output) {
stockDatax <- reactive({
tq_get(input$tick, get="stock.prices", from ="2015-01-01")
})
daily_returns <- reactive({
stockDatax() %>%
tq_transmute(select = adjusted, # this specifies which column to select
mutate_fun = periodReturn, # This specifies what to do with that column
period = "daily")
})
plt1 <- reactive({daily_returns() %>%
ggplot(aes(x = date, y = daily.returns )) +
geom_line() +
theme_classic() +
labs(x = "Date", y = "daily returns") +
ggtitle(paste("Daily Returns for ", input$tick)) +
scale_x_date(date_breaks = "years", date_labels = "%Y") +
scale_y_continuous(breaks = seq(-0.5,0.6,0.05),
labels = scales::percent)
})
# This is where the part related to plot 2 begins and where the error(s) lie...
x<- reactive({pull(daily_returns(),2)})
n <- reactive({length(x()) })
# need this function for log-likelihood function immediately below.
csi0 <-function(x){
y<- log(2*dnorm(x))
return(y)
}
est <- reactive({
csi0 <-function(x){
y<- log(2*dnorm(x))
return(y)
}
ll<-function(mu, sigma, alpha){
n()*log(sigma)+ sum((x()-mu)^2/sigma^2) -
sum(csi0(alpha*(x()-mu)/sigma)) # negative log-likelihood
}
mle(minuslogl=ll, start=list(mu=0, sigma=1, alpha=-0.1))
})
# define likelihood function for skewed normal
# This uses mle to find the mle solutions numerically.
# est<-
# summary(est)
# extract the solutions to use them individually in what follows
m <- reactive({ unname(est()#coef[1]) })
s <- reactive({ unname(est()#coef[2]) })
a <- reactive({ unname(est()#coef[3]) })
#
# symbolic probability density function that uses the parameters just estimated above.
# dsknorm <- function(x){
# y <- 2/s*dnorm((x-m())/s())*pnorm(a()*(x-m())/s())
# return(y)
# }
# # doing the same for the normal distribution.
# MLE for normal distribution
m1 <- reactive({mean(x()) })
s1 <- reactive({sd(x()) })
ylistnormdf <- reactive(
print(sapply(grid_x(), function(x) dnorm(x, m1(), s1())))
)
ylistskdf <- reactive(
print(sapply(grid_x(),
function(x) 2/s()*dnorm((x-m())/s())*pnorm(a()*(x-m())/s()) ))
)
grid_x <- reactive({ seq(min(x())-.03, max(x())+0.03, .005) })
# ylistskdf <-reactive({sapply(grid_x(), dsknorm)})
# ylistnormdf <-reactive({sapply(grid_x(), dnorms)})
# create a dataframe to use for plotting
dataToPlot <- reactive({data.frame(grid_x(), ylistnormdf(), ylistskdf()) })
plt2 <- reactive({ggplot(data=dataToPlot(), aes(x=grid_x())) +
geom_line(aes(y=ylistskdf(),colour="skewed_normal")) +
geom_line(aes(y=ylistnormdf(),colour="normal")) +
scale_color_manual(name="distribution", values=c(skewed_normal="blue", normal="red"))
# daily_returns() %>%
# ggplot(aes(x = daily.returns)) +geom_density()+
# theme_classic() +
# labs(x = "Daily returns") +
# ggtitle(paste("Daily Returns for", ticker)) +
# scale_x_continuous(breaks = seq(min(x)-.03, max(x)+0.03, .02),
# labels = scales::percent)
})
output$plot1 <-renderPlot({plt1()})
output$plot2 <-renderPlot({plt2()})
}
shinyApp(ui, server)
Result

R Shiny dynamically edit reactiveTimer timer interval

I am trying to create an R shiny dashboard which has a play and pause button for updating displaying a graph data along a sequence of time. To do this I was using a reactiveTimer, but it does not allow me to dynamically edit the reactiveTimer interval.
Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
My attempted code is
library(shiny)
library(ggplot2)
library(tidyr)
input.df <- read.csv(file = 'InputData.csv', header = TRUE, sep=",")
ui<-fluidPage(
titlePanel("Auckland Volcanic Simulation"),
hr(style="border-color: grey;"),
sidebarLayout(
# panel with all inputs
sidebarPanel(
fluidRow(
column(7,actionButton("stop","Pause")),
column(5,actionButton("play","Play"))
),
fluidRow(
column(7,actionButton("skip","Skip")),
column(3,actionButton("reset","Reset"))
)
),
# plot panel
mainPanel(
# visual data on same row
fluidRow(
span(textOutput("Date"), style="font-size: 24px;font-style: italic;")
),
fluidRow(
column(12,plotOutput('defGraph'))
)
)
)
)
server<-function(input,output){
control<-reactiveValues() # reactive to store all reactive variables
control$resetindicator<-0 # used to change button labels
control$count<-0 # day number in sequence
control$min<-0
control$max<-0
control$timer<-Inf
forward<-function(){
print("in forward")
control$resetindicator<-1 # change button label
step <- 12
if (step >= control$count) {
min <- 0
max <- control$count
} else {
min <- control$count - step
max <- control$count
}
control$min <- min
control$max <- max
control$count<-control$count+1
}
observeEvent(input$skip,{
forward()
})
session<-reactiveValues()
session$timer<-reactiveTimer(intervalMs = control$timer, session = getDefaultReactiveDomain())
observeEvent(input$play,{
print("play")
control$timer<-1000
#session$timer<-reactiveTimer(intervalMs = 1000, session = getDefaultReactiveDomain())# Time interval
observeEvent(session$timer(),{
print("calling forward")
forward()
})
})
observeEvent(input$stop,{
print("stop")
control$timer<-Inf
#session$timer<-reactiveTimer(intervalMs = Inf, session = getDefaultReactiveDomain())
})
## when reset button is pressed (set everything to original values, plus set seed)
observeEvent(input$reset,{
control$resetindicator<-0
control$count= 0
})
# ## depth plot output
output$DepthGraph <- renderPlot({
eqdepthdata.df <- input.df[(input.df$DayTimeID <= control$max & input.df$DayTimeID >= control$min), ]
ggplot(data.df, aes(x = DateTime, y = -1*AverageDepth_km)) +
geom_point() +
scale_color_manual(values = c("darkorange")) +
labs(title = "Average depth of earthquakes", x = "", y = "Depth (km)") +
ylim(-40, 0) +
theme_light() +
theme(text = element_text(size = 14)) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
})
## visual data outputs
output$Date<-renderText({
paste("Date:", input.df$DateTime[input.df$DayTimeID == control$count-1])
})
}
shinyApp(ui = ui, server = server)
CSV example is
DayTimeID,Date, Time, DateTime, AverageDepth_km
0, 20/08/20, 0:00, 20/08/20 0:00, 17
1, 20/08/20, 4:00, 20/08/20 4:00, 8
2, 20/08/20, 8:00, 20/08/20 8:00, 14
3, 20/08/20, 12:00, 20/08/20 12:00, 3
4, 20/08/20, 16:00, 20/08/20 16:00, 5
5, 20/08/20, 20:00, 20/08/20 20:00, 9
I was trying to update a parameter for the interval by updating the parameter. I was following code presented here https://nhsrcommunity.com/blog/animating-a-graph-over-time-in-shiny/ but if press play->pause->play but the time interval goes twice as fast as if I understand you create another reactiveTimer.
Any assistance would be appreciated.

Shiny Dashboard Date Slider Input

I am new to R and Shiny and have a problem that I have not been able to solve for hours.
I have a dataset from which I display the daily consumption of coffee on a dashboard, which works very well. The plot is a ggplot geom_line chart.
But now I want to be able to change the time period with two sliders.
The sliders I have also managed to do, but the plot does not change when the slider is moved.
I also suspect that I have an error with the date format.
What am I doing wrong?
Thanks for the help
RawData Dataset
library(shiny)
library(dplyr)
library(data.table)
library(ggplot2)
ui <- shinyUI(fluidPage(
# Application title
titlePanel("Coffee consumption"),
# Sidebar with a slider input for the number of bins
sidebarLayout(
sidebarPanel(
sliderInput("DatesMerge",
"Dates:",
min = as.Date("2018-01-22","%Y-%m-%d"),
max = as.Date("2020-04-04","%Y-%m-%d"),
value= c(as.Date("2018-01-22","%Y-%m-%d"),as.Date("2020-04-04","%Y-%m-%d")),
timeFormat="%Y-%m-%d")
),
mainPanel(
plotOutput("plot_daycount"),
tableOutput("structure"),
tableOutput("rawdata"),
tableOutput("dayconsumption"))
)
)
)
# RawData import
coffeedata = fread("C:/temp/ProductList.csv")
setDF(coffeedata)
coffeedata$Date = as.Date(coffeedata$Date, "%d.%m.%Y")
# Products a day counter
countcoffee <- function(timeStamps) {
Dates <- as.Date(strftime(coffeedata$Date, "%Y-%m-%d"))
allDates <- seq(from = min(Dates), to = max(Dates), by = "day")
coffee.count <- sapply(allDates, FUN = function(X) sum(Dates == X))
data.frame(day = allDates, coffee.count = coffee.count)}
# Making a DF with day consumption
daylicounter = countcoffee(df$coffee.date)
server <- shinyServer(function(input, output) {
output$structure = renderPrint({
str(coffeedata)
})
# Raw Data
output$rawdata = renderTable({
head(coffeedata)
})
output$dayconsumption = renderTable({
head(daylicounter)
})
# GGPLOT2
output$plot_daycount = renderPlot({
DatesMerge = input$DatesMerge
ggplot(daylicounter[daylicounter == DatesMerge], aes(daylicounter$day, daylicounter$coffee.count)) +
geom_line(color = "orange", size = 1)
scale_x_date(breaks = "3 month",
date_labels = "%d-%m-%Y")
# Try outs
# ggplot(daylicounter[month(day) == month(DatesMerge)], mapping = aes(day = day)) +
# geom_line(color = "orange", size = 1)
# scale_x_date(breaks = "3 month",
# date_labels = "%d-%m-%Y")
})
})
shinyApp(ui, server)
I appreciate your help
As noted by #Kevin, you need to use input$DatesMerge[1] and input$DatesMerge[2] when subsetting your data. For clarity, this can be done in a separate step. Try something like this in your server:
output$plot_daycount = renderPlot({
DatesMerge <- as.Date(input$DatesMerge, format = "%Y-%m-%d")
sub_data <- subset(daylicounter, day >= DatesMerge[1] & day <= DatesMerge[2])
ggplot(sub_data, aes(x = day, y = coffee.count)) +
geom_line(color = "orange", size = 1) +
scale_x_date(breaks = "3 month", date_labels = "%d-%m-%Y")
})
Edit Additional question from OP was asked:
Why does my date format look normal with str(coffeedata) but with
head(coffeedata) the date is just a number?
renderTable uses xtable which may have trouble with dates. You can get your dates to display correctly by converting to character first (one option):
output$rawdata = renderTable({
coffeedata$Date <- as.character(coffeedata$Date)
head(coffeedata)
})
output$dayconsumption = renderTable({
daylicounter$day <- as.character(daylicounter$day)
head(daylicounter)
})
See other questions on this topic:
as.Date returns number when working with Shiny
R shiny different output between renderTable and renderDataTable
Welcome to R and Shiny, you are off to a great start. Few things:
I don't recommend you using = in shiny, majority of the cases you want to use <-
To simplify code, no reason to add a variable like DatesMerge. It adds no value (at least in the code above)
For a dual slider, you need to tell shiny which end to pick. input$DatesMerge doesn't mean anything but input$DatesMerge[1] does.
When asking for help, it is always better to add a subset of data within the code itself. You tend to get more help and it is easier to run for the person trying to help (like me I was too lazy to download the file and place it in a folder so I didn't run it)
You need to account for a range of dates in your slider when subsetting the data; you also for the , when subsetting the data.
ggplot(daylicounter[daylicounter %in% input$DatesMerge[1]:input$DatesMerge[2],], aes(daylicounter$day, daylicounter$coffee.count)) +
geom_line(color = "orange", size = 1) +
scale_x_date(breaks = "3 month",
date_labels = "%d-%m-%Y")

R: Removed n rows containing missing values (geom_path)

I get a warning (Warning: Removed 2 rows containing missing values (geom_path).), which I don't want to have for the following code:
library(shiny)
library(ggplot2)
library(scales)
ui <- navbarPage("Test",
tabPanel("Test_2",
fluidPage(
fluidRow(
column(width = 12, plotOutput("plot", width = 1200, height = 600))
),
fluidRow(
column(width = 12, sliderInput("slider",
label = "Range [h]",
min = as.POSIXct("2019-11-01 00:00"),
max = as.POSIXct("2019-11-01 07:00"),
value = c(as.POSIXct("2019-11-01 00:00"),as.POSIXct("2019-11-01 07:00"))))
))))
server <- function(input, output, session) {
df <- data.frame("x" = c(as.POSIXct("2019-11-01 00:00"),as.POSIXct("2019-11-01 01:00"),
as.POSIXct("2019-11-01 02:00"),as.POSIXct("2019-11-01 03:00"),
as.POSIXct("2019-11-01 04:00"),as.POSIXct("2019-11-01 05:00"),
as.POSIXct("2019-11-01 06:00"),as.POSIXct("2019-11-01 07:00")),
"y" = c(0,1,2,3,4,5,6,7))
observe({
len_date_list <- length(df$x)
min_merge_datetime <- df$x[1]
max_merge_datetime <- df$x[len_date_list]
updateSliderInput(session, "slider",
min = as.POSIXct(min_merge_datetime),
max = as.POSIXct(max_merge_datetime),
timeFormat = "%Y-%m-%d %H:%M")
})
output$plot <- renderPlot({
in_slider_1 <- input$slider[1]
in_slider_2 <- input$slider[2]
ggplot(data=df, aes(x, y, group = 1)) +
theme_bw() +
geom_line(color="black", stat="identity") +
# geom_point() +
scale_x_datetime(labels = date_format("%m-%d %H:%M"),
limits = c(
as.POSIXct(in_slider_1),
as.POSIXct(in_slider_2)))
})
}
shinyApp(server = server, ui = ui)
It seems to be an general problem with the "missing values", because I have found a lot of similar questions. In this question it is explained that it must be the range of the axis. So in my case I'm sure that it is because of the limits in scale_x_datetime.
scale_x_datetime(labels = date_format("%m-%d %H:%M"),
limits = c(
as.POSIXct(in_slider_1),
as.POSIXct(in_slider_2)))
But I didn't found an answered question when scale_x_datetime, as.POSIXct and a slider is used.
BTW: If I comment out "geom_point" I get a further similar warning.
I think it is because you haven't filtered df so when the limits of scale_x_datetime come along they remove the rows in df that don't fit between the slider parameters. I added this:
df %>% filter(between(x, in_slider_1, in_slider_2))
which seems to remove the issue for me. Please test. Just to mention that I did have some time zone problems.
Full code below:
library(shiny)
library(ggplot2)
library(scales)
ui <- navbarPage("Test",
tabPanel("Test_2",
fluidPage(
fluidRow(
column(width = 12, plotOutput("plot", width = 1200, height = 600))
),
fluidRow(
column(width = 12, sliderInput("slider",
label = "Range [h]",
min = as.POSIXct("2019-11-01 00:00"),
max = as.POSIXct("2019-11-01 07:00"),
value = c(as.POSIXct("2019-11-01 00:00"),as.POSIXct("2019-11-01 07:00"))))
))))
server <- function(input, output, session) {
df <- data.frame("x" = c(as.POSIXct("2019-11-01 00:00"),as.POSIXct("2019-11-01 01:00"),
as.POSIXct("2019-11-01 02:00"),as.POSIXct("2019-11-01 03:00"),
as.POSIXct("2019-11-01 04:00"),as.POSIXct("2019-11-01 05:00"),
as.POSIXct("2019-11-01 06:00"),as.POSIXct("2019-11-01 07:00")),
"y" = c(0,1,2,3,4,5,6,7))
observe({
len_date_list <- length(df$x)
min_merge_datetime <- df$x[1]
max_merge_datetime <- df$x[len_date_list]
updateSliderInput(session, "slider",
min = as.POSIXct(min_merge_datetime),
max = as.POSIXct(max_merge_datetime),
timeFormat = "%Y-%m-%d %H:%M")
})
output$plot <- renderPlot({
in_slider_1 <- input$slider[1]
in_slider_2 <- input$slider[2]
ggplot(data=df %>% filter(between(x, in_slider_1, in_slider_2)), aes(x, y, group = 1)) +
theme_bw() +
geom_line(color="black", stat="identity") +
# geom_point() +
scale_x_datetime(labels = date_format("%m-%d %H:%M"),
limits = c(
as.POSIXct(in_slider_1),
as.POSIXct(in_slider_2)))
})
}
shinyApp(server = server, ui = ui)
It looks like you could now actually remove the scale_x_datetime completely and just have:
ggplot(data=df %>% filter(between(x, in_slider_1, in_slider_2)), aes(x, y, group = 1)) +
theme_bw() +
geom_line(color="black", stat="identity")
I know this question already has an answer, but this is another possible solution for you.
If you just want to get rid of it, that implies to me that you are OK with the output. Then you can try the following:
Add na.rm=TRUE to geom_line like : geom_line(..., na.rm=TRUE )
This explicitly tells geom_line and geom_path that is OK to remove NA values.
Reasoning with the warning:
Warning of: Removed k rows containing missing values (geom_path)
This tells you mainly 3 things:
geom_path is being called by another geom_something which is firing the warning. In your case, is geom_line.
It already removed k rows. So if the output is as desired, then you want to those rows removed.
The reason for removal is that some values ARE missing (NA).
What the warning doesn't tells you is WHY those rows have missing (NA) values.
You know that the reason comes from scale_x_datetime. Mainly from the limits argument. In a sense of (X,Y) pairs to be drawn, you set the X scale to values where is no "Y", or Y=NA. Your scale may be continuous, but your data is not. You may want to set a larger scale for a different number of reasons, but ggplot will always find that there isn't an associated Y value, and it makes a unilateral decision and fires a warning instead of an error.
Hopefully, times will come when Errors and Warnings highlights intuitive, language-independent calling trace to the emitter and a link to a correctly explained site with common mistakes, etc.

Suppressing non-integer axis breaks in ggplot2 graphs (in Shiny app)

I have tried all solutions recommended in How to display only integer values on an axis using ggplot2. Unfortunately, I could not solve the issue with any of them.
I have created a Shiny app that produces line graphs of annual data on a variety of variables. This works out nicely for most parameterizations:
No non-integer breaks
However, if I choose certain time spans on the slider, it produces graphs that have non-integer breaks on the x-axis, which makes no sense for a yearly data.
With non-integer breaks
Edit: Here a minimal reproducible version of the application
library(tidyverse)
library(shiny)
options(scipen = 999)
# Data
data1<-data.frame(values = c(15500, 16300, 18200, 28300, 23500, 23700,
31500, 35800, 34700, 36900, 40000, 44700,
53300, 55800, 69800, 89500, 1.13E+5,
1.53E+5, 1.77E+5, 1.83E+5, 1.99E+5),
year = seq(1990, 2010, 1))
#Shiny app
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("period", "Year:", min = 1990, max = 2010, value = c(1990, 2010), sep = "")),
mainPanel(plotOutput("ggplot2"))))
server <- function(input, output) {
data1_subset <- reactive({
filter(data1, year >= input$period[1] & year <= input$period[2])
})
output$ggplot2 <- renderPlot({
ggplot(data = data1_subset(), aes(x = year, y = values)) +
geom_line(aes(color = "red")) +
scale_x_continuous(name = "Year") +
scale_color_discrete(guide=FALSE)+
theme_minimal()
})
}
shinyApp(ui = ui, server = server)
To see the problem, select e.g. time span 2000-2010
Is there any way to suppress non-integer breaks as there are clearly nonsensical with annual data?
Thanks a lot in advance for your help!
Thanks for your help! It seems that the answer was much simpler as I thought. Putting breaks = function(x) unique(floor(pretty(x))) in my scale_x_continuous() function produced integer-only breaks, even without transforming the data into Date format. Removing the unique() does not change the behavior in my case, but it might do in other cases.
You should take care to use the correct class for your date column year (something like Date or POSIXct). As the comment suggests, you need to pick a particular date, like Jan 1st. This will inform ggplot2 about what's probably your intention and it will make it easier for you to steer the display format to something meaningful.
It will work out of the box, you can tune it more with ggplot's date/time scales.
The reprex with the presented idea implemented (and one more glitch regarding the line colour fixed):
library(tidyverse)
library(shiny)
# Data
tibble(values = c(15500,16300,18200,28300,23500,23700,
31500,35800,34700,36900,40000,44700,
53300,55800,69800,89500,1.13E+5,
1.53E+5,1.77E+5,1.83E+5,1.99E+5),
year = seq(1990, 2010, 1)) %>%
mutate(year = lubridate::ymd(year, truncated = 2L)) ->
data1
# Shiny app
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("period", "Year:",
min = 1990, max = 2010,
value = c(1990, 2010), sep = "")),
mainPanel(plotOutput("ggplot2"))))
server <- function(input, output) {
data1_subset <- reactive({
filter(data1,
year >= lubridate::ymd(input$period[1], truncated = 2L),
year <= lubridate::ymd(input$period[2], truncated = 2L))
})
output$ggplot2 <- renderPlot({
ggplot(data = data1_subset(), aes(x = year, y = values)) +
geom_line(color = "red") +
xlab("Year") +
theme_minimal()
})
}
shinyApp(ui = ui, server = server)

Resources