2017-09-03 127 views
1

我有一个数据框,我想从两列中输出一个双向列联表。他们都有价值“太少”,“关于正确”或“太多”。R中的双向列联表

我打字

df %>% 
    filter(!is.na(col1)) %>% 
    group_by(col1) %>% 
    summarise(count = n()) 

分别为二者的和得到的东西是这样的:

col1  count 
<fctr>  <int> 
Too Little 19259   
About Right 9539    
Too Much 2816  

我想实现的是:

 Too Little About Right Too Much Total 
col1 19259  9539   2816  31614 
col2 20619  9374   2262  32255 
Total 39878  18913   5078  63869 

我一直试图使用表功能

addmargins(table(df$col1, df$col2)) 

但结果不是我想要

   Too Little About Right Too Much Sum 
    Too Little  13770  4424  740 18934 
    About Right  4901  3706  700 9307 
    Too Much   1250   800  679 2729 
    Sum    19921  8930  2119 30970 
+0

那么什么是有望走出把你想要 – Wen

+1

欢迎喜来所以,提问在[mimimal但完整的(很重要https://stackoverflow.com/help/ mcve)形式。也总是试图包含一些示例数据(截至目前没有人,但你可以看到'df')也许只有几行就足以作为一个简单的例子 – Nate

回答

3

我给tabulate一试,这是table基础(见?tabulate)是什么。例如,给定

set.seed(123) 
vals <- LETTERS[1:3] 
df <- as.data.frame(replicate(3, sample(vals, 5, T))) 
df <- data.frame(lapply(df, "levels<-", vals)) 

那么你可以做

m <- t(sapply(df, tabulate, nbins = length(vals))) 
colnames(m) <- vals 
addmargins(m) 
#  A B C Sum 
# V1 1 1 3 5 
# V2 1 3 1 5 
# V3 1 2 2 5 
# Sum 3 6 6 15 

或(通过@thelatemail)刚刚

addmargins(t(sapply(df, table))) 
#  A B C Sum 
# V1 1 1 3 5 
# V2 1 3 1 5 
# V3 1 2 2 5 
# Sum 3 6 6 15 
+1

我不知道为什么你需要'tabulate'具体。只是'addmargins(t(sapply(df,table))''会做到这一点,并且会保留这些名字。 – thelatemail

+0

@thelatemail真的,谢谢,我补充了这一点(尽管它可能与zx8754的解决方案太相似了,现在...) – lukeA

+0

不管怎样,做'sapply'比简单地列出一个列表更简单 – thelatemail

2

我们可以在一个循环,然后rbind使用表:

# Using dummy data from @lukeA's answer 

addmargins(do.call(rbind, lapply(df1, table))) 
#  A B C Sum 
# V1 1 1 3 5 
# V2 1 3 1 5 
# V3 1 2 2 5 
# Sum 3 6 6 15 

标杆

# bigger data 
set.seed(123) 
vals <- LETTERS[1:20] 
df1 <- as.data.frame(replicate(20, sample(vals, 100000, T))) 
df1 <- data.frame(lapply(df1, "levels<-", vals)) 


microbenchmark::microbenchmark(
    lukeA = { 
    m1 <- t(sapply(df1, tabulate, nbins = length(vals))) 
    colnames(m1) <- vals 
    m1 <- addmargins(m1) 
    }, 
    # as vals only used for luke's solution, keep it in. 
    lukeA_1 = { 
    vals <- LETTERS[1:20] 
    m2 <- t(sapply(df1, tabulate, nbins = length(vals))) 
    colnames(m2) <- vals 
    m2 <- addmargins(m2) 
    }, 
    thelatemail = {m3 <- addmargins(t(sapply(df1, table)))}, 
    zx8754 = {m4 <- addmargins(do.call(rbind, lapply(df1, table)))} 
) 
# Unit: milliseconds 
#  expr  min  lq  mean median  uq  max neval 
#  lukeA 2.349969 2.371922 2.518447 2.473839 2.558653 3.363738 100 
#  lukeA_1 2.351680 2.377196 2.523473 2.473839 2.542831 3.459242 100 
# thelatemail 38.316506 42.054136 43.785777 42.674912 44.234193 90.287809 100 
#  zx8754 38.695101 41.979728 44.933602 42.762006 44.244314 110.834292 100 
+1

无论如何,它的速度肯定会更快,但是你的基准会遗漏'vals '并不总是提前知道(或者至少不应该被认为是已知的),所以你必须在那里查找'vals < - unique(df1 [,1)') – thelatemail

+0

@ thelatemail真的,也许它应该是'vals < - unique(unlist(df1))',但是从OP的例子中,他们事先知道这些值。 – zx8754