Использование атрибутов ' ftable` для извлечения данных

я иногда использую ftable функция исключительно для представления иерархических категорий. Однако иногда, когда таблица большая, я хотел бы дополнительно подмножество таблицы перед ее использованием.

предположим, мы начинаем с:

mytable <- ftable(Titanic, row.vars = 1:3)
mytable
##                    Survived  No Yes
## Class Sex    Age                   
## 1st   Male   Child            0   5
##              Adult          118  57
##       Female Child            0   1
##              Adult            4 140
## 2nd   Male   Child            0  11
##              Adult          154  14
##       Female Child            0  13
##              Adult           13  80
## 3rd   Male   Child           35  13
##              Adult          387  75
##       Female Child           17  14
##              Adult           89  76
## Crew  Male   Child            0   0
##              Adult          670 192
##       Female Child            0   0
##              Adult            3  20

str(mytable)
##  ftable [1:16, 1:2] 0 118 0 4 0 154 0 13 35 387 ...
##  - attr(*, "row.vars")=List of 3
##   ..$ Class: chr [1:4] "1st" "2nd" "3rd" "Crew"
##   ..$ Sex  : chr [1:2] "Male" "Female"
##   ..$ Age  : chr [1:2] "Child" "Adult"
##  - attr(*, "col.vars")=List of 1
##   ..$ Survived: chr [1:2] "No" "Yes"
## NULL

потому что нет dimnames, Я не могу извлечь данные так же, как я бы с объектом, который имеет dimnames. Например, для меня нет возможности напрямую извлечь все "дочерние" значения из " 1st" и "3-й" класс.

мой текущий подход заключается в том, чтобы преобразовать его в table, выполните извлечение, а затем преобразуйте его обратно в ftable.

пример:

mytable[c("1st", "3rd"), , "Child", ]
## Error: incorrect number of dimensions

## Only the underlying data are seen as having dims
dim(mytable)
## [1] 16  2

## I'm OK with the "Age" column being dropped in this case....
ftable(as.table(mytable)[c("1st", "3rd"), , "Child", ])
##              Survived No Yes
## Class Sex                   
## 1st   Male             0   5
##       Female           0   1
## 3rd   Male            35  13
##       Female          17  14

однако мне не нравится этот подход, потому что общий макет иногда меняется, если вы не осторожны. Сравните его со следующим, который удаляет требование о подмножестве только детей и добавляет требование о подмножестве только тех, кто не выжить:

ftable(as.table(mytable)[c("1st", "3rd"), , , "No"])
##              Age Child Adult
## Class Sex                   
## 1st   Male           0   118
##       Female         0     4
## 3rd   Male          35   387
##       Female        17    89

мне не нравится, что общая компоновка строк и столбцов изменилась. Это классический случай того, чтобы не забыть использовать drop = FALSE для поддержания размеров при извлечении одного столбца:

ftable(as.table(mytable)[c("1st", "3rd"), , , "No", drop = FALSE])
##                    Survived  No
## Class Sex    Age               
## 1st   Male   Child            0
##              Adult          118
##       Female Child            0
##              Adult            4
## 3rd   Male   Child           35
##              Adult          387
##       Female Child           17
##              Adult           89

я знаю, что есть много способы получить данные, которые я хочу, начиная с подмножества из необработанных данных, а затем делая мой ftable, но для этого вопроса предположим, что это невозможно.

конечная цель - имейте подход, который позволяет мне извлекать из ftable сохранение формата отображения вложенной иерархии "строк".

есть ли другие решения для этого? Можем ли мы использовать row.vars и col.vars атрибуты для извлечения данных из ftable и сохранить его форматирование?


мой текущий подход также не работает для иерархических столбцов, поэтому я надеюсь, что предлагаемое решение также может справиться с этими случаи.

пример:

tab2 <- ftable(Titanic, row.vars = 1:2, col.vars = 3:4)
tab2
##              Age      Child     Adult    
##              Survived    No Yes    No Yes
## Class Sex                                
## 1st   Male                0   5   118  57
##       Female              0   1     4 140
## 2nd   Male                0  11   154  14
##       Female              0  13    13  80
## 3rd   Male               35  13   387  75
##       Female             17  14    89  76
## Crew  Male                0   0   670 192
##       Female              0   0     3  20

обратите внимание на гнездование "возраст"и " выжил".

попробуйте мой текущий подход:

ftable(as.table(tab2)[c("1st", "3rd"), , , , drop = FALSE])
##                    Survived  No Yes
## Class Sex    Age                   
## 1st   Male   Child            0   5
##              Adult          118  57
##       Female Child            0   1
##              Adult            4 140
## 3rd   Male   Child           35  13
##              Adult          387  75
##       Female Child           17  14
##              Adult           89  76

я могу вернуться к тому, что я хочу с:

ftable(as.table(tab2)[c("1st", "3rd"), , , , drop = FALSE], row.vars = 1:2, col.vars = 3:4)

но я надеюсь на что-то более прямое.

2 ответов


вот что мне удалось взломать вместе с помочь С Дровосек:

replace_empty_arguments <- function(a) {
  empty_symbols <- vapply(a, function(x) {
    is.symbol(x) && identical("", as.character(x)), 0)
  } 
  a[!!empty_symbols] <- 0
  lapply(a, eval)
}

`[.ftable` <- function (inftable, ...) {
  if (!class(inftable) %in% "ftable") stop("input is not an ftable")
  tblatr <- attributes(inftable)[c("row.vars", "col.vars")]
  valslist <- replace_empty_arguments(as.list(match.call()[-(1:2)]))
  x <- sapply(valslist, function(x) identical(x, 0))
  TAB <- as.table(inftable)
  valslist[x] <- dimnames(TAB)[x]
  temp <- as.matrix(expand.grid(valslist))
  out <- ftable(
    `dimnames<-`(`dim<-`(TAB[temp], lengths(valslist)), valslist),
    row.vars = seq_along(tblatr[["row.vars"]]),
    col.vars = seq_along(tblatr[["col.vars"]]) + length(tblatr[["row.vars"]]))
  names(attributes(out)[["row.vars"]]) <- names(tblatr[["row.vars"]])
  names(attributes(out)[["col.vars"]]) <- names(tblatr[["col.vars"]])
  out
}

попробуйте с примерами из вопроса:

mytable[c("1st", "3rd"), , "Child", ]
##                    Survived No Yes
## Class Sex    Age                  
## 1st   Male   Child           0   5
##       Female Child           0   1
## 3rd   Male   Child          35  13
##       Female Child          17  14

mytable[c("1st", "3rd"), , , "No"]
##                    Survived  No
## Class Sex    Age               
## 1st   Male   Child            0
##              Adult          118
##       Female Child            0
##              Adult            4
## 3rd   Male   Child           35
##              Adult          387
##       Female Child           17
##              Adult           89

tab2[c("1st", "3rd"), , , ]
##              Age      Child     Adult    
##              Survived    No Yes    No Yes
## Class Sex                                
## 1st   Male                0   5   118  57
##       Female              0   1     4 140
## 3rd   Male               35  13   387  75
##       Female             17  14    89  76

как только данные агрегируются с частотами по комбинации факторов, как в случае с Titanic набор данных, это, пожалуй, легче подмножество исходных данных и обработать его для показа, а не манипулируя объект вывода.

Я признаю, что OP запрашивает решения с использованием ftable, но с взад и вперед в разделе комментариев, запрашивая другие идеи, я думал, что опубликую другой подход к этому вопросу, потому что он иллюстрирует способ одновременно подмножество данных и генерировать иерархическую структуру таблиц непредвиденных обстоятельств без пользовательских функций.

вот подход с использованием tables пакет, который сохраняет иерархическую структуру Titanic data, а также устранение ячеек, которые пусты, когда мы подмножество фрейма данных.

Сначала мы бросили входящих таблицу в качестве фрейма данных, чтобы мы могли подмножество его в