214

I have a table in R that has str() of this:

 table [1:3, 1:4] 0.166 0.319 0.457 0.261 0.248 ...
 - attr(*, "dimnames")=List of 2
  ..$ x: chr [1:3] "Metro >=1 million" "Metro <1 million" "Non-Metro Counties"
  ..$ y: chr [1:4] "q1" "q2" "q3" "q4"

And looks like this when I print it:

                    y
x                           q1        q2        q3        q4
  Metro >=1 million  0.1663567 0.2612212 0.2670441 0.3053781
  Metro <1 million   0.3192857 0.2480012 0.2341030 0.1986102
  Non-Metro Counties 0.4570341 0.2044960 0.2121102 0.1263597

I want to get rid of the x and y and convert it to a data frame that looks exactly the same as the above (three rows, four columns), but without the x or y. If I use as.data.frame(mytable), instead I get this:

                    x  y      Freq
1   Metro >=1 million q1 0.1663567
2    Metro <1 million q1 0.3192857
3  Non-Metro Counties q1 0.4570341
4   Metro >=1 million q2 0.2612212
5    Metro <1 million q2 0.2480012
6  Non-Metro Counties q2 0.2044960
7   Metro >=1 million q3 0.2670441
8    Metro <1 million q3 0.2341030
9  Non-Metro Counties q3 0.2121102
10  Metro >=1 million q4 0.3053781
11   Metro <1 million q4 0.1986102
12 Non-Metro Counties q4 0.1263597

I probably fundamentally do not understand how tables relate to data frames.

Simon.S.A.
  • 6,240
  • 7
  • 22
  • 41
Victor Van Hee
  • 9,679
  • 7
  • 33
  • 41
  • 2
    When searching I was surprised how difficult it was to find a similar question on SO. Here's one:http://stackoverflow.com/questions/5855225/generate-a-vector-in-r-and-insert-it-in-a-stacked-frame It's a pretty basic maneuver and described in `?xtabs` (not that is necessarily the most obvious location.) – IRTFM May 26 '12 at 02:32
  • Pretty sure all you need to do is set `deparse.level = 0` (or possibly 2) in the call to `table` – Rich Scriven Jan 02 '15 at 08:47

5 Answers5

402

I figured it out already:

as.data.frame.matrix(mytable) 

does what I need -- apparently, the table needs to somehow be converted to a matrix in order to be appropriately translated into a data frame. I found more details on this as.data.frame.matrix() function for contingency tables at the Computational Ecology blog.

Jaap
  • 81,064
  • 34
  • 182
  • 193
Victor Van Hee
  • 9,679
  • 7
  • 33
  • 41
  • 37
    Or just `as.data.frame(mytable)`. (`is.matrix(mytable)` will reveal that tables really are just dressed up matrices, and `as.data.frame.matrix` is the method that gets dispatched when `as.data.frame()` is passed a matrix argument.) – Josh O'Brien May 25 '12 at 17:59
  • 21
    Josh - in the example shown at the top, as.data.frame(mytable) didnt' work - that's why Victor was asking the question, I thought? Could you clarify? – Heather Stark Nov 30 '13 at 21:53
  • 5
    @HeatherStark I suspect this is because it is in fact `as.data.frame.table` that is being dispatched, rather than the less specific `as.data.frame.matrix`. – jbaums Feb 20 '14 at 07:33
  • 5
    very nice find. the only thing I dislike is that my xtab factors (first "column") turn into `row.names`. I have managed to add a column using the `row.names` values, but I rather prevent `as.data.frame.matrix` writing `row.names` in the first place.. – Thieme Hennis Dec 11 '14 at 10:16
  • ``as.data.frame.matrix(table(x))`` gives me ``Error in seq_len(ncols) : argument must be coercible to non-negative integer``, while ``as.data.frame(table(x))`` works, where ``x`` is just a numeric vector ``c(1,2,...)`` – PatrickT Oct 21 '17 at 19:57
  • It's not working, the row names are addedd to each entry instead of being saved as row names in the data frame – robertspierre May 22 '21 at 20:48
  • Just a small function to deal with the `row.names` problem: `dftbls <- function (x) { f <- as.data.frame.matrix(x, stringsAsFactors = F) f <- cbind(row.names(f), f) row.names(f) <- NULL names(f)[1] <- "Data" return(f) }` – Mak Feb 24 '22 at 13:12
25

While the results vary in this case because the column names are numbers, another way I've used is data.frame(rbind(mytable)). Using the example from @X.X:

> freq_t = table(cyl = mtcars$cyl, gear = mtcars$gear)

> freq_t
   gear
cyl  3  4  5
  4  1  8  2
  6  2  4  1
  8 12  0  2

> data.frame(rbind(freq_t))
  X3 X4 X5
4  1  8  2
6  2  4  1
8 12  0  2

If the column names do not start with numbers, the X won't get added to the front of them.

BLT
  • 2,492
  • 1
  • 24
  • 33
  • This is also actually working better than as.data.frame.matrix in my example that returns an error: out <- structure(c(zone1 = 1208160L, zone2 = 1126841L, zone3 = 2261808L, zone4 = 1827557L, zone5 = 1038999L, zone6 = 353569L, zone7 = 351484L, zone8 = 441930L, zone9 = 25266L, zoneNA = 14751L), .Dim = 10L, .Dimnames = list( c("zone1", "zone2", "zone3", "zone4", "zone5", "zone6", "zone7", "zone8", "zone9", "zoneNA")), class = "table") > as.data.frame.matrix(out) Error in d[[2L]] : subscript out of bounds – cmbarbu Jun 04 '20 at 15:52
15

Short answer: using as.data.frame.matrix(mytable), as @Victor Van Hee suggested.

Long answer: as.data.frame(mytable) may not work on contingency tables generated by table() function, even if is.matrix(your_table) returns TRUE. It will still melt you table into the factor1 factor2 factori counts format.

Example:

> freq_t = table(cyl = mtcars$cyl, gear = mtcars$gear)

> freq_t
   gear
cyl  3  4  5
  4  1  8  2
  6  2  4  1
  8 12  0  2

> is.matrix(freq_t)
[1] TRUE

> as.data.frame(freq_t)
  cyl gear Freq
1   4    3    1
2   6    3    2
3   8    3   12
4   4    4    8
5   6    4    4
6   8    4    0
7   4    5    2
8   6    5    1
9   8    5    2
> as.data.frame.matrix(freq_t)
   3 4 5
4  1 8 2
6  2 4 1
8 12 0 2
coatless
  • 20,011
  • 13
  • 69
  • 84
X.X
  • 961
  • 2
  • 12
  • 16
9

If you are using the tidyverse, you can use

as_data_frame(table(myvector))

to get a tibble (i.e. a data frame with some minor variations from the base class)

Ben
  • 41,615
  • 18
  • 132
  • 227
-3

This is deprecated:

as.data.frame(my_table)

Instead use this package:

library("quanteda")
convert(my_table, to="data.frame") 
koen
  • 5,383
  • 7
  • 50
  • 89