Jitter text/labels with position_stack

前端 未结 2 846
感情败类
感情败类 2021-02-13 17:32

Consider the following data.frame and chart:

library(ggplot2)
library(scales)
df <- data.frame(L=rep(LETTERS[1:2],each=4),
                 l=rep         


        
2条回答
  •  广开言路
    2021-02-13 18:20

    We can create a new Position, position_jitter_stack().

     position_jitter_stack <- function(vjust = 1, reverse = FALSE, 
                                      jitter.width = 1, jitter.height = 1,
                                      jitter.seed = NULL, offset = NULL) {
      ggproto(NULL, PositionJitterStack, vjust = vjust, reverse = reverse, 
              jitter.width = jitter.width, jitter.height = jitter.height,
              jitter.seed = jitter.seed, offset = offset)
    }
    
    PositionJitterStack <- ggproto("PositionJitterStack", PositionStack,
      type = NULL,
      vjust = 1,
      fill = FALSE,
      reverse = FALSE,
      jitter.height = 1,
      jitter.width = 1,
      jitter.seed = NULL,
      offset = 1,
    
      setup_params = function(self, data) {
        list(
          var = self$var %||% ggplot2:::stack_var(data),
          fill = self$fill,
          vjust = self$vjust,
          reverse = self$reverse,
          jitter.height = self$jitter.height,
          jitter.width = self$jitter.width,
          jitter.seed = self$jitter.seed,
          offset = self$offset
        )
      },
    
      setup_data = function(self, data, params) {
        data <- PositionStack$setup_data(data, params)
        if (!is.null(params$offset)) {
          data$to_jitter <- sapply(seq(nrow(data)), function(i) {
            any(abs(data$y[-i] - data$y[i]) <= params$offset)
          })
        } else {
          data$to_jitter <- TRUE
          }
        data
      },
    
      compute_panel = function(data, params, scales) {
        data <- PositionStack$compute_panel(data, params, scales)
    
        jitter_df <- data.frame(width = params$jitter.width,
                                height = params$jitter.height)
    
        if (!is.null(params$jitter.seed)) jitter_df$seed = params$jitter.seed
        jitter_positions <- PositionJitter$compute_layer(
          data[data$to_jitter, c("x", "y")],
          jitter_df
        )
    
        data$x[data$to_jitter] <- jitter_positions$x
        data$y[data$to_jitter] <- jitter_positions$y
    
        data
      }
    )
    

    And plot it ...

    ggplot(df,aes(x=L,y=val,fill=l)) +
      geom_bar(stat="identity") +
      geom_text(aes(label=percent(val/100)),
                position = position_jitter_stack(vjust =0.5,
                 jitter.height = 0.1,
                 jitter.width =  0.3, offset = 1))
    

    Alternatively, we could write a very simple repel function.

    library(rlang)
    
    position_stack_repel <- function(vjust = 1, reverse = FALSE, 
                                     offset = 1) {
      ggproto(NULL, PositionStackRepel, vjust = vjust, reverse = reverse,
              offset = offset)
    }
    
    PositionStackRepel <- ggproto("PositionStackRepel", PositionStack,
      type = NULL,
      vjust = 1,
      fill = FALSE,
      reverse = FALSE,
      offset = 1,
    
      setup_params = function(self, data) {
        list(
          var = self$var %||% ggplot2:::stack_var(data),
          fill = self$fill,
          vjust = self$vjust,
          reverse = self$reverse,
          offset = self$offset
        )
      },
    
      setup_data = function(self, data, params) {
        data <- PositionStack$setup_data(data, params)
        data <- data[order(data$x), ]
        data$to_repel <- unlist(by(data, data$x, function(x) {
          sapply(seq(nrow(x)), function(i) {
            (x$y[i]) / sum(x$y) < 0.1 & (
              (if (i != 1) (x$y[i-1] / sum(x$y)) < 0.1 else FALSE) | (
                if (i != nrow(x)) (x$y[i+1] / sum(x$y)) < 0.1 else FALSE))
          })
        }))
        data
      },
    
      compute_panel = function(data, params, scales) {
        data <- PositionStack$compute_panel(data, params, scales)
        data[data$to_repel, "x"] <- unlist(
          by(data[data$to_repel, ], data[data$to_repel, ]$x, 
             function(x) seq(x$x[1] - 0.3, x$x[1] + 0.3, length.out = nrow(x))))
        data
      }
    )
    

    Plot it:

    ggplot(df,aes(x=L,y=val,fill=l)) +
      geom_bar(stat="identity") +
      geom_text(aes(label=percent(val/100)),
                position = position_stack_repel(vjust =0.5))
    

提交回复
热议问题