【问题标题】:Counts & Percentages in xTable, Sweave, R, cross tabulationsxTable、Sweave、R、交叉表中的计数和百分比
【发布时间】:2010-08-09 21:42:17
【问题描述】:

编辑:根据 aL3xa 的回答,我在下面修改了他的语法。不完美,但越来越接近。我还没有找到让 xtable 接受列或行的 \multicolumn{} 参数的方法。 Hmisc 似乎也在幕后处理了其中一些类型的任务,但要了解那里发生的事情似乎是一项艰巨的任务。有人对 Hmisc 中的乳胶功能有经验吗?

ctab <- function(tab, dec = 2, margin = NULL) {
    tab <- as.table(tab)
    ptab <- paste(round(prop.table(tab, margin = margin) * 100, dec), "%", sep = "")
    res <- matrix(NA, nrow = nrow(tab) , ncol = ncol(tab) * 2, byrow = TRUE)
    oddc <- 1:ncol(tab) %% 2 == 1
    evenc <- 1:ncol(tab) %% 2 == 0
    res[,oddc ] <- tab
    res[,evenc ] <- ptab
    res <- as.table(res)
    colnames(res) <- rep(colnames(tab), each = 2)
    rownames(res) <- rownames(tab)
    return(res)
}

我想为 LaTeX 输出创建一个表格,其中包含每个列或变量的计数和百分比。我还没有找到解决这个问题的现成解决方案,但我觉得我必须在某种程度上重新创建轮子。

我已经为直接制表开发了一个解决方案,但在为交叉制表采用某些东西时遇到了困难。

首先是一些示例数据:

#Generate sample data
dow <- sample(1:7, 100, replace=TRUE)
purp <- sample(1:4, 100, replace=TRUE)
dow <- factor(dow, 1:7, c("Mon", "Tues", "Wed", "Thurs", "Fri", "Sat", "Sun"))
purp <- factor(purp, 1:4, c("Business", "Commute", "Vacation", "Other"))

现在可以使用直接选项卡功能:

customTable <- function(var, capt = NULL){
    counts <- table(var)
    percs <- 100 * prop.table(counts)       

    print(
        xtable(
            cbind(
                Count = counts
                , Percent = percs
            )
        , caption = capt
        , digits = c(0,0,2)
        )
    , caption.placement="top"
    )
}

#Usage
customTable(dow, capt="Day of Week")
customTable(purp, capt="Trip Pupose")

有没有人有任何建议将其用于交叉表(即按旅行目的一周中的哪一天)?这是我目前写的,它不使用 xtable 库,几乎可以工作,但不是动态的,而且很难使用:

#Create table and percentages
a <- table(dow, purp)
b <- round(prop.table(a, 1),2)

#Column bind all of the counts & percentages together, this SHOULD become dynamic in future
d <- cbind( cbind(Count = a[,1],Percent =  b[,1])
        , cbind(Count = a[,2], Percent = b[,2])
        , cbind(Count = a[,3], Percent = b[,3])
        , cbind(Count = a[,4], Percent = b[,4])
)

#Ugly function that needs help, or scrapped for something else
crossTab <- function(title){
    cat("\\begin{table}[ht]\n")
    cat("\\begin{center}\n")
    cat("\\caption{", title, "}\n", sep="") 

    cat("\\begin{tabular}{rllllllll}\n")
    cat("\\hline\n")

    cat("", cat("", paste("&\\multicolumn{2}{c}{",colnames(a), "}"), sep = ""), "\\\\\n", sep="")
    c("&", cat("", colnames(d), "\\\\\n", sep=" & "))
    cat("\\hline\n")
    c("&", write.table(d, sep = " & ", eol="\\\\\n", quote=FALSE, col.names=FALSE))

    cat("\\hline\n")
    cat("\\end{tabular}\n")
    cat("\\end{center}\n")
    cat("\\end{table}\n")   
}   

crossTab(title = "Day of week BY Trip Purpose")

【问题讨论】:

  • 不要对 LaTeX 进行硬编码,很快它就会变得难以管理。同样代表 HTML。看看xtable 的文档,看看我的回答(看看一个自负的人的话)。

标签: r latex sweave xtable


【解决方案1】:

在 Tables-package 中只有一行:

# data:
dow <- sample(1:7, 100, replace=TRUE)
purp <- sample(1:4, 100, replace=TRUE)
dow <- factor(dow, 1:7, c("Mon", "Tues", "Wed", "Thurs", "Fri", "Sat", "Sun"))
purp <- factor(purp, 1:4, c("Business", "Commute", "Vacation", "Other"))

dataframe <-  data.frame( dow, purp)

# The packages

library(tables)
library(Hmisc)

# The table
tabular(  (Weekday=dow) ~  (Purpose=purp)*(Percent("row")+ 1)    ,data=dataframe        )

# The latex table
latex(  tabular(  (Weekday=dow) ~  (Purpose=purp)*(Percent("col")+ 1)    ,data=dataframe        ))

使用 booktabs,你会得到这个(可以进一步定制):

【讨论】:

    【解决方案2】:

    很好的问题,这个问题困扰了我一段时间(这不是那么难,只是我很懒惰......像往常一样)。然而......虽然这个问题很好,但恐怕你的方法不是。您可以(误用)使用名为xtable 的无价包。此外,这个问题太常见了 - Internet 上很可能已经有一些现成的解决方案。

    这些天我将一劳永逸地解决它(我将在 GitHub 上发布代码)。主要思想有点像这样:您想要一个单元格中的频率和/或百分比值(由 \ 分隔)还是连续具有绝对和相对频率(或 %)的行?我会选择第二个nd,所以我现在将发布一个“急救”解决方案:

    ctab <- function(tab, dec = 2, ...) {
      tab <- as.table(tab)
      ptab <- paste(round(prop.table(tab) * 100, dec), "%", sep = "")
      res <- matrix(NA, nrow = nrow(tab) * 2, ncol = ncol(tab), byrow = TRUE)
      oddr <- 1:nrow(tab) %% 2 == 1
      evenr <- 1:nrow(tab) %% 2 == 0
      res[oddr, ] <- tab
      res[evenr, ] <- ptab
      res <- as.table(res)
      colnames(res) <- colnames(tab)
      rownames(res) <- rep(rownames(tab), each = 2)
      return(res)
    }
    

    现在尝试类似:

    data(HairEyeColor)           # load an appropriate dataset
    tb <- HairEyeColor[, , 1]    # choose only male respondents
    ctab(tb)
          Brown  Blue   Hazel Green
    Black 32     11     10    3    
    Black 11.47% 3.94%  3.58% 1.08%
    Brown 53     50     25    15   
    Brown 19%    17.92% 8.96% 5.38%
    Red   10     10     7     7    
    Red   3.58%  3.58%  2.51% 2.51%
    Blond 3      30     5     8    
    Blond 1.08%  10.75% 1.79% 2.87%
    

    确保您加载了xtable 包并使用print(它是一个通用函数,因此您必须传递一个xtable 类对象)。抑制行名很重要。我明天会优化这个——它应该是xtable 兼容的。现在是我所在时区的凌晨 3 点,所以我将用以下几行来结束我的回答:

    print(xtable(ctab(tb)), include.rownames = FALSE)
    

    干杯!

    【讨论】:

    • 再一次:小心,这个是从零开始写的,没有优化。如果它弄坏了你的机器,我概不负责! =)
    【解决方案3】:

    我无法弄清楚如何使用 xtable 生成多列标题,但我确实意识到我可以将我的计数和百分比连接到同一列中以用于打印目的。不理想,但似乎可以完成工作。这是我写的函数:

    ctab3 <- function(row, col, margin = 1, dec = 2, percs = FALSE, total = FALSE, tex = FALSE, caption = NULL){
        tab <- as.table(table(row,col))
        ptab <- signif(prop.table(tab, margin = margin), dec)
    
        if (percs){
    
            z <- matrix(NA, nrow = nrow(tab), ncol = ncol(tab), byrow = TRUE) 
            for (i in 1:ncol(tab)) z[,i] <- paste(tab[,i], ptab[,i], sep = " ")
            rownames(z) <- rownames(tab)
            colnames(z) <- colnames(tab)
    
            if (margin == 1 & total){
                rowTot <- paste(apply(tab, 1, sum), apply(ptab, 1, sum), sep = " ")
                z <- cbind(z, Total = rowTot)
            } else if (margin == 2 & total) {
                colTot <- paste(apply(tab, 2, sum), apply(ptab, 2, sum), sep = " ")
                z <- rbind(z,Total = colTot)
            }
        } else {
            z <- table(row, col)    
        }
    ifelse(tex, return(xtable(z, caption)), return(z))
    }
    

    可能不是最终产品,但确实允许在参数方面具有一定的灵活性。在最基本的层面上,它只是table() 的包装器,但也可以生成 LaTeX 格式的输出。这是我最终在Sweave 文档中使用的内容:

    <<echo = FALSE>>=
    for (i in 1:ncol(df)){
        print(ctab3(
            col = df[,1]
            , row = df[,i]
            , margin = 2
            , total = TRUE
            , tex = TRUE
            , caption = paste("Dow by", colnames(df[i]), sep = " ")
        ))
    }
    @
    

    【讨论】:

      【解决方案4】:

      使用 Hmisc 包中的 multicolumnlatex 并不算太糟糕。这个最小的 Sweave 文档:

      \documentclass{article}
      \begin{document}
      
      <<echo = FALSE,results = tex>>=
      library(Hmisc)
      dow <- sample(1:7, 100, replace=TRUE)
      purp <- sample(1:4, 100, replace=TRUE)
      dow <- factor(dow, 1:7, c("Mon", "Tues", "Wed", "Thurs", "Fri", "Sat", "Sun"))
      purp <- factor(purp, 1:4, c("Business", "Commute", "Vacation", "Other"))
      tbl <- table(dow,purp)
      tbl_prop <- round(100 * prop.table(tbl,1),2)
      
      tbl_df <- as.data.frame.matrix(tbl)
      tbl_prop_df <- as.data.frame.matrix(tbl_prop)
      colnames(tbl_prop_df) <- paste(colnames(tbl_prop_df),"1",sep = "")
      df <- cbind(tbl_df,tbl_prop_df)[,ggplot2:::interleave(1:4,5:8)]
      colnames(df) <- rep(c('n','\\%'),times = 4)
      
      latex(object=df,file="",cgroup = colnames(tbl_df),
            colheads = NULL,rowlabel = "",
            center = "centering",collabel.just = rep("r",8))
      @
      
      \end{document}
      

      为我制作这个:

      显然,我已经硬编码了很多东西,并且可能有更巧妙的方法来生成您最终传递给latex 的数据帧,但这至少应该让您开始使用multicolum .

      另外,有个小问题,我使用 ggplot2interleave 函数结合计数和百分比来交替列。那只是因为我很懒。

      【讨论】:

      • 有没有办法在星期几上方添加一个粗体标签,希望与purp在同一行?
      • 处理不同列数的通用版本:pastebin.com/uJpwYbeZ
      【解决方案5】:

      这对你有什么作用?

      library(reshape)
      library(plyr)
      df <- data.frame(dow = dow, purp = purp)
      
      df.count <- count(df)
      df.count <- ddply(df.count, .(dow), transform, p = round(freq / sum(freq),2))
      
      df.m <- melt(df.count)
      
      df.print <- cast(df.m, dow ~ purp + variable)
      
      library(xtable)
      xtable(df.print)
      

      它没有给你很好的多列,而且我对xtable 没有足够的经验来弄清楚这是否可能。但是,如果您要编写自定义函数,您可以尝试使用对 df.print 的列名进行操作的函数。您甚至可以编写一个足够通用的代码来将各种重铸数据帧作为输入。

      编辑: 只是想到了一个让你更接近的好解决方案。创建后df.m

      df.preprint <- ddply(df.m, .(dow, purp), function(x){
              x <- cast(x, dow ~ variable)
              x$value <- paste(x$freq, x$p, sep = " / ")
              return(c(value = x$value))
           }
      )
      
      df.print <- cast(df.preprint, dow ~ purp)
      
      print(xtable(df.print), include.rownames = F)
      

      现在,每个单元格都将包含 N / percent

      【讨论】:

      • 我是否遗漏了一些非常基本的东西,或者 count() 不在基础 R 中?我得到了Error: could not find function "count"No documentation for 'count' in specified packages and libraries: you could try '??count'?搜索??count 会产生很多结果,但不是我认为您想要的结果?或者,我只需要关掉电脑,明天早上再回到这个……
      • countplyr 包中可用。 JoFrhwld,加载plyr 就像...他答案的第三行。 library(sos)(先安装软件包)-findFn("somefunction") 在您遇到一些“真正未知”的功能时会有所帮助。
      • 出于某种原因,我正在使用 R 2.10 并且 plyr 的行为不正常...加载 R 2.11.1 并且星星开始对齐...我显然需要一些睡眠 - 将继续这个明天。谢谢大家的意见!
      【解决方案6】:

      我意识到这个线程有点老了,但是 reporttools 包中的 tableNominal() 函数可能会提供您正在寻找的功能。

      【讨论】:

      • 想举个例子(最好有一些输出)?
      【解决方案7】:
      tab<-table(row, col)
      ctab<-round(100*prop.table(tab,2), 2) # for column percents (see the args for prop.table)
      
      for (i in 1:length(tab)) {
        ctab[i]<-paste(tab[i]," (", ctab[i], "%", ")", sep="")
      }
      
      require(xtable);
      k<-xtable(ctab,digits=1) # make latex table
      

      【讨论】:

        猜你喜欢
        • 2016-12-02
        • 2020-11-30
        • 1970-01-01
        • 1970-01-01
        • 2022-01-21
        • 1970-01-01
        • 1970-01-01
        • 2019-09-05
        • 1970-01-01
        相关资源
        最近更新 更多