使用table()生成数据框架的更好方法

Tom*_*eif 9 r crosstab

最近,我发现我一遍又一遍地使用以下模式.过程是:

  • 使用因子交叉制表数字变量 table
  • 从创建的表创建数据框
  • 将原始数值添加到数据框(从行名称(!))
  • 删除行名称
  • 重新排序聚合数据框的列

在R中,它看起来像这样:

# Sample data
df <- data.frame(x = round(runif(100), 1), 
                 y = factor(ifelse(runif(100) > .5, 1, 0), 
                            labels = c('failure', 'success')) 
                )

# Get frequencies
dfSummary <- as.data.frame.matrix(table(df$x, df$y))
# Add column of original values from rownames
dfSummary$x <- as.numeric(rownames(dfSummary))
# Remove rownames
rownames(dfSummary) <- NULL
# Reorder columns
dfSummary <- dfSummary[, c(3, 1, 2)]
Run Code Online (Sandbox Code Playgroud)

R中有更优雅的东西,最好是使用基本功能吗?我知道我可以使用sql在单个命令中执行此操作 - 我认为必须有可能在R中实现类似的行为.

sqldf解决方案:

library(sqldf)
dfSummary <- sqldf("select 
                     x, 
                     sum(y = 'failure') as failure,
                     sum(y = 'success') as success
                    from df group by x")
Run Code Online (Sandbox Code Playgroud)

tal*_*lat 8

基础R的替代方案可以是:

aggregate(. ~ x, transform(df, success = y == "sucess", 
                               failure = y == "failure", y = NULL), sum)
#     x success failure
#1  0.0       2       4
#2  0.1       6       8
#3  0.2       1       7
#4  0.3       5       4
#5  0.4       6       6
#6  0.5       3       3
#7  0.6       4       6
#8  0.7       6       6
#9  0.8       4       5
#10 0.9       6       7
#11 1.0       1       0
Run Code Online (Sandbox Code Playgroud)


akr*_*run 6

base R(到目前为止)的其他解决方案相比,将您的代码修改为函数将是高效的.如果你想要一行代码,base R可以使用"reshape/table"组合.

reshape(as.data.frame(table(df)), idvar='x', timevar='y',
        direction='wide')
#     x Freq.failure Freq.success
#1    0            3            2
#2  0.1            3            9
#3  0.2            5            5
#4  0.3            8            7
#5  0.4            5            3
#6  0.5            9            4
#7  0.6            3            6
#8  0.7            7            6
#9  0.8            3            1
#10 0.9            4            3
#11   1            0            4
Run Code Online (Sandbox Code Playgroud)

如果你想尝试 data.table

library(data.table)
dcast.data.table(setDT(df), x~y)
#          x failure success
# 1: 0.0       3       2
# 2: 0.1       3       9
# 3: 0.2       5       5
# 4: 0.3       8       7
# 5: 0.4       5       3
# 6: 0.5       9       4
# 7: 0.6       3       6
# 8: 0.7       7       6
# 9: 0.8       3       1
#10: 0.9       4       3
#11: 1.0       0       4
Run Code Online (Sandbox Code Playgroud)

更新

我没有注意到as.data.frame(table(转换为"因子"列(感谢@ Hadley的评论).解决方法是:

res <- transform(reshape(as.data.frame(table(df), stringsAsFactors=FALSE),
     idvar='x', timevar='y', direction='wide'), x= as.numeric(x))
Run Code Online (Sandbox Code Playgroud)

数据

set.seed(24)
df <- data.frame(x = round(runif(100), 1), 
             y = factor(ifelse(runif(100) > .5, 1, 0), 
                        labels = c('failure', 'success')) 
            )
Run Code Online (Sandbox Code Playgroud)

基准

set.seed(24)
df <- data.frame(x = round(runif(1e6), 1), 
             y = factor(ifelse(runif(1e6) > .5, 1, 0), 
                        labels = c('failure', 'success')) 
            )

tomas <- function(){
   dfSummary <- as.data.frame.matrix(table(df$x, df$y))
   dfSummary$x <- as.numeric(rownames(dfSummary))
   dfSummary <- dfSummary[, c(3, 1, 2)]}


 doc <- function(){aggregate(. ~ x, transform(df,
        success = y == "success", failure = y == "failure",
                     y = NULL), sum)}

 akrun <- function(){reshape(as.data.frame(table(df)),
             idvar='x', timevar='y', direction='wide')}

library(microbenchmark)
 microbenchmark(tomas(), doc(), akrun(), unit='relative', times=20L)
 Unit: relative
 #expr       min         lq      mean    median         uq       max neval cld
 #tomas()  1.000000  1.0000000  1.000000  1.000000  1.0000000  1.000000    20  a 
 #doc()   13.451037 11.5050997 13.082074 13.043584 12.8048306 19.715535    20   b
 #akrun()  1.019977  0.9522809  1.012332  1.007569  0.9993835  1.533191    20  a 
Run Code Online (Sandbox Code Playgroud)

更新了 dcast.data.table

df1 <- copy(df)
akrun2 <- function() {dcast.data.table(setDT(df1), x~y)}
microbenchmark(tomas(), akrun2(), unit='relative', times=20L)
#   Unit: relative
# expr      min       lq     mean  median       uq      max neval cld
# tomas() 6.493231 6.345752 6.410853 6.51594 6.502044 5.591753    20   b
# akrun2() 1.000000 1.000000 1.000000 1.00000 1.000000 1.000000    20  a 
Run Code Online (Sandbox Code Playgroud)