doomlab / MOTE

Magnitude of the Effect - An Effect Size and CI calculator
http://www.statstools.com
16 stars 13 forks source link

Use S3 methods for effect size functions #3

Open crsh opened 6 years ago

crsh commented 6 years ago

Hi Erin,

building on our other discussion, here some thoughts on how to handle different output objects. I would suggest to define S3 methods to do this to avoid "having to write it 10 times". Currently, you suggest to do the following for every effect:

##run my analysis
aov_saved = summary(aov(mpg~cyl, data = mtcars))
##process through MOTE
eta_saved = eta.anova(aov_saved[[1]][["Df"]][1], aov_saved[[1]][["Df"]][2], aov_saved[[1]][["F value"]][1], a = .05)

I would suggest the following:

# Define S3 generic
eta_anova <- function(x, ...) {
  UseMethod("eta_anova", x)
}

# Define default method for users who want to pass statistics (e.g., from publications)
eta_anova.default <- function (x, dfm, dfe, a = .05) {
  eta <- (dfm * x) / (dfm * x + dfe)
  ncpboth <- conf.limits.ncf(x, df.1 = dfm, df.2 = dfe, conf.level = (1 - a))
  elow <- ncpboth$Lower.Limit / (ncpboth$Lower.Limit + dfm + dfe + 1)
  ehigh <- ncpboth$Upper.Limit / (ncpboth$Upper.Limit + dfm + dfe + 1)
  p <- pf(x, dfm, dfe, lower.tail = F)

  output <- data.frame(es = eta,
                       es_ll = elow,
                       es_ul = ehigh,
                       Fvalue = x,
                       dfm = dfm,
                       dfe = dfe)
  attr(output, "model") <- c(F = x, dfm = dfm, dfe = dfe)
  class(output) <- c("mote_anova", class(output))
  return(output)
}

# Define methods for analysis objects
eta_anova.aov <- function(x, ...) {
  x_summary <- summary(x)
  eta_anova(x_summary, ...)
}

eta_anova.summary.aov <- function(x, a = .05) {
  x_df <- x[[1]]

  output <- data.frame()
  for(i in 1:(nrow(x_df) - 1)) {
    output <- rbind(
      output,
      eta_anova(x_df[["F value"]][i], x_df[["Df"]][i], x_df[["Df"]][nrow(x_df)], a = a)
    )
  }
  output <- cbind(effects = rownames(x_df)[1:(nrow(x_df) - 1)], output)
  attr(output, "model") <- x
  class(output) <- c("mote_anova", class(output))
  output
}

# Run my analysis
aov_saved = aov(mpg~cyl * disp, data = mtcars)
aov_summary = summary(aov_saved)

# Process efficiently through MOTE
eta_anova(aov_summary[[1]][["F value"]][1], aov_summary[[1]][["Df"]][1], aov_summary[[1]][["Df"]][4], a = .05)
eta_anova(aov_saved)
eta_anova(aov_summary)

Adding the input object to the output facilitates interoperability with papaja.

Does that make sense? If so feel free to use this (untested) code (a contributor note would be appreciated).

doomlab commented 6 years ago

@crsh - yes! Thank you for the working example - I've already thought about how to apply that to t-tests. I will play around with these ideas and see what I can do. Will definitely add a contributor note once I get my readme and manual in order.