Edit: see addition at bottom for related but simpler problem
The below R Shiny code, using package rhandsontable
, retains full decimal precision via the function row_decimals()
. As a next step I am trying to show these values as percentages while retaining full decimal precision. Everything I have tried using format()
or custom JavaScript renderer either rounds or truncates the rendered values, which I don't want. Ideally, any values with less than or equal to 2 decimal places are shown with 2 decimals (for example, 0.20, 0.212, 0.2123 shown as 20.00%, 21.20%, and 21.23%, respectively) and any values with greater than 2 decimal places show all decimals (for example, 0.123456789 shown as 12.3456789%). Any recommendations for how to do this? As illustrated in the image below.
Code:
library(shiny)
library(rhandsontable)
# Calculates decimal places for each value in specified column
row_decimals <- function(df, col_name) {
col <- df[[col_name]]
n_digits <- sapply(col, function(x) {
nchar(gsub(".*\\.|^[^.]+$", "", as.character(x)))
})
return(n_digits)
}
ui <- fluidPage(
br(),
rHandsontableOutput("myTable"),
br(),
textOutput("productOutput")
)
server <- function(input, output) {
values <- reactiveValues(data = data.frame(Values = c(0.5, 0.123456789)))
observeEvent(input$myTable, {values$data <- hot_to_r(input$myTable)})
output$myTable <- renderRHandsontable({
data <- values$data
decimals <- row_decimals(data, 'Values')
data$Values <- mapply(function(x,d){format(x, nsmall = d)},data$Values,decimals)
rhandsontable(data)
})
output$productOutput <- renderText({
product <- as.numeric(values$data$Values[1]) * as.numeric(values$data$Values[2])
paste("Product of the two rows: ", product)
})
}
shinyApp(ui = ui, server = server)
Addition to OP: running the below code using rhandsontable
, I was unable to get full precision. The problem is relying on hot_to_r(input$hottable_1)
to retrieve data from rhandsontable
, which is influenced by JavaScript-based display constraints (4 decimal places), leading to a truncated value. In the image below this is resolved by bypassing input$hottable_1
for retrieving values.
Code without full precision:
library(shiny)
library(rhandsontable)
ui <- fluidPage(
rHandsontableOutput("hottable_1")
)
server <- function(input, output, session) {
table <- reactiveVal(data.frame(Percent = 1/3))
output$hottable_1 <- renderRHandsontable({
rhandsontable(table())
})
observeEvent(input$hottable_1, {
edited_data <- hot_to_r(input$hottable_1)
print(paste("Edited data:", edited_data))
})
}
shinyApp(ui, server)
And in the image below the solution is illustrated in the bottom half:
There's a very nice JavaScript library for numbers formatting, namely d3.format. You can use it for a handsontable with the help of the numeric renderer:
library(shiny)
library(rhandsontable)
ui <- fluidPage(
tags$head(
# https://github.com/d3/d3-format
tags$script(src = "https://cdn.jsdelivr.net/npm/d3-format@3")
),
br(),
rHandsontableOutput("myTable"),
br(),
textOutput("productOutput")
)
server <- function(input, output) {
dat <- data.frame(Values = c(0.5, 0.123456789))
output$myTable <- renderRHandsontable({
rhandsontable(dat) %>%
hot_col(
1,
renderer = "function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.NumericRenderer.apply(this, arguments);
const p = Math.max(0, d3.precisionFixed(0.0005) - 2);
const f = d3.format('.' + p + '%');
td.innerHTML = f(value);
}"
)
})
output$productOutput <- renderText({
dd <- hot_to_r(input$myTable)
product <- dd$Values[1] * dd$Values[2]
paste("Product of the two rows: ", product)
})
}
shinyApp(ui = ui, server = server)
I don't understand the line p = Math.max(0, d3.precisionFixed(0.0005) - 2)
, I copied it from an example.