Sorting with NA using datatable function in DT package - r

I am trying to create an html table using the datatable function in the DT package so that when I sort the data in R markdown, missing rows are sorted after the highest number.
For example, in the following table, when I sort by "age" in the markdown file, I would like the row with NA to be listed last so that the order is 14,15,21,NA.
dat <- data.frame("Age" = c(21,15,NA,14),
"Name" = c("John","Dora", "Max", "Sam"),
"Gender" = c("M","F","M",NA))
DT::datatable(dat, filter = c("top"))
I have tried using "na.last = TRUE" and this works when the datatable initially prints, however when clicking the column to sort, NA is still before 14.
Any help would be much appreciated!

With the render columnwise option, you can set the value of the missing values during the sorting:
library(DT)
dat <- data.frame("Age" = c(21,15,NA,14),
"Name" = c("John","Dora", "Max", "Sam"),
"Gender" = c("M","F","M",NA))
render <- JS(
"function(data, type, row) {",
" if(type === 'sort' && data === null) {",
" return 999999;",
" }",
" return data;",
"}"
)
datatable(
dat,
filter = "top",
options = list(
columnDefs = list(
list(targets = 1, render = render)
)
)
)

Related

How to preserve order in shiny app using datatables when sorting in the app?

The first column of the datatable consits of names, while the second column uses characters which are a combination of numeric with comma delimiters, and characters for other values, for example, "1,000" , "2,000", "19,000", "Data missing", "Data suppressed". Using type = "num-fmt" I am able to get the datatable to display correctly when I run it as a function by itself, i.e. "1,000", "2,000", "19,000", and when using sort in the Rstudio terminal its ordering is correct, and as such when first displayed in the R shiny app it works. However, when using the sort options in the shiny interface, the ordering no longer works correctly, i.e. "1,000" , "19,000" , "2,000".
My understanding is that I must do the sorting in the server, or use java script, but I don't know how.
ui <- dashboardPage(
box(title = "Industries from selected region",
status = "danger",
solidHeader = TRUE,
DT::dataTableOutput("industry_tbl"),
width = 6)
)
server <- function(input, output, session) {
values <- reactiveValues(direction = "Exports",
year = "2020",
partner_country = "Spain",
industry = "Mining",
home_country = "UK")
output$industry_tbl<- DT::renderDT({
industry_table_server(new_data,
values$year,
values$partner_country,
values$direction,
values$home_country)
})
function:
industry_table_server <- function(dataset,
selected_year,
selected_country,
selected_direction,
selected_region){
this_selection <- dplyr::filter(dataset,
Year == selected_year,
Country == selected_country,
Direction == selected_direction,
`Area name` == selected_region) %>%
select(Industry, value)
DT::datatable(this_selection ,
colnames = c("Industry",
paste0("£millions")),
filter = "none",
rownames = TRUE,
extensions = c('Buttons'),
options = list(
dom = 'Bftip',
buttons = c('copy', 'excel', 'print'),
searchHighlight = TRUE,
searchDelay = 0,
selection = "single",
pageLength = 10, # Shows 10 results
lengthMenu = c(5, 10),
columnDefs = list(list(className = 'dt-right', targets = c(0,2)), list(targets = c(2), type = "num-fmt"))
)
)
} ```
As said in the datatables website, regarding the type option:
Please note that if you are using server-side processing this option has no effect since the ordering and search actions are performed by a server-side script.
So you have to set server = FALSE in renderDT:
output$industry_tbl <- renderDT({
industry_table_server(new_data,
values$year,
values$partner_country,
values$direction,
values$home_country)
}, server = FALSE)

How to style reactable cell background with custom grouping select

I have some data I'm trying to present using reactable. I am styling the background of cells based on the value. There are a number of groups in the data which are useful, but the groups themselves do not have an aggregated value that is useful.
The issue I'm facing is that when the data is grouped with the custom grouping select, the table will retain the style of the first few rows of data so the background is coloured. I would like it to be blank for the grouped row.
In the example below, when grouping by group you'll notice that A and C have the background coloured, inheriting the style from rows 1 and 3 in the data. I could imagine a hacky way of organizing the data so only non-stylized rows come first, but that is not really appropriate as the data would be too disorganized at initial presentation.
Is there a way to strip the style when grouped, but retain it for the rows with values?
library(reactable)
library(htmltools)
set.seed(1)
data <- data.frame(
group = rep(c("A", "B", "C"), each = 5),
value = rnorm(15)
)
htmltools::browsable(
tagList(
div(tags$label("Group by", `for` = "tab")),
tags$select(
id = "tab",
onchange = "Reactable.setGroupBy('tab', this.value ? [this.value] : [])",
tags$option("None", value = ""),
tags$option("Group", value = "group"),
),
reactable(
data,
columns = list(
value = colDef(style = function(value){
if (value < 0) list(background = "#ffa500")
})
),
defaultPageSize = 15,
elementId = "tab"
)
)
)
I found a way using JavaScript. I've changed the variable value to num in the example below so it's more clear how to apply the function.
The grouping is done via JavaScript in the browser, so there isn't a way to control group styling in R as far as I'm aware.
library(reactable)
library(htmltools)
set.seed(1)
data <- data.frame(
group = rep(c("A","B","C"), each = 5),
num = rnorm(15)
)
htmltools::browsable(
tagList(
div(tags$label("Group by", `for` = "tab")),
tags$select(
id = "tab",
onchange = "Reactable.setGroupBy('tab', this.value ? [this.value] : [])",
tags$option("None", value = ""),
tags$option("Group", value = "group"),
),
reactable(
data,
columns = list(
num = colDef(style = JS("function(rowInfo) {
var value = rowInfo.row['num']
if (value < 0) {
var background = '#ffa500'
}
return {background: background}
}"))
),
defaultPageSize = 15,
elementId = "tab"
)
)
)

Filter parameter from DT doesn't work with all the columns

I am using DT package and filter='top' and I realised that some columns can give you the chance to click the different options of the column, but some columns don't give you that.
Here for example, you can click the options in the "am" column. However, if you try to do the same with the column named "model", you cannot.
Does anyone know if there is way to have the same filter in the first column (model)?
The code:
library(DT)
mtcars2 = mtcars[c(1:3,9)]
mtcars2$am = factor(mtcars$am, c(0, 1), c('automatic', 'manual'))
datatable(
mtcars2, colnames = c('model' = 1),
filter = list(position = 'top', clear = FALSE),
options = list(
pageLength = 10
)
)
Thanks in advance
Regards

Passing column name to target option in a shiny datatable

I'm stuck in trying to pass the column name (instead the column number) in the target option of columnDefs. The table is dynamic so I definitely need the option to target the column name. Below is a reproducible example. The example is not dynamic, however.
datatable(iris[c(1:20, 51:60, 101:120), ], options = list(columnDefs = list(list(
targets = 5,
render = JS(
"function(data, type, row, meta) {",
"return type === 'display' && data.length > 6 ?",
"'<span title=\"' + data + '\">' + data.substr(0, 6) + '...</span>' : data;",
"}")
))), callback = JS('table.page(3).draw(false);'))
Tried with targets = 'Species' , targets = iris$Species but they didn't work.
if you are interested in setting multiple columns to different widths, consider the following (take care that R is one based, while javascript is one based):
col_a <- which(names(dat) %in% c("column_name1", "column_name2"))
col_b <- which(names(dat) %in% c("column_name3", "column_name4"))
col_c <- which(names(dat) %in% c("column_name5"))
columnDefs = list(list(width = '30px', targets = as.list(col_a - 1)),
list(width = '80px', targets = as.list(col_b - 1)),
list(width = '200px', targets = as.list(col_c - 1)))

Show Inf in DT::datatable()

I would like to explicitly show the Inf value inside a datatable instead of a blank
iris[1, 1] <- Inf
DT::datatable(iris[1:2, ])
I don't want to turn the column info character to be able to sort the column (If I do this, sorting will be alphabetically)
Any ideas?
Edit:
I thinks it's possible to adapt this kind of code :
datatable(iris[c(1:20), ], options = list(columnDefs = list(list(
targets = 5,
render = JS(
"function(data, type, row, meta) {",
"return type === 'display' && data.length > 2 ?",
"'<span title=\"' + data + '\">' + data.substr(0, 2) + '...</span>' : data;",
"}")
))))
with #MLavoie solution, it doesn't distinct NA and Inf
df = iris
df[1,1]<-Inf
df[2,1]<-NA
DT::datatable(df)
library(DT)
DT::datatable(df[,], options = list(columnDefs = list(list(
targets = 1,
render = JS(
"function(data, type, row, meta) {",
"return data === null ? 'Inf' : data;",
"}")
))))
The solution is :
options(htmlwidgets.TOJSON_ARGS = list(na = 'string'))
iris[1,1] <- NA
iris[2,1] <- Inf
DT::datatable(head(iris))
thanks to #yihui-xie and #Jeroen at : https://github.com/rstudio/DT/issues/496#issuecomment-363867449
You can do this:
df = iris
df[1,1]<-Inf
datatable(df[1:2,], options = list(columnDefs = list(list(
targets = 1,
render = JS(
"function(data, type, row, meta) {",
"return data === null ? 'Inf' : data;",
"}")
))))
and you could also do it manually:
DT::datatable(df[1:2,], editable = TRUE)

Resources