Как написать метод формулы, который преобразует длинные в широкие - PullRequest
0 голосов
/ 22 мая 2018

В пакете twoway у меня есть метод twoway.default(), который берет матрицу или фрейм данных и применяет методы Тьюки для анализа таблиц twoway.

Пример:

> data(taskRT)
> taskRT
       topic1 topic2 topic3 topic4
Easy     2.43   3.12   3.68   4.04
Medium   3.41   3.91   4.07   5.10
Hard     4.21   4.65   5.87   5.69
> twoway(taskRT)

Mean decomposition (Dataset: "taskRT")
Residuals bordered by row effects, column effects, and overall

         topic1    topic2    topic3    topic4      roweff   
       + --------- --------- --------- --------- + ---------
Easy   | -0.055833  0.090833  0.004167 -0.039167 : -0.864167
Medium |  0.119167  0.075833 -0.410833  0.215833 : -0.059167
Hard   | -0.063333 -0.166667  0.406667 -0.176667 :  0.923333
       + ......... ......... ......... ......... + .........
coleff | -0.831667 -0.288333  0.358333  0.761667 :  4.181667

Я хочу расширить это с помощью метода формулы, который принимает фрейм данных и формулу вида response ~ row + column, изменяет его с длинного на широкий и затем вызываетметод по умолчанию.Я знаю несколько способов сделать это непосредственно в консоли, но я не могу заставить какой-либо из них работать в функции метода формул.

Таким образом, для этих данных в длинном формате со значением ячейкис именем RT и переменными строки и столбца как task и topic, я хотел бы получить те же результаты с вызовом

twoway(RT ~ task + topic, data=long)

На верхнем уровне, в консоли IЭто можно сделать разными способами, начиная с версии long тех же данных.

library(reshape2)
long <- melt(as.matrix(taskRT))
colnames(long) <- c("task", "topic", "RT")

Преобразовать обратно в широкоформатный формат и вызвать twoway() для этого:

# convert wide to long: dcast
(wide <- dcast(long, task ~ topic, value.var="RT"))
twoway(wide[,-1])

# tidyr::spread
library(tidyr)
(wide <- spread(long, key=topic, value=RT))
twoway(wide[,-1])

# base, unstack
wide <- unstack(long, form = RT ~ topic)
rownames(wide) <- unique(long$task)
twoway(wide)

Ниже приведен начальный эскиз метода twoway.formula.У меня проблема в том, что я не могу понять, как использовать результаты синтаксического анализа объекта формулы и соответствующего фрейма данных в функции для создания вызова функции, который привел бы к подходящей широкой матрице или фрейму данных.для перехода к методу по умолчанию.До сих пор я пробовал различные формы dcast внутри функции, показанные в виде комментариев, ни одна из которых не доставляет мне радости.

#' Initial sketch for a twoway formula method
#'
#' Doesn't do anything useful yet, but the idea is to be able to use a
#' formula for a twoway table in long form, e.g.,
#' twoway(response ~ row + col, data=mydata)
#'
#' @param formula A formula of the form \code{response ~ rowvar + colVAR}
#' @param data The name of the data set
#' @param subset An expression to subset the data (unused)
#' @param na.action What to do with NAs? (unused)
#' @param ... other arguments, passed down
#' @importFrom stats terms
#'
twoway.formula <- function(formula, data, subset, na.action, ...) {

  if (missing(formula) || !inherits(formula, "formula"))
    stop("'formula' missing or incorrect")
  if (length(formula) != 3L)
    stop("'formula' must have both left and right hand sides")
  tt <- if (is.data.frame(data))
    terms(formula, data = data)
  else terms(formula)
  if (any(attr(tt, "order") > 1))
    stop("interactions are not allowed")

  rvar <- attr(terms(formula[-2L]), "term.labels")
  lvar <- attr(terms(formula[-3L]), "term.labels")
  rhs.has.dot <- any(rvar == ".")
  lhs.has.dot <- any(lvar == ".")
  if (lhs.has.dot || rhs.has.dot)
    stop("'formula' has '.' in left or right hand sides")
  m <- match.call(expand.dots = FALSE)
  edata <- eval(m$data, parent.frame())
  lhs <- formula[[2]]
  rhs <- formula[[3]]

  #  wide <- dcast(data=edata, formula=as.formula(rhs), value.var=lhs )
  #  wide <- dcast(data=edata, value.var=lhs)
  #  wide <- dcast(data=edata, rvar[1] ~ rvar[2], value.var=cvar)
  #  wide <- dcast(data=edata, list(.(rvar[1], .(rvar[2], .(cvar)))))
#browser()
  stop("The formula method is not yet implemented.")

  # call the default method on the wide data set
  twoway(wide)
}

Кто-нибудь может помочь?

1 Ответ

0 голосов
/ 22 мая 2018

Использование tidyverse ...

library(tibble)
library(tidyr)
library(dplyr)

twoway.formula <- function(formula, data, subset, na.action, ...) {

  if (missing(formula) || !inherits(formula, "formula"))
    stop("'formula' missing or incorrect")
  if (length(formula) != 3L)
    stop("'formula' must have both left and right hand sides")
  tt <- if (is.data.frame(data)) {
    terms(formula, data = data)
  } else { terms(formula) }
  if (any(attr(tt, "order") > 1))
    stop("interactions are not allowed")

  rvar <- attr(terms(formula[-2L]), "term.labels")
  lvar <- attr(terms(formula[-3L]), "term.labels")
  rhs.has.dot <- any(rvar == ".")
  lhs.has.dot <- any(lvar == ".")
  if (lhs.has.dot || rhs.has.dot)
    stop("'formula' has '.' in left or right hand sides")
  m <- match.call(expand.dots = FALSE)
  edata <- eval(m$data, parent.frame())
  lhs <- formula[[2]]
  rhs <- formula[[3]]

  wide <- 
    edata %>% 
    select(one_of(rvar, lvar)) %>% 
    spread(key = rvar[2], value = lvar) %>% 
    column_to_rownames(rvar[1])

  # call the default method on the wide data set
  twoway(wide)
}


library(twoway)
data(taskRT)

library(reshape2)
long <- melt(as.matrix(taskRT))
colnames(long) <- c("task", "topic", "RT")

twoway(taskRT)

twoway(RT ~ task + topic, data = long)
...