glin / reactable

Interactive data tables for R
https://glin.github.io/reactable
Other
633 stars 80 forks source link

bar_chart with positive and negative values #16

Open JodyStats opened 4 years ago

JodyStats commented 4 years ago

Hi, I read through all vignettes, including Demo Cookbook, yet I cant find a bar_chart that support both negative and positive values. A sample of the figure I am looking for

negative

I learn how to plot a bar chart with positive values from your vignettes

library(reactable)
library(htmltools)

positive_negative_values <- data.frame(value = c(runif(5), -runif(5)))
#          value
# 1   0.69613639
# 2   0.19890088
# 3   0.36667802
# 4   0.00623717
# 5   0.47794897
# 6  -0.08616278
# 7  -0.78401541
# 8  -0.30227764
# 9  -0.26267277
# 10 -0.48009791

# Render a bar chart with a label on the left
bar_chart <- function(label, width = "100%", height = "16px", fill = "#00bfc4", background = NULL) {
  bar <- div(style = list(background = fill, width = width, height = height))
  chart <- div(style = list(flexGrow = 1, marginLeft = "8px", background = background), bar)
  div(style = list(display = "flex", alignItems = "center"), label, chart)
}

reactable::reactable(positive_negative_values, columns = list(
  value = colDef(
    cell = function(value) {
      width <- paste0(value / max(iris$Sepal.Length) * 100, "%")
      label <- format(value, nsmall = 5)
      bar_chart(label, width = width)
    })
))

jpgg

Please kindly shed some light on how to tweak the code to get bar chart with negative and positive values. Thank you in advance!

glin commented 4 years ago

To create a negative bar chart, you could use the existing bar chart but just align everything to the right. Then a negative/positive bar chart could be made from 2 equal-width containers, with the negative chart on the left, and the positive chart on the right.

Using more CSS flexbox, the HTML might look something like this:

<div style="display: flex;">
  <div style="flex: 1 1 0;">
    <!-- negative bar chart -->
  </div>
  <div style="flex: 1 1 0;">
    <!-- positive bar chart -->
  </div>
</div>

Here's an example where I've tweaked the bar_chart() function to create a negative or positive bar. The function now takes a numeric value rather than a width percentage, where max_value is the maximum value (1 in the case of percentages).

library(reactable)
library(htmltools)

set.seed(5)

positive_negative_values <- data.frame(
  company = sprintf("Company%02d", 1:10),
  value = c(runif(5), -runif(5))
)

# Render a bar chart with positive and negative values
bar_chart_pos_neg <- function(label, value, max_value = 1, height = "16px",
                              pos_fill = "#02aa5c", neg_fill = "#ff121a") {
  neg_chart <- div(style = list(flex = "1 1 0"))
  pos_chart <- div(style = list(flex = "1 1 0"))
  width <- paste0(abs(value / max_value) * 100, "%")

  if (value < 0) {
    bar <- div(style = list(marginLeft = "8px", background = neg_fill, width = width, height = height))
    chart <- div(style = list(display = "flex", alignItems = "center", justifyContent = "flex-end"), label, bar)
    neg_chart <- tagAppendChild(neg_chart, chart)
  } else {
    bar <- div(style = list(marginRight = "8px", background = pos_fill, width = width, height = height))
    chart <- div(style = list(display = "flex", alignItems = "center"), bar, label)
    pos_chart <- tagAppendChild(pos_chart, chart)
  }

  div(style = list(display = "flex"), neg_chart, pos_chart)
}

reactable(positive_negative_values, columns = list(
  company = colDef(minWidth = 100),
  value = colDef(
    cell = function(value) {
      label <- paste0(round(value * 100), "%")
      bar_chart_pos_neg(label, value)
    },
    minWidth = 400
  )
))

screenshot of table

I added this example to the demo cookbook with slight changes: https://glin.github.io/reactable/articles/cookbook/cookbook.html#positive-and-negative-values

amberalpha commented 3 years ago

This is very ingenious. I wonder if there is a less laborious way of achieving the objective (+ve & -ve bars) now?