3

Indian style thousand separators are used like this. First separator at 3 digits (thousands) but thereafter separator at every two digits.

1
10
100
1,000
10,000
1,00,000
10,00,000
1,00,00,000
10,00,00,000

I know that I can change/format axes in ggplot2 chart by using scale_y_continuous(labels = scales::comma)

But how can I change the thousand separator placeholder in r ggplot2 chart axes according to Indian formats as described above.

A sample example

library(tidyverse)
iris %>%
  mutate(Petal.Length= Petal.Length*100000) %>%
  ggplot(aes(x= Species, y = Petal.Length)) +
  geom_col() +
  scale_y_continuous(labels = scales::comma)

Created on 2021-06-28 by the reprex package (v2.0.0)

AnilGoyal
  • 25,297
  • 4
  • 27
  • 45
  • Not R, but I guess you could re-use the regex from this post: https://stackoverflow.com/q/16037165/680068 – zx8754 Jun 28 '21 at 09:13

3 Answers3

9

You can define your own formatting function and supply this as the labels argument to scale_y_continuous(). Here is an example using the base prettyNum() function:

library(ggplot2)

indian_comma <- function(x) {
  
  # Format the number, first dividing by 10 to place the first comma at the 
  # right point
  out <- prettyNum(x %/% 10, big.interval = 2L, big.mark = ",", scientific = FALSE)
  out <- paste0(out, x %% 10)
  
  # Switch between formatted and un-formatted depending on the size of the
  # number
  ifelse(
    x < 1000, x, out
  )
  
}

iris %>%
  mutate(Petal.Length= Petal.Length*100000) %>%
  ggplot(aes(x= Species, y = Petal.Length)) +
  geom_col() +
  scale_y_continuous(labels = indian_comma)

Indian comma example

EDIT

The following function uses a regular expression and I think it's much nicer:

indian_comma <- function(x) {
  x <- prettyNum(x, scientific = FALSE)
  gsub("(?<!^)(?=(\\d{2})+\\d$)", ",", x, perl = TRUE)
}
wurli
  • 2,314
  • 10
  • 17
  • 3
    I like your function better than mine, except for two things: I'd use `x %/% 10` to drop the last digit, then `paste0(x, x %% 10)` to put it back. That way if someone chose breaks that weren't round numbers it would still work. – user2554330 Jun 28 '21 at 09:54
  • 1
    @BluVoxe, very good strategy. I am editing your answer to correct a minor bug there. :) – AnilGoyal Jun 28 '21 at 10:29
2

You can set the breaks for the y-axis, and then label them according to the Indian system:

iris %>%
    mutate(Petal.Length= Petal.Length*100000) %>%
    ggplot(aes(x= Species, y = Petal.Length)) +
    geom_col() +
    scale_y_continuous(breaks = c(0,10000000,20000000),labels = c("0","1,00,00000","2,00,00,000"))
MonJeanJean
  • 2,876
  • 1
  • 4
  • 20
2

This post: https://stackoverflow.com/a/62037466/2554330 defines a function format2(). It doesn't quite work as is, but with some minor fixes, this works:

format2 <- function(x, ..., big.mark = "", big.interval = c(3L, 2L), decimal.mark = ".") {
  intervene <- !is.na(x) && x > 0 && (log(abs(x), 10) >= sum(big.interval)) && nzchar(big.mark)
  cl <- match.call()
  cl[[1]] <- substitute(format)
  if (intervene) {
    cl$x <- x %/% 10^big.interval[1]
    cl$big.interval <- big.interval[2]
    bigx <- eval.parent(cl)
    cl$x <- x 
    cl$big.interval <- big.interval[1]
    mostx <- eval.parent(cl)
    mostx <- 
      substr(mostx,
             1L + nchar(x %/% 10^big.interval[1]) +
               trunc(trunc(log(abs(x %/% 10^big.interval[1]), 10L)) / big.interval[1]),
             nchar(mostx))
    return( paste0(bigx, mostx) )
  } else eval.parent(cl)
}

f <- function(x) {
  sapply(x, format2, scientific = FALSE, big.mark = ",")
}

library(tidyverse)
iris %>%
  mutate(Petal.Length= Petal.Length*100000) %>%
  ggplot(aes(x= Species, y = Petal.Length)) +
  geom_col() +
  scale_y_continuous(labels = f)

Created on 2021-06-28 by the reprex package (v2.0.0)

user2554330
  • 37,248
  • 4
  • 43
  • 90