Related
How to filter out columns in shiny DT datatable based on cell color. Just like we have in excel.
[Need to filter the column with yellow color in background.]
Below is the code for cells with color:
input_data <- data.frame(Record_Status = c("Modified","NO","NO","Modified","NO","NO","Modified","NO","NO"),
Field_Changed = c("Brand,ratio","Gender","Name","ratio,Name,Gender","cost","Brand,cost","ratio,cost","cost","Name"),
Brand = c(3,6,9,12,15,18,21,24,27),
ratio = c (1,2,3,4,5,6,7,8,9),
cost = c(3,6,9,12,15,18,21,24,27),
Name = c("A","B","C","A","B","C","A","B","C"),
Gender = c("A","B","C","A","B","C","A","B","C"),
stringsAsFactors = FALSE)
# Build hidden logical columns for conditional formatting
dataCol_df <- ncol(input_data)
dataColRng <- 3:dataCol_df
argColRng <- (dataCol_df + 1):(dataCol_df * 2 -2)
df <- sapply(1:ncol(input_data),function(i) ifelse(input_data[[1]]=="Modified" &
str_detect(input_data[[2]], names(input_data)[i]),
"1","0"))
df <- df[,-c(1,2)]
input_data <- data.frame(input_data, df)
# Create Shiny Output
shinyApp(
ui =
navbarPage("Testing",dataTableOutput('dt')),
server = function(input, output, session) {
output$dt = DT::renderDataTable(
datatable(input_data,
# Hide logical columns
options=list(columnDefs = list(list(visible=FALSE,
targets=argColRng)))) %>%
# Format data columns based on the values of hidden logical columns
formatStyle(columns = dataColRng,
valueColumns = argColRng,
backgroundColor = styleEqual(c('1', '0'),
c("yellow", "white")))
)}
)
I think you have more than I issue here. For me the shiny app is not running and I believe this might be due to a mixup what should be in the ui and what in the server function.
About your original question. You could use the library DT and color the cells you like. This is independent of your shiny app, however, I believe you can use this also in the app, once you have the app running without the coloring.
library(DT)
datatable(input_data) %>% formatStyle(
'Brand', 'X1',
backgroundColor = styleEqual(c(0, 1), c('gray', 'yellow'))
)
I am trying to make an interactive stock performance plot from R. It is to compare the relative performance of several stocks. Each stock's performance line should start at 0%.
For static plots I would use dplyr group_by and mutate to calculate performance (see my code).
With ggplot2 and plotly/ggplotly, rangeslider() allows to interactively select the x-axis range. Now I'd like performance to be starting at 0 from any start range selected.
How can I either move the dplyr calculation into the plotting or have a feedback loop to recalculate as the range is changed?
Ideally it should be usable in static RMarkdown HTML. Alternatively I'd also switch to Shiny.
I tried several options for rangeslider. Also I tried with ggplot stat_function but could not achieve the desired result. Also I found dygraphs which has dyRangeSelector. But also here I face the same problem.
This is my code:
library(plotly)
library(tidyquant)
stocks <- tq_get(c("AAPL", "MSFT"), from = "2019-01-01")
range_from <- as.Date("2019-02-01")
stocks_range <- stocks %>%
filter(date >= range_from) %>%
group_by(symbol) %>%
mutate(performance = adjusted/first(adjusted)-1)
p <- stocks_range %>%
ggplot(aes(x = date, y = performance, color = symbol)) +
geom_line()
ggplotly(p, dynamicTicks = T) %>%
rangeslider(borderwidth = 1) %>%
layout(hovermode = "x", yaxis = list(tickformat = "%"))
If you do not want to use shiny, you can either use the dyRebase option in dygraphs, or you have to insert custom javascript code in plotly. In both examples, I rebase to one, not zero.
Option 1: with dygraphs
library(dygraphs)
library(tidyquant)
library(timetk)
library(tidyr)
stocks <- tq_get(c("AAPL", "MSFT"), from = "2019-01-01")
stocks %>%
dplyr::select(symbol, date, adjusted) %>%
tidyr::spread(key = symbol, value = adjusted) %>%
timetk::tk_xts() %>%
dygraph() %>%
dyRebase(value = 1) %>%
dyRangeSelector()
Note that `dyRebase(value = 0) does not work.
Option 2: with plotly using event handlers. I try to avoid ggplotly, hence my plot_ly solution. Here the time selection is just by zooming, but I think it can be done by a range selector as well. The javascript code in onRenderRebaseTxt rebases every trace to the first visible data point (taking care of possible missing values). It is only called with the relayout event, hence the first rebasing must be done before the plot.
library(tidyquant)
library(plotly)
library(htmlwidgets)
library(dplyr)
stocks <- tq_get(c("AAPL", "MSFT"), from = "2019-01-01")
pltly <-
stocks %>%
dplyr::group_by(symbol) %>%
dplyr::mutate(adjusted = adjusted / adjusted[1L]) %>%
plotly::plot_ly(x = ~date, y = ~adjusted, color = ~symbol,
type = "scatter", mode = "lines") %>%
plotly::layout(dragmode = "zoom",
datarevision = 0)
onRenderRebaseTxt <- "
function(el, x) {
el.on('plotly_relayout', function(rlyt) {
var nrTrcs = el.data.length;
// array of x index to rebase to; defaults to zero when all x are shown, needs to be one per trace
baseX = Array.from({length: nrTrcs}, (v, i) => 0);
// if x zoomed, increase baseX until first x point larger than x-range start
if (el.layout.xaxis.autorange == false) {
for (var trc = 0; trc < nrTrcs; trc++) {
while (el.data[[trc]].x[baseX[trc]] < el.layout.xaxis.range[0]) {baseX[trc]++;}
}
}
// rebase each trace
for (var trc = 0; trc < nrTrcs; trc++) {
el.data[trc].y = el.data[[trc]].y.map(x => x / el.data[[trc]].y[baseX[trc]]);
}
el.layout.yaxis.autorange = true; // to show all traces if y was zoomed as well
el.layout.datarevision++; // needs to change for react method to show data changes
Plotly.react(el, el.data, el.layout);
});
}
"
htmlwidgets::onRender(pltly, onRenderRebaseTxt)
I found a solution with plotly_relayout which reads out the visible x-axis range. This is used to recompute the performance. It works as a Shiny app. Here's my code:
library(shiny)
library(plotly)
library(tidyquant)
library(lubridate)
stocks <- tq_get(c("AAPL", "MSFT"), from = "2019-01-01")
ui <- fluidPage(
titlePanel("Rangesliding performance"),
mainPanel(
plotlyOutput("plot")
)
)
server <- function(input, output) {
d <- reactive({ e <- event_data("plotly_relayout")
if (is.null(e)) {
e$xaxis.range <- c(min(stocks$date), max(stocks$date))
}
e })
stocks_range_dyn <- reactive({
s <- stocks %>%
group_by(symbol) %>%
mutate(performance = adjusted/first(adjusted)-1)
if (!is.null(d())) {
s <- s %>%
mutate(performance = adjusted/nth(adjusted, which.min(abs(date - date(d()$xaxis.range[[1]]))))-1)
}
s
})
output$plot <- renderPlotly({
plot_ly(stocks_range_dyn(), x = ~date, y = ~performance, color = ~symbol) %>%
add_lines() %>%
rangeslider(start = d()$xaxis.range[[1]], end = d()$xaxis.range[[2]], borderwidth = 1)
})
}
shinyApp(ui = ui, server = server)
Definign the start/end of the rangeslider only works with plot_ly, not with a ggplot object converted by ggplotly. I am unsure if this is a bug, therefore opened an issue on Github.
I want to change the font size of the entire data table., The default size is too large for what I want to display. I am trying to change the font-size of variable names and column content with formatStyle as follows
library(DT)
library(magrittr)
iris %>%
datatable() %>%
formatStyle(columns = colnames(.), `font-size` = '25%')
But, what is rendered is the same font size regardless of the changes. Tried different formats for font-size such as 'large, small' '150%, 50%' and '10px,20px,30px'
This question has already been addressed here.
To accomplish what you are trying to do:
library(DT)
library(magrittr)
iris %>%
datatable() %>%
DT::formatStyle(columns = colnames(iris), fontSize = '25%')
Was able to change the font-size for header and the columns by updating the CSS. The table header with JS api and the column content font size with the formatStyle as follows. However, the header and the footer font size stayed the same. I would like to change header/footer/body (entire font for the table) in one swoop. Is that possible?
datatable(head(iris, 20), options = list(
initComplete = JS(
"function(settings, json) {",
"$(this.api().table().header()).css({'font-size': '5px', 'background-color': '#c2d1f0', 'color': '#fff'});",
"}"))) %>% formatStyle(columns = colnames(.$x$data), `font-size` = '12px')
Attempted to update CSS for columns with the following command without success
"$(this.api().columns().data()).css({'font-size': '5px'});"
"$(this.api().table().footer()).css({'font-size': '10px});"
"$(this.api().tables().body()).css({'font-size': '10px'});"
datatable(head(iris, 20), options = list(
initComplete = JS("
function(settings, json) {
$(this.api().table().header()).css({
'font-size': '12px',
});
}
")
)) %>%
formatStyle(columns = colnames(.$x$data), `font-size` = "12px")
I have some tables in Microsoft Excel that I need to recreate in an R Shiny App. The formatting in R has to remain at least mostly the same as the original context.
Here are images of the original tables:
Table 1
Table 2
Notice the formatting: There are lines under table headers and above totals, headers and totals are bolded, numbers in the Monthly Bill column have thousands seperated by commas and have dollar symbols, and the final number in Table 2 is boxed in.
If the lines were not recreatable it would be fine, but I need to at least be able to bold the selected topics, headers, and totals, and be able to get the correct number format for the Monthly Bill column.
I have tried using the DT package but I can't figure out how to format rows instead of columns. I noticed DT uses wrappers for JavaScript functions but I don't personally know JavaScript myself. Is there a way to format this the way I that I need through R packages or Javascript?
Edit:
Although it would be simple, I cannot merely include an image of the tables because some of the numbers are going to be linked to user input and must have the ability to update.
pixiedust makes it easy to do cell-specific customizations.
T1 <- data.frame(Charge = c("Environmental", "Base Power Cost",
"Base Adjustment Cost", "Distribution Adder",
"Retail Rate Without Fuel", "Fuel Charge Adjustment",
"Retail Rate With Fuel"),
Summer = c(0.00303, 0.06018, 0.00492, 0.00501, 0.07314,
0.02252, 0.09566),
Winter = c(0.00303, 0.05707, 0.00468, 0.01264, 0.07742,
0.02252, 0.09994),
Transition = c(0.00303, 0.05585, 0.00459, 0.01264,
0.07611, 0.02252, 0.09863),
stringsAsFactors = FALSE)
T2 <- data.frame(Period = c("Summer", "Winter", "Transition", "Yearly Bill"),
Rate = c(0.09566, 0.09994, 0.09863, NA),
Monthly = c(118.16, 122.44, 121.13, 1446.92),
stringsAsFactors = FALSE)
library(shiny)
library(pixiedust)
library(dplyr)
options(pixiedust_print_method = "html")
shinyApp(
ui =
fluidPage(
uiOutput("table1"),
uiOutput("table2")
),
server =
shinyServer(function(input, output, session){
output$table1 <-
renderUI({
dust(T1) %>%
sprinkle(rows = 1,
border = "bottom",
part = "head") %>%
sprinkle(rows = c(5, 7),
cols = 2:4,
border = "top") %>%
sprinkle(rows = c(5, 7),
bold = TRUE) %>%
sprinkle(pad = 4) %>%
sprinkle_colnames(Charge = "") %>%
print(asis = FALSE) %>%
HTML()
})
output$table2 <-
renderUI({
T2 %>%
mutate(Monthly = paste0("$", trimws(format(Monthly, big.mark = ",")))) %>%
dust() %>%
sprinkle(rows = 1,
border = "bottom",
part = "head") %>%
sprinkle(rows = 4,
cols = 1,
bold = TRUE) %>%
sprinkle(rows = 4,
cols = 3,
border = "all") %>%
sprinkle(na_string = "",
pad = 4) %>%
sprinkle_colnames(Period = "",
Monthly = "Monthly Bill") %>%
print(asis = FALSE) %>%
HTML()
})
})
)
This would be easier if you provided an example of your data, but sticking with DT, you should be able to utilize formatStyle to change formatting of both rows and columns. For an example to bold the first row, see the following (assuming your data frame is called df):
df %>%
datatable() %>%
formatStyle(
0,
target = "row",
fontWeight = styleEqual(1, "bold")
)
The rstudio DT page offers more examples: http://rstudio.github.io/DT/010-style.html
Alternatively, I think you might be better off using the stargazer package.
The base plot would look very similar to your desired result.
stargazer::stargazer(df, type = "html", title = "Table 1")
That will get you started, but see here for a LOT more flexibility: https://www.jakeruss.com/cheatsheets/stargazer/
I am trying to only show two decimal places for all data in my table and align everything centrally. The first column is countries, but the rest are numbers. This is the code
output$Composite <- renderDataTable(FVI_DATA_COMPOSITE, options = list(pageLength = 15,lengthChange=FALSE))
Any idea how to do that?
Edit: This does not work.
output$Composite <- renderDataTable(FVI_DATA_COMPOSITE,
options = list(pageLength = 10,lengthChange=FALSE), round(FVI_DATA_COMPOSITE[3:9], digits=2)
output$Composite <- renderDataTable(datatable(FVI_DATA_COMPOSITE,
options = list(pageLength = 10,lengthChange=FALSE)) %>% formatRound(c(3:9), 2)
Documentation here
Edit: To center align
output$Composite <- renderDataTable(datatable(FVI_DATA_COMPOSITE,
options = list(pageLength = 10,lengthChange=FALSE)) %>%
formatRound(c(3:9), 2) %>%
formatStyle(columns = c(3:9), 'text-align' = 'center')
The accepted answer's code pattern kept returning the following warning/error:
Warning: Error in : object of type 'closure' is not subsettable
I had to use a slightly different pattern within a Shiny app.R file:
server <- function(input, output) {
output$dtable <- DT::renderDataTable({
datatable(FVI_DATA_COMPOSITE) %>%
formatRound(columns = c(3:9), digits = 2)
})
}