The row names of the iris dataframe are "1", "2", "3", ...
When I set the 0-th column as orderable with DT, the ordering behaves as if the row names were numeric:
library(DT)
datatable(iris,
options = list(
columnDefs = list(
list(orderable=TRUE, targets=0)
)
)
)
Nice. Now, when I do the same inside shiny, the behaviour is different: the ordering behaves as if the row names were character strings:
library(shiny)
shinyApp(
ui = fluidPage(fluidRow(column(12, DTOutput('tbl')))),
server = function(input, output) {
output$tbl = renderDT(
iris, options = list(
columnDefs = list(
list(orderable=TRUE, targets=0)
)
)
)
}
)
Not nice. What is the cause of the difference? I'd like to have the first behavior in Shiny. As a workaround, we could set a numeric column at the first position and set rownames=FALSE, but I'm wondering whether there's an easier solution and I'm intrigued by this difference.
EDIT
I've finally proceed in this way:
output$tbl = renderDT({
dt <- datatable(
iris, options = list(
columnDefs = list(
list(orderable=TRUE, targets=0)
)
)
)
dt$x$data[[1]] <- as.numeric(dt$x$data[[1]])
dt
})
SO is telling me I need 50 reputation to comment, so here's my comment in answer form.
Another workaround would be to do the following:
output$tbl = renderDT({
dt <- datatable(
iris %>%
rownames_to_column("UID") %>%
select(UID, everything()),
options = list(
columnDefs = list(
list(orderable=TRUE, targets=0)
)
)
)
dt
It doesn't answer your question of why it happens though.
Related
Was hoping someone can help sort a column by absolute value in a Shiny app in the datatable() function? Tried multiple methods (dplyr, arrange, etc) but for some reason it's not clicking with me. It's a three column datatable, trying to sort column 2/val2 by the absolute value.
table_stage <- reactive ({
tbl <- datatable(tabledat(),
rownames = FALSE,
options = list(
columnDefs = list(list(className = "dt-center", targets = 2)),
order = list(list(2, "asc"))
)) %>%
formatRound("val", 2) %>%
formatRound("val2", 2)
return(tbl)
})
This is definitely wrong, did not work at all.
table_stage <- reactive ({
tbl <- datatable(tabledat(),
rownames = FALSE,
options = list(
columnDefs = list(list(className = "dt-center", targets = 2)),
order = list(list((arrange(abs(2)), "desc"))
)) %>%
formatRound("val", 2) %>%
formatRound("val2", 2)
return(tbl)
})
The code runs without problems when using dataTableOutput in a UI context and renderDataTable in the Server function.
This script works with order by mpg (the first column in the example table) and if mtcars is a data frame, it works too.
library(data.table)
library(shiny)
library(dplyr)
library(DT)
if (interactive()) {
ui <- fluidPage(
dataTableOutput("table")
)
#optional test data
tabledat <- data.table::as.data.table(mtcars)
server <- function(input, output) {
output$table <-
renderDataTable({
tabledat %>%
datatable(
rownames = FALSE,
options = list(
columnDefs = list(list(className = "dt-center", targets = 2)),
order = list(list(1, "asc"))
)
)
},
)
}
shinyApp(ui, server)
}
You need to use the render option:
library(DT)
js <- "
function(data, type, row, meta) {
if(type === 'sort') {
data = Math.abs(data);
}
return data;
}
"
mydata <- as.data.frame(
matrix(runif(40, -10000, 10000), nrow = 10, ncol = 4)
)
datatable(
mydata,
options = list(
"columnDefs" = list(
list(
"targets" = 1,
"render" = JS(js)
)
)
)
)
Let's say that I have a shiny app displaying a data table like the following:
library(shiny)
library(tidyverse)
library(datasets)
library(DT)
data<- as.data.frame(USArrests)
#data<- cbind(state = rownames(data), data)
ui <- fluidPage(
dataTableOutput("preview")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$preview<- renderDataTable(
datatable(data, options = list(searching = T, pageLength = 10, lengthMenu = c(5,10,15, 20), scrollY = "600px", scrollX = T ))
)
}
# Run the application
shinyApp(ui = ui, server = server)
Let's say I then type in "Iowa" into the search box. I would like to save that filtered datatable into a seperate dataframe within the app. I would like it to be dynamic as well so if I typed "Kentucky", it would save Kentucky's filtered data into the dataframe instead. Is there a way to do this?
NOTE: this is a DT datatable
Maybe this type of solution. It is possible to add further conditions like checking the first letter in upper case, but the main idea is to check each column and search for the pattern entered inside the datatable searchbox. This may or may not result in more than one dataset to print (depending if the string is partially matched in multiple columns (this is also solvable with rbind function.
code:
library(shiny)
library(tidyverse)
library(datasets)
library(DT)
data <- as.data.frame(USArrests)
data <- cbind(state = rownames(data), data)
ui <- fluidPage(
dataTableOutput("preview"),
tableOutput('filtered_df')
)
# Define server logic required to draw a histogram
server <- function(input, output) {
df <- reactiveValues()
output$preview<- renderDataTable(
datatable(data, options = list(searching = T, pageLength = 10, lengthMenu = c(5,10,15, 20), scrollY = "600px", scrollX = T ))
)
observeEvent(input$preview_search, {
searched_string <- map(data, ~str_subset(.x, input$preview_search)) %>% discard(~length(.x) == 0)
df$filtered <- syms(names(data)) %>%
map(~ filter(data, !!.x %in% searched_string)) %>%
discard(~ nrow(.x) == 0)
})
output$filtered_df <- renderTable({df$filtered})
}
# Run the application
shinyApp(ui = ui, server = server)
I have a example shiny app here. It displays editable datatable using DT package.
To enable downloading all data shown on multiple pages, I use server=FALSE together with renderDT.
What I want to achieve now is
restrict user to edit some specific columns.
The following code does not seem to work.
editable = list(target = 'cell', disable = list(column = c("Sepal.Length", "Sepal.Width")))
I want to specify a default file name when exporting to csv, something like data.csv. Is that possible?
Super appreciate it if someone can help me out on that. Thanks a lot.
library(shiny)
library(DT)
library(dplyr)
# UI
ui = fluidPage(
selectInput("nrows",
"select n entries",
choices = 100:150,
selected = 100,
multiple = FALSE),
DT::dataTableOutput('tbl'),
checkboxGroupInput('datacols',
label='Select Columns:',
choices= c('Sepal.Length', 'Sepal.Width', 'Petal.Length', 'Petal.Width', 'Specie'),
selected = c('Sepal.Length', 'Sepal.Width', 'Petal.Length', 'Petal.Width', 'Specie'),
inline=TRUE )
)
# SERVER
server = function(input, output) {
df = reactiveValues()
observe ({
df$dat = iris %>% .[1:input$nrows, ]
})
# render DT
output$tbl = renderDT(server=FALSE, {
datatable(df$dat %>% select(one_of(input$datacols)),
editable = list(target = 'cell', disable = list(column = c("Sepal.Length", "Sepal.Width"))), #"cell",
extensions = "Buttons",
options = list(
dom = "Bfrtip", buttons = list("csv")))
})
observeEvent(input[["tbl_cell_edit"]], {
cellinfo <- input[["tbl_cell_edit"]]
df$dat <- editData(df$dat, input[["tbl_cell_edit"]])
})
}
shinyApp(ui=ui, server = server)
To disable some columns for editing, you have to give the column indices, not the column names. Moreover the key word is columns, not column:
editable = list(target = 'cell', disable = list(columns = c(1,2)))
To specify the file name, do:
buttons = list(
list(extend = "csv", text = "Export to CSV", filename = "iris")
)
I am using the DT library to visualize tables, but let's say I want to give a color to some rows like for example RED from row 1 to row 4:
Also it would be really nice to change the color of the text if it's possible. After hours of searching I found this function from library DT:
datatable(df, rownames = FALSE) %>%
formatStyle(columns = "inputval",
background = styleInterval(c(0.7, 0.8, 0.9)-1e-6, c("white", "lightblue", "magenta", "white")))
But I need to give color to all columns not just a selected column like inputval in the code, can I give to columns value something like names(df) so it can give color to all columns? And styleInterval selects the values in table not the interval of rows, how can I do that so I can select the rows and give them a color?
Something like this should do the job. Note that I coloured the rows 2:4 on purpose instead of 1:4 for more functionality:
library(shiny)
library(DT)
ui <- basicPage(
mainPanel(DT::dataTableOutput('mytable'))
)
server <- function(input, output,session) {
output$mytable = DT::renderDataTable(
DT::datatable(mtcars, options = list(
pageLength = 25,
rowCallback = JS('function(row, data, index, rowId) {',
'console.log(rowId)','if(rowId >= 1 && rowId < 4) {',
'row.style.backgroundColor = "pink";','}','}')
)
)
)
}
runApp(list(ui = ui, server = server))
Edit: Dynamically colour rows: here I simply used sub to substitute for the range to colour the rows
library(shiny)
library(DT)
fnc <- JS('function(row, data, index, rowId) {',
'console.log(rowId)','if(rowId >= ONE && rowId < TWO) {',
'row.style.backgroundColor = "pink";','}','}')
ui <- basicPage(
sliderInput("colorrows", "Which to color:",min = 0, max = 10, value = c(1,3)),
mainPanel(DT::dataTableOutput('mytable'))
)
server <- function(input, output,session) {
Coloring <- eventReactive(input$colorrows,{
fnc <- sub("ONE",input$colorrows[1],fnc)
fnc <- sub("TWO",input$colorrows[2],fnc)
fnc
})
output$mytable = DT::renderDataTable(
DT::datatable(mtcars, options = list(pageLength = 25,rowCallback = Coloring())
)
)
}
runApp(list(ui = ui, server = server))
Is there a way to highlight a row based on the search criteria in a data table in R Shiny??
On using Data table, we get the search bar on the top that filters the rows accordingly.. I want to highlight the part in the row which is matching the search criteria.
Thank you.
How to do datatable highlighting in R. The shiny implementation should be straight forward.
library(DT)
mtcars2 = head(mtcars[, 1:5], 20)
mtcars2$model = rownames(mtcars2)
rownames(mtcars2) = NULL
options(DT.options = list(pageLength = 5))
# global search
datatable(mtcars2, options = list(searchHighlight = TRUE, search = list(search = 'da')))
See here: R Studio DT Explanation
EDIT:
Small shiny example
server.R:
shinyServer(function(input, output) {
output$testme <- renderDataTable({
mtcars2 = head(mtcars[, 1:5], 20)
mtcars2$model = rownames(mtcars2)
rownames(mtcars2) = NULL
options(DT.options = list(pageLength = 5))
# global search
datatable(mtcars2, options = list(searchHighlight = TRUE, search =
list(search = 'da')))
})
})
ui.R:
library(shiny)
library(DT)
shinyUI(fluidPage(
DT::dataTableOutput(outputId = "testme")
)
)