9

I have a data frame which looks like this:

structure(list(ab = c(0, 1, 1, 1, 1, 0, 0, 0, 1, 1), bc = c(1, 
1, 1, 1, 0, 0, 0, 1, 0, 1), de = c(0, 0, 1, 1, 1, 0, 1, 1, 0, 
1), cl = c(1, 2, 3, 1, 2, 3, 1, 2, 3, 2)), .Names = c("ab", "bc", 
"de", "cl"), row.names = c(NA, -10L), class = "data.frame")

The column cl indicates a cluster association and the variables ab,bc & de carry binary answers, where 1 indicates yes and 0 - No.

I am trying to create a table cross tabbing cluster along with all the other columns in the data frame viz ab, bc and de, where the clusters become column variables. The desired output is like this

    1  2  3
 ab 1  3  2
 bc 2  3  1
 de 2  3  1

I tried the following code:

with(newdf, tapply(newdf[,c(3)], cl, sum))

This provides me values cross tabbing only one column at a time. My data frame has 1600+ columns with 1 cluster column. Can someone help?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Apricot
  • 2,925
  • 5
  • 42
  • 88
  • 1
    It seems you could try with `aggregate`; `aggregate(. ~ cl, newdf, sum)`? – alexis_laz Oct 31 '15 at 22:38
  • alexis_laz...thank you for a simple execution. This is really nice, but since my current dataset have 1600+ variables, it becomes a tad bit difficult to read all of them in one go. – Apricot Nov 01 '15 at 03:42

5 Answers5

8

In base R:

t(sapply(data[,1:3],function(x) tapply(x,data[,4],sum)))
#   1 2 3
#ab 1 3 2
#bc 2 3 1
#de 2 3 1
nicola
  • 24,005
  • 3
  • 35
  • 56
7

One way using dplyr would be:

library(dplyr)
df %>% 
  #group by the varialbe cl
  group_by(cl) %>%
  #sum every column
  summarize_each(funs(sum)) %>%
  #select the three needed columns
  select(ab, bc, de) %>%
  #transpose the df
  t

Output:

   [,1] [,2] [,3]
ab    1    3    2
bc    2    3    1
de    2    3    1
LyzandeR
  • 37,047
  • 12
  • 77
  • 87
6

Your data is in a half-long half-wide format, and you want it in a fully wide format. This is easiest if we first covert it to a fully long format:

library(reshape2)
df_long = melt(df, id.vars = "cl")
head(df_long)
#    cl variable value
# 1   1       ab     0
# 2   2       ab     1
# 3   3       ab     1
# 4   1       ab     1
# 5   2       ab     1
# 6   3       ab     0

Then we can turn it into a wide format, using sum as the aggregating function:

dcast(df_long, variable ~ cl, fun.aggregate = sum)
#   variable 1 2 3
# 1       ab 1 3 2
# 2       bc 2 3 1
# 3       de 2 3 1
Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
2

You can also combine tidyr:gather or reshape2::melt and xtabs to have your contengency table

library(tidyr)
xtabs(value ~ key + cl, data = gather(df, key, value, -cl))
##     cl
## key  1 2 3
##   ab 1 3 2
##   bc 2 3 1
##   de 2 3 1

If your prefer to use pipe

df %>%
  gather(key, value, -cl) %>%
  xtabs(value ~ key + cl, data = .)
dickoa
  • 18,217
  • 3
  • 36
  • 50
0

Just to update using dplyr's pivot_longer (that supersedes gather) following the code dickoa wrote:

library(dplyr)

df %>% 
pivot_longer(cols = ab:de,
          names_to = "key",
          values_to = "value") %>% 
xtabs(value ~ key + cl, data = .)
Zoë Turner
  • 459
  • 5
  • 8