Search code examples
rggplot2plotlytooltipggplotly

ggplotly: format numbers in tooltip when part of a function


I used ggplot2 and ggplotly to compose a density chart of a given variable (population per census tract, in two groups). The tooltip shows six decimal places for the variable:

persons_per_census_tract: 16.684932

However, I want it to show integers:

persons_per_census_tract: 17

the plot as it looks now

Code:

### create dummy data
# normal distribution
rnorm1 <- rnorm(200, mean=10, sd=3)

# dataframe with the urban/rural labels and normal distribution
data <- data.frame(spatial_type = rep(c("1: urban", "2: rural"), each = 200 / 2),
                   persons_per_census_tract = rnorm1)


# define a function to combine several histograms in one plot, inspired by 
# https://stackoverflow.com/a/53680101
# here already defining some parts of the appearance (y axis, outline size, colour via scale_fill_manual)
plot_multi_histogram <- function(df, feature, label_column) {
  plt <- ggplot(df, aes(x=!!sym(feature),
                        fill=!!sym(label_column)),
                text=paste(scales::percent(!!sym(feature)), "persons/census tract")) +
    scale_fill_manual(
      values=c("#9BB7B0", "#2DC6D6"),
      breaks=c("1: urban", "2: rural")) +   
    geom_density(alpha=.7, size = .2) +
    scale_y_continuous(labels = scales::unit_format(scale = 100, unit = "%")) +  
    labs(x=feature, y="density")
  plt <- plt + guides(fill=guide_legend(title=label_column))
  
  # Convert ggplot to a plotly object
  plt <- ggplotly(plt, tooltip=c(feature))
  
  return(plt)
}


# revert factor order of spatial_type to determine rendering order
# https://stackoverflow.com/a/43784645
data$spatial_type <- factor(data$spatial_type, c("2: rural", "1: urban"))

# Apply the function to create the plot based on dummy data
density.p <- plot_multi_histogram(data, 'persons_per_census_tract', 'spatial_type')

# print the plot
plotly::ggplotly(density.p)

I've tried to include several options into the code that defines the function plot_multi_histogram <- ...

, e.g. scales::percent

text=paste(scales::percent(!!sym(feature)), "persons/census tract")

or as.integer

text=paste(as.integer(!!sym(feature)), "persons/census tract")

or sprintf() or round().

I suppose the rounding attempts fail because they are embedded in the function. Any ideas? Thx!


Solution

  • First issue is that you have set tooltip=feature. Hence, the value for the tooltip is taken from the raw feature column of your dataset. That's the reason why changing anything in text=... has no effect. Second, you have put text= outside of aes() in ggplot(). Well, in case of ggplot() all arguments outside of aes() will be silently ignored. If you want create a tooltip from a data column using !!sym() you have to do that inside aes().

    Unfortunately simply moving text= inside aes() will also not fix the issue as this works only fine in case of geoms using stat="identity". Perhaps there is a more straightforward approach I'm not aware of. But one fix would be to set and format the tooltip text using after_stat and of course by setting tooltip="text" in ggplotly:

    library(ggplot2)
    library(plotly)
    
    set.seed(123)
    
    plot_multi_histogram <- function(df, feature, label_column) {
      plt <- ggplot(df, aes(
        x = !!sym(feature),
        fill = !!sym(label_column)
      )) +
        scale_fill_manual(
          values = c("#9BB7B0", "#2DC6D6"),
          breaks = c("1: urban", "2: rural")
        ) +
        geom_density(
          aes(text = after_stat(
            paste(
              scales::number(x, accuracy = 1), "persons/census tract"
            )
          )),
          alpha = .7, size = .2
        ) +
        scale_y_continuous(labels = scales::unit_format(scale = 100, unit = "%")) +
        labs(x = feature, y = "density")
      plt <- plt + guides(fill = guide_legend(title = label_column))
    
      ggplotly(plt, tooltip = "text")
    }
    
    data$spatial_type <- factor(data$spatial_type, c("2: rural", "1: urban"))
    
    plot_multi_histogram(data, "persons_per_census_tract", "spatial_type")
    

    enter image description here