daattali / ggExtra

📊 Add marginal histograms to ggplot2, and more ggplot2 enhancements
http://daattali.com/shiny/ggExtra-ggMarginal-demo/
Other
383 stars 45 forks source link

Modifying axis tick marks for ggMarginal object #82

Closed IndrajeetPatil closed 6 years ago

IndrajeetPatil commented 6 years ago

Hi,

This is a figure generated with some custom code and now I want to modify the range and number of axis tick marks on the y-axis to reflect the underlying Likert scale (1-10).

image

Usually, I'd use something like- ggplot_object + coord_cartesian(ylim = c(1, 10)) + scale_y_continuous(breaks = seq(1, 10, by = 1)) But doing the same with ggMarginal object gives the following error- non-numeric argument to binary operator

Is there some way to get around this error?

Thanks.

crew102 commented 6 years ago

Can you give a fully reproducible example of code that results in the error you are talking about?

daattali commented 6 years ago

@IndrajeetPatil are you still getting this problem? If so, can you please show us the exact code that causes this problem?

IndrajeetPatil commented 6 years ago

Yes, sorry for the delay. Here is a reproducible example. Sorry for the long code, but this is the custom function I am working with, so maybe that's where something is going wrong.


library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 3.4.3
library(stats)
library(MASS)

# custom theme
theme_mprl <- function() {
  library(ggplot2)
  theme_grey() +
    theme(
      axis.title.x = element_text(size = 14, face = "bold"),
      strip.text.x = element_text(size = 14, face = "bold"),
      strip.text.y = element_text(size = 14, face = "bold"),
      strip.text = element_text(size = 14, face = "bold"),
      axis.title.y = element_text(size = 14, face = "bold"),
      axis.text.x = element_text(size = 14, face = "bold"),
      axis.text.y = element_text(size = 14, face = "bold"),
      axis.line = element_line(),
      legend.text = element_text(size = 14),
      legend.title = element_text(size = 14, face = "bold"),
      legend.title.align = 0.5,
      legend.text.align = 0.5,
      legend.key.height = unit(1, "line"),
      legend.key.width = unit(1, "line"),
      plot.margin = unit(c(1, 1, 1, 1), "lines"),
      # requires library(grid))
      panel.border = element_rect(
        colour = "black",
        fill = NA,
        size = 1
      ),
      plot.title = element_text(
        color = "black",
        size = 16,
        face = "bold",
        hjust = 0.5
      ),
      plot.subtitle = element_text(
        color = "black",
        size = 12,
        face = "bold",
        hjust = 0.5
      )
    )

}

ggscatterstats <-
  function(data = NULL,
           x,
           y,
           xlab = NULL,
           ylab = NULL,
           marginal = NULL,
           marginaltype = NULL,
           xfill = NULL,
           yfill = NULL,
           intercept = NULL,
           test = NULL,
           title = NULL,
           caption = NULL,
           k = 3) {
    # if fill colors for x and y axes are not specified, use the defaults
    if (is.null(xfill))
      xfill <- "orange"
    if (is.null(yfill))
      yfill <- "green"

    # if test to be run is not satisfied, then use default, which is robust regression from MASS package
    if (is.null(test))
      test <- "pearson"

    if (test == "pearson") {
      # running the correlation test and preparing the subtitle text
      c <- stats::cor.test(x, y, method = "pearson", exact = FALSE)
      stat_label <-
        base::substitute(
          paste(
            "Pearson's ",
            italic("r"),
            "(",
            df,
            ")",
            " = ",
            estimate,
            ", ",
            italic("p"),
            " = ",
            pvalue
          ),
          list(
            df = c$parameter,
            # degrees of freedom are always integer
            estimate = ggstatsplot::specify_decimal(c$estimate, k),
            pvalue = ggstatsplot::specify_decimal_p(c$p.value, k)
          )
        )

    }   else if (test == "spearman") {
      # running the correlation test and preparing the subtitle text
      # note that stats::cor.test doesn't give degress of freedom; it's calculated as df = (no. of pairs - 2)
      c <- stats::cor.test(x, y, method = "spearman", exact = FALSE)
      stat_label <-
        base::substitute(
          paste(
            "Spearman's ",
            italic(rho),
            "(",
            df,
            ")",
            " = ",
            estimate,
            ", ",
            italic("p"),
            " = ",
            pvalue
          ),
          list(
            df = (length(x) - 2),
            # degrees of freedom are always integer
            estimate = ggstatsplot::specify_decimal(c$estimate, k),
            pvalue = ggstatsplot::specify_decimal_p(c$p.value, k)
          )
        )

    } else if (test == "robust") {
      # running robust regression test and preparing the subtitle text
      MASS_res <-
        MASS::rlm(
          scale(y) ~ scale(x),
          maxit = 1000,
          # number of iterations
          na.action = na.omit,
          data = data
        )
      stat_label <-
        base::substitute(
          paste(
            "robust regression: estimate = ",
            estimate,
            ", ",
            italic("t"),
            "(",
            df,
            ")",
            " = ",
            t,
            ", ",
            italic("p"),
            " = ",
            pvalue
          ),
          list(
            estimate = ggstatsplot::specify_decimal(summary(MASS_res)$coefficients[[2]], k),
            t = ggstatsplot::specify_decimal(summary(MASS_res)$coefficients[[6]], k),
            df = summary(MASS_res)$df[2],
            # degrees of freedom are always integer
            pvalue = ggstatsplot::specify_decimal_p((sfsmisc::f.robftest(MASS_res))$p.value),
            k
          )
        )
      base::warning(
        "For robust regression: no. of iterations = 1000; estimate is standardized",
        noBreaks. = TRUE,
        call. = TRUE
      )
    }

    # preparing the scatterplotplot
    library(ggplot2)
    plot <- ggplot2::ggplot(data = data, mapping = aes(x = x, y = y)) +
      geom_count(
        show.legend = FALSE,
        color = "black",
        size = 3,
        alpha = 0.5,
        position = position_jitterdodge(
          jitter.width = NULL,
          jitter.height = 0.2,
          dodge.width = 0.75
        )
      ) +
      geom_smooth(method = "lm",
                  se = TRUE,
                  size = 1.5) + # default is robust linear model
      ggstatsplot::theme_mprl() +
      labs(
        x = xlab,
        y = ylab,
        title = title,
        subtitle = stat_label,
        caption = caption
      )

    # by default, if the input is NULL, then no intercept lines will be plotted

    if (is.null(intercept)) {
      plot <- plot

    } else if (intercept == "mean") {
      plot <- plot +
        geom_vline(
          xintercept = mean(x),
          linetype = "dashed",
          color = xfill,
          size = 1.2
        ) +
        geom_hline(
          yintercept = mean(y),
          linetype = "dashed",
          color = yfill,
          size = 1.2
        )

    } else if (intercept == "median") {
      plot <- plot +
        geom_vline(
          xintercept = mean(x),
          linetype = "dashed",
          color = xfill,
          size = 1.2
        ) +
        geom_hline(
          yintercept = mean(y),
          linetype = "dashed",
          color = yfill,
          size = 1.2
        )

    }

    # if marginal should be plotted or not is not specified, it will be plotted by default
    if (is.null(marginal))
      marginal <- TRUE

    if (isTRUE(marginal)) {
      # if ggmarginal marginaltype has not been specified, go to this default
      if (is.null(marginaltype))
        marginaltype <- "histogram"

      # creating the ggMarginal plot of a given marginaltype
      plot <-
        ggExtra::ggMarginal(
          plot,
          type = marginaltype,
          xparams = list(fill = xfill, col = "black"),
          yparams = list(fill = yfill, col = "black")
        )
    }

    return(plot)

  }

# making the plot
ggscatterstats(data = iris, x = iris$Sepal.Length, y = iris$Sepal.Width)
#> Warning: position_jitterdodge requires non-overlapping x intervals
#> Warning: The plyr::rename operation has created duplicates for the
#> following name(s): (`colour`)

#> Warning: The plyr::rename operation has created duplicates for the
#> following name(s): (`colour`)
#> Warning: position_jitterdodge requires non-overlapping x intervals


# assigning it to plot and making modification to the plot
plot <- ggscatterstats(data = iris, x = iris$Sepal.Length, y = iris$Sepal.Width)
#> Warning: position_jitterdodge requires non-overlapping x intervals
#> Warning: The plyr::rename operation has created duplicates for the
#> following name(s): (`colour`)

#> Warning: The plyr::rename operation has created duplicates for the
#> following name(s): (`colour`)
#> Warning: position_jitterdodge requires non-overlapping x intervals
plot + coord_cartesian(xlim = c(1, 10)) + scale_x_continuous(breaks = seq(1, 10, by = 1))
#> Error in plot + coord_cartesian(xlim = c(1, 10)): non-numeric argument to binary operator

Created on 2018-01-22 by the reprex package (v0.1.1.9000).

crew102 commented 6 years ago

Hi @IndrajeetPatil, the problem here is that you're trying to modify a ggMarginal plot with + coord_cartesian(xlim = c(1, 10)) + scale_x_continuous(breaks = seq(1, 10, by = 1)) (you can't modify a plot created by ggMarginal in the same way you can with those created by ggplot2). If you want to modify the scatter plot with something like + coord_cartesian(xlim = c(1, 10)) + scale_x_continuous(breaks = seq(1, 10, by = 1)), you have to do it before you create the ggMarginal plot, not after. Does that make sense?

IndrajeetPatil commented 6 years ago

Ah, I was afraid of that. The problem is that this function is a part of a package I am working on and the user won't have access to this code. I don't want the user to lose any ggplot2 functionality, but I guess this function will have this limitation and there is no way around it.

IndrajeetPatil commented 6 years ago

You can close this thread by the way.