Search code examples
rcontingency

How do table and ftable differ?


The documentation for ftable tells us that "ftable creates ‘flat’ contingency tables". However, the meaning of this isn't getting through to me. I've placed two examples below, but they look so similar that I feel like I'm completely missing the distinction between table and ftable. Am I ignorant of some critical programming or statistical idea?

> ftable(mtcars[c("cyl", "vs", "am", "gear")])
          gear  3  4  5
cyl vs am              
4   0  0        0  0  0
       1        0  0  1
    1  0        1  2  0
       1        0  6  1
6   0  0        0  0  0
       1        0  2  1
    1  0        2  2  0
       1        0  0  0
8   0  0       12  0  0
       1        0  0  2
    1  0        0  0  0
       1        0  0  0
> table(mtcars[c("cyl", "vs", "am", "gear")])
, , am = 0, gear = 3

   vs
cyl  0  1
  4  0  1
  6  0  2
  8 12  0

, , am = 1, gear = 3

   vs
cyl  0  1
  4  0  0
  6  0  0
  8  0  0

, , am = 0, gear = 4

   vs
cyl  0  1
  4  0  2
  6  0  2
  8  0  0

, , am = 1, gear = 4

   vs
cyl  0  1
  4  0  6
  6  2  0
  8  0  0

, , am = 0, gear = 5

   vs
cyl  0  1
  4  0  0
  6  0  0
  8  0  0

, , am = 1, gear = 5

   vs
cyl  0  1
  4  1  1
  6  1  0
  8  2  0

My suspicion is that it means "flat" as in "flattern a nested list", but if that was the case, then I'm not sure why I cannot feed exactly the same arguments to ftable as I can table. For example, ftable(Titanic, row.vars = 1:3) is valid, but table(Titanic, row.vars = 1:3) throws an error about the arguments having non-equal lengths.


Solution

  • If we look at the structure, it is evident from the dim attributes

    tbl1 <- table(mtcars[c("cyl", "vs", "am", "gear")])
    
    str(tbl1)
     'table' int [1:3, 1:2, 1:2, 1:3] 0 0 12 1 2 0 0 0 0 0 ...
     - attr(*, "dimnames")=List of 4
      ..$ cyl : chr [1:3] "4" "6" "8"
      ..$ vs  : chr [1:2] "0" "1"
      ..$ am  : chr [1:2] "0" "1"
      ..$ gear: chr [1:3] "3" "4" "5"
    
    ftbl1 <- ftable(mtcars[c("cyl", "vs", "am", "gear")])
    str(ftbl1)
     'ftable' int [1:12, 1:3] 0 0 1 0 0 0 2 0 12 0 ...
     - attr(*, "row.vars")=List of 3
      ..$ cyl: chr [1:3] "4" "6" "8"
      ..$ vs : chr [1:2] "0" "1"
      ..$ am : chr [1:2] "0" "1"
     - attr(*, "col.vars")=List of 1
      ..$ gear: chr [1:3] "3" "4" "5"
    

    i.e. flat table is 2D, while the table is a 4D array

    dim(tbl1)
    #[1] 3 2 2 3
    
    dim(ftbl1)
    #[1] 12  3
    

    Note that both of them are arrays, and an array is also a vector with some dim attributes.

    Both are not lists. It is the attributes that make them different and how they are arranged. e.g. if we remove the attributes, they are just vectors arranged in different order of values

    c(ftbl1)
    #[1]  0  0  1  0  0  0  2  0 12  0  0  0  0  0  2  6  0  2  2  0  0  0  0  0  0  1  0  1  0  1  0  0  0  2  0  0
    c(tbl1)
    #[1]  0  0 12  1  2  0  0  0  0  0  0  0  0  0  0  2  2  0  0  2  0  6  0  0  0  0  0  0  0  0  1  1  2  1  0  0
    

    Check if it is a list

    is.list(tbl1)
    #[1] FALSE
    is.list(ftbl1)
    #[1] FALSE
    

    Regarding the error in table, it is just that it doesn't have the row.vars argument if we check the ?table

    table(..., exclude = if (useNA == "no") c(NA, NaN), useNA = c("no", "ifany", "always"), dnn = list.names(...), deparse.level = 1)

    whereas ?ftable

    ftable(..., exclude = c(NA, NaN), row.vars = NULL, col.vars = NULL)