I need to get the dataframe from a function in rShiny server. But that function returns a Plot and the return value cannot be changed as the plots are used in the future use.
have not pasted the whole code as its like 200 lines each for the function and also for the rshiny server.
Hist_Read_data4 <- full_join(Hist_Read_data1,Hist_Read_data_opst, by = c("timestamp"))%>%
arrange(timestamp)%>%
subset(timestamp >= as.POSIXct(start_timestamp, origin = "1970-01-01") & timestamp <= as.POSIXct(end_timestamp, origin = "1970-01-01"))%>%
mutate(value.y = na.locf(value.y, na.rm = FALSE))%>%
mutate(value.y = fct_explicit_na(value.y, na_level = "None"))%>%
mutate(value.x = na.locf(value.x, na.rm=FALSE))%>%
mutate(new_value = abs(value.x - lag(value.x)))%>%
mutate(new_value = replace_na(new_value, 0))%>%
mutate(new_value = cumsum(new_value))
plot <- ggplot() +
geom_path(data = Hist_Read_data4, mapping = aes(x = timestamp, y=value.x, color = value.y), na.rm = TRUE, linejoin = 'round' , size=1.5, group = 1)
//Hist_Read_data4 is the dataframe which i need to return//
//plot is the return value of the function//
output$HoverText <- renderText({
coordinfo <- input$PlotHover
nearpts <- nearPoints(Hist_Read_data4, coordinfo, xvar= "timestamp", yvar = "value.y", threshold = 20)
})
need Hist_Read_data4 in inside nearpoints. But it cannot be accessed as its inside a function named chooseDevice() in a separate script file named data_funcs.R
I do not want to change the return value of the chooseDevice function from plot to returning this dataframe as it will complicate the whole code and 2 months work will be wasted.
Related
Using the bi_class(), I am trying to create mapping classes for a bivariate map. These data will be stored in a new variable named bi_class, which will be added to the given data object.
The code below returns an error of
Error in cut.default(.data[[var]], breaks = classInt::classIntervals(.data[[var]],:'breaks' are not unique
IDD_nhmap <- IDD_nhmap %>%
group_by(ProjectID) %>%
bi_class(x = race_black, y = svi, style = "quantile", dim = 3) %>%
bi_class(x = race_hisp, y = svi, style = "quantile", dim = 3)
I have a problem with the joined plot of an updatable line and static markers in R plotly. The line plot is updated via a drop down menu button, which works well on its own. The additional dots in the add_markers function are also correct when the plot is first initialized.
But after the first update, the markers are cut off (to the left side of the plot where the line starts) and remaining markers are modified (y values are different to initial ones).
For the example here the button function is simplified, but the result shows the same strange behavior.
`
sample_df <- tibble::tibble(quarter_date = rep(c("2022-06-30","2022-09-30","2022-12-31"),3),
forecast_value = runif(9,min = 10,max = 16),
forecast_date = c(rep("2022-07-23",3),rep("2022-08-26",3),rep("2022-09-15",3)))
marks = tibble::tibble(dates = c("2022-05-21","2022-06-15","2022-07-02","2022-07-26","2022-08-27"),
values = c(11,13,12,15,14))
create_buttons <- function(df, date_id) {
lapply(
date_id,
FUN = function(date_id,df) {
button <- list(
method = 'restyle',
args = list('y', list(df %>%
dplyr::filter(forecast_date == date_id) %>%
dplyr::pull(forecast_value))),
label = sprintf('Forecast # %s', date_id)
)
},
df
)
}
plotly::plot_ly(x = ~quarter_date) %>%
plotly::add_trace(data = sample_df %>%
dplyr::filter(forecast_date == max(forecast_date)),
#x = ~period_date,
y = ~forecast_value,
type = 'scatter',
mode = 'markers+lines',
name = 'forecasts') %>%
plotly::layout(
title = "Drop down menue",
yaxis = list(title = "y"),
updatemenus = list(
list(
y =1,
x = 0.9,
buttons = create_buttons(sample_df, unique(sample_df$forecast_date))
)
)) %>%
plotly::add_markers(data = marks,
x = ~dates,
y = ~values)
`
I have tried to set a wide xrange, used a second y2 axis and different approaches in the button calculation but nothing works as intended.
Does anyone have a clue why the add_markers is not working correctly after updating the line plot? Any ideas are highly appreciated!
Adding markers aren't the issue. The issue comes from the restyle. When you restyle the plot without designating that you only meant to change one trace, you changed all traces.
The solution is actually quite simple, you just need one more argument in your args call-- the trace number in a list: list(0) in this case. I've commented out your original args call, so you can see the change.
To make this repeatable, I added set.seed(46) before the creation of sample_df.
create_buttons <- function(df, date_id) {
lapply(
date_id,
FUN = function(date_id, df) {
button <- list(
method = 'restyle',
args = list('y', list(df %>% filter(forecast_date == date_id) %>%
pull(forecast_value)), list(0)),
# args = list('y', list(df %>%
# filter(forecast_date == date_id) %>%
# pull(forecast_value))),
label = sprintf('Forecast # %s', date_id)
)
},
df
)
}
Now when you run your plot, you will see that your marker data remains visible.
The whole function which i need to convert the for loop in to apply for optimization
plans_achievements <- function(pa_m,pa_q){
if(nrow(pa_m)==0 & nrow(pa_q==0)){
df = data.frame(a = c(""), b = c("No Data Available"))
colnames(df)=""
}else{
pa_m= pa_m%>% select(inc,month_year,Plans,Achievements,quarter_year)
colnames(pa_mon)[2] = "Period"
pa_q= pa_q%>% select(inc,quarter_year,Plans,Achievements)
colnames(pa_qtr)[2] = "Period"
df = data.frame(inc=c(""),Period=c(""),Plans=c(""),Achievements=c(""))
for (q in unique(pa_q$Period)){
df1 = pa_q[pa_q$Period==q,]
df1$Period = paste0("<span style=\"color:#288D55\">",df1$Period,"</span>")
df1$Plans = paste0("<span style=\"color:#288D55\">",df1$Plans,"</span>")
df1$Achievements = paste0("<span style=\"color:#288D55\">",df1$Achievements,"</span>")
df = rbind(df,df1)
for (m in unique(pa_m$quarter_year)){
if(m==q){
df2 = pa_m[pa_m$quarter_year==q,][-5]
df = rbind(df,df2)
}
}
}
df = df[-1,]
}
return(df)
}
The apply which i tried
my_fun <- function(q){
df1 = pa_qtr[pa_qtr$Period==q,]
df1$Period = paste0("<span style=\"color:#288D55\">",df1$Period,"</span>")
df1$Plans = paste0("<span style=\"color:#288D55\">",df1$Plans,"</span>")
df1$Achievements = paste0("<span style=\"color:#288D55\">",df1$Achievements,"</span>")
df = rbind(df,df1)
}
df = do.call(rbind,lapply(unique(pa_qtr$Period), my_fun))
my_fun2 <- function(m,my_fun){
if (m == q) {
df2 = pa_mon[pa_mon$qtr_yr == q, ][-5]
df = rbind(df,df2)
}
}
df = do.call(cbind,lapply(unique(pa_mon$qtr_yr), my_fun2))
DT::datatable(plans_achievements(pa_m[pa_m$inc=="vate",],pa_q[pa_q$inc=="vate",]), rownames = F,escape = FALSE,selection=list(mode="single",target="row"),options = list(pageLength = 50,scrollX = TRUE,dom = 'tp',ordering=F,columnDefs = list(list(visible=FALSE, targets=c(0)),list(className = 'dt-left', targets = '_all'))))
Why you get the error comparison is possible only for atomic and list types
I will answer your original question first:
You get the error because you haven't defined q as a variable inside the function my_fun2. Since you haven't defined this variable, R will look for it in the global environment. There R will find the function q() (used to quit R). So you get the error message comparison (1) is possible only for atomic and list types because R thinks you are trying to compare a number m with the function q.
Here is a small example to make it easy to see:
# Run this in a clean environment
m <- 1
m == b # Understandable error message - "b" is not found
m == q # Your error - because R thinks you are comparing m to a function
You fix this error by making sure that q is defined inside your function. Either by creating it inside the function, or by supplying it as an input argument.
A possible solution for your problem
As I understand your code, you want to format, merge and sort the values in pa_q and pa_m, to display them in a html table.
Under is a possible solution, using tidyverse and vectorized operations, rather than a loop or apply functions. Vectorized functions are typically your fastest option in R, as I know you want to optimize your code.
library(dplyr)
plans_achievements <- function(pa_m, pa_q) {
# I've modified the logic a bit: there is no need to wrap the full function in
# an else statement, since we can return early if the data has no rows
if (nrow(pa_m) == 0 && nrow(pa_q == 0)) {
df = data.frame(a = c(""), b = c("No Data Available"))
colnames(df) = ""
return(df)
}
pa_q <-
pa_q %>%
# Select and rename the columns vi need
select(inc, Period = quarter_year, Plans, Achievements, date) %>%
# Format the values
mutate(
Period = paste0("<span style=\"color:#288D55\">", Period,"</span>"),
Plans = paste0("<span style=\"color:#288D55\">", Plans,"</span>"),
Achievements = paste0("<span style=\"color:#288D55\">", Achievements,"</span>")
)
pa_m <-
pa_m %>%
# Select and rename the columns we need
select(inc, Period = month_year, Plans, Achievements, date) #%>%
# Combine the datasets
bind_rows(
pa_q,
pa_m
) %>%
# Make sure that R understand date as a date value
mutate(
date = lubridate::dmy(date)
) %>%
# Sort by date
arrange(desc(date)) %>%
# Remove columns we do not need
select(-date, -inc)
}
DT::datatable(
plans_achievements(
pa_m[pa_m$inc=="vate",],
pa_q[pa_q$inc=="vate",]
),
rownames = FALSE,
escape = FALSE,
selection = list(mode = "single", target = "row"),
options = list(
pageLength = 50,
scrollX = TRUE,
dom = 'tp',
ordering = FALSE,
columnDefs = list(
list(className = 'dt-left', targets = '_all')
)
)
)
Hopefully this solves your problem.
I'm trying to build a Shiny App, everything works ok, but my issue is at the beginning, the first time that my app is launched i get an error in my highcharts due the size of the data (more than 3M of rows),
After 10 seconds the error disapear and everithing looks ok, but i want to remove the error, now i'm using waiter package, loading screeen is displayed 1.5 seconds, then the error appear and later the graph is showed .
I want to use Waiter package to hide this error until every calculation is finished. This is the Error
Below here my code for the graph
# Graph for shortInterest tab By CvsI (bars) --Dynamic
output$graph_bars_shortInterest_hc <- renderHighchart({
waiter_show(
id = "graph_bars_shortInterest_hc",
html = tagList(spin_fading_circles(),
"Loading Model ..."),
color = "#63666a",
logo = "",
hide_on_render = !is.null(id)
)
Client <- subset(Data_russel, Metrics == "marketCap") %>%
filter(Value >= input$MC_bars_[1])%>%
filter(Value <= input$MC_bars_[2])%>%
select(Client_Name) %>% unique()
Client_2 <- subset(Data_russel, Metrics == "Annual_Limit_Adequacy") %>%
filter(Value >= input$AL_filter_[1])%>%
filter(Value <= input$AL_filter_[2])%>%
select(Client_Name) %>% unique()
Data_Metric <- subset(Data_russel, Metrics == "shortInterest" & Industry %in% input$industry_CvsI_bars)
Client_filtered <- inner_join(Client, Client_2, by = "Client_Name")
Data_ <- inner_join(Client_filtered, Data_Metric, by = "Client_Name") # Clients in the range of Selected Market cap
Data_c <- subset(Data_russel, Metrics == "shortInterest" & Industry %in% input$industry_CvsI_bars & Client_Name == input$clientname_CvsI_bars)
Table_ <- seq(input$perc_range_[1], input$perc_range_[2], 1) %>% as.data.frame()
names(Table_) <- "Percentile"
Table_$Value <- round( quantile(Data_c$Value, Table_$Percentile/100), digits = 2)
Table_$Industry <- round( quantile(Data_$Value, Table_$Percentile/100), digits = 2)
hc_1 <- Table_ %>%
hchart(. , type = "line", hcaes(x = Percentile, y = Value), name = "Client", color = "#FFB81C") %>%
hc_add_series(data = Table_ ,type = 'line' , color = "#00a0d2", name = "Industry", hcaes(x = Percentile, y = Industry))%>%
hc_yAxis(opposite = TRUE) %>%
hc_title(text = "shortInterest Benchmark", margin = 30,
align = "center",
style = list(color = "#702080", useHTML = TRUE)) %>%
hc_yAxis(max = max(Table_$Industry)+(sd(Table_$Industry)/5))%>%
hc_yAxis(min = min(Table_$Industry)-(sd(Table_$Industry)/5))%>%
hc_add_theme(hc_theme_google())
hc_1
})
Thanks !!
I fixed using next function, and using each output in the UI into this function
output %>% withSpinner(
type = getOption("spinner.type", default = 3),
color.background = getOption("spinner.color.background", default = "#C8D7DF" ),
color="#00A0D2")
}```
I want to create a new stat which calculates interval-censored survival with survival::survfit.formula. But I seem to get a wrong data frame in the compute_group function, and I struggle to find the reason for it.
Creating a data frame with exactly the same code "outside" and using geom_path (which I want to use for the stat), results in a fine result (see expected result). - it seems as if survfit.formula() is creating NAs within compute_group(), but I don't understand why.
setting /adding na.rm = TRUE/FALSE does not change anything.
Also using Inf instead of NA for time2 does not help.
library(ggplot2)
library(survival)
set.seed(42)
testdf <- data.frame(time = sample(30, replace = TRUE), time2 = c(20, 10, 10, 30, rep(NA, 26)))
fit_icens <-
survival::survfit.formula(
survival::Surv(time = time, time2 = time2, type = "interval2") ~ 1,
data = testdf
)
Expected result
path <- data.frame(time = fit_icens$time, time2= fit_icens$surv)
ggplot(path, aes(x = time, y = time2)) +
geom_path() +
coord_cartesian(ylim = c(0, 1))
Failing
StatIcen <- ggplot2::ggproto("StatIcen", Stat,
required_aes = c("time", "time2"),
compute_group = function(data, scales) {
fit_icens <-
survival::survfit.formula(
survival::Surv(time = data$time, time2 = data$time2, type = "interval2") ~ 1,
data = data
)
path <- data.frame(x = fit_icens$time, y = fit_icens$surv)
path
}
)
stat_icen <- function(mapping = NULL, data = NULL, geom = "path",
position = "identity", show.legend = NA,
inherit.aes = TRUE, ...) {
layer(
stat = StatIcen, data = data, mapping = mapping, geom = geom,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(...)
)
}
ggplot(testdf, aes(time = time, time2 = time2)) +
stat_icen()
#> Warning: Removed 26 rows containing non-finite values (stat_icen).
Created on 2020-05-04 by the reprex package (v0.3.0)
Great question Tjebo, thanks for posting.
As you have already figured out, the problem is that the NA values are being stripped out of your data before it is passed to compute_group. The Extending ggplot vignette doesn't mention this, but your data is first passed through the compute_layer member function of your ggproto object. Since you haven't defined a compute_layer method, your StatIcen class inherits the method from the class ggplot2::Stat.
If you look at the source code for this method in ggplot2::Stat$compute_layer, you will see this is where your NA values are stripped out, using the remove_missing function, which removes rows in your data frame with missing values in any of the named columns. Presumably, you still want NA values removed if they appear in your time column, but not if they appear in time2.
So all I have done here is to copy the source code from Stat$compute_layer and adjust the remove_missing call slightly, then make it a member of StatIcen:
StatIcen <- ggplot2::ggproto("StatIcen", Stat,
required_aes = c("time", "time2"),
compute_group = function(data, scales){
fit_icens <- survival::survfit.formula(
survival::Surv(time = data$time, time2 = data$time2,
type = "interval2") ~ 1, data = data)
data.frame(x = fit_icens$time, y = fit_icens$surv)
},
compute_layer = function (self, data, params, layout)
{
ggplot2:::check_required_aesthetics(self$required_aes, c(names(data),
names(params)), snake_class(self))
data <- remove_missing(data, params$na.rm, "time",
ggplot2:::snake_class(self), finite = TRUE)
params <- params[intersect(names(params), self$parameters())]
args <- c(list(data = quote(data), scales = quote(scales)), params)
ggplot2:::dapply(data, "PANEL", function(data) {
scales <- layout$get_scales(data$PANEL[1])
tryCatch(do.call(self$compute_panel, args),
error = function(e) {
warning("Computation failed in `",
ggplot2:::snake_class(self),
"()`:\n", e$message, call. = FALSE)
ggplot2:::new_data_frame()
})
})
}
)
So now we get:
ggplot(testdf, aes(time = time, time2 = time2)) + stat_icen()