Редактировать: Основываясь на ответе aL3xa ниже, я изменил его синтаксис ниже. Не идеально, но все ближе. Я до сих пор не нашел способ заставить xtable принимать аргументы \ multicolumn {} для столбцов или строк. Также кажется, что Hmisc выполняет некоторые из этих типов задач за кулисами, но это кажется чем-то вроде понимания того, что там происходит. У кого-нибудь есть опыт работы с функцией latex в 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")
Есть ли у кого-нибудь какие-либо предложения по принятию этого для кросс-табуляции (т.е. день недели BY цель поездки)? Вот то, что я в настоящее время написал, который НЕ использует библиотеку xtable, и ALMOST работает, но не является динамическим и довольно уродливым для работы:
#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")
Мне не удалось понять, как сгенерировать заголовок из нескольких столбцов с помощью 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 = " ")
))
}
@
Отличный вопрос, это меня какое-то время беспокоит (это не так сложно, просто я чертовски ленив ... как обычно). Однако ... хотя вопрос отличный, но, боюсь, ваш подход - нет. Есть бесценный пакет под названием xtable
, который можно (не) использовать. Кроме того, эта проблема слишком распространена - велика вероятность, что где-то в Интернет уже есть готовое решение.
На днях я собираюсь разобраться с этим раз и навсегда (выложу код на GitHub). Основная идея выглядит примерно так: хотите ли вы, чтобы частота и / или процентные значения в одной ячейке (разделенной \) или строках с абсолютной и относительной частотами (или%) последовательно? Я бы выбрал 2 и , поэтому сейчас я опубликую решение для «первой помощи»:
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)
Ура!
Как это может работать для вас?
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
. Возможно, вы даже сможете написать достаточно общую функцию, которая будет принимать на вход всевозможные переделанные фреймы данных.
Edit:.
Только что пришло в голову хорошее решение, которое поможет вам приблизиться к цели. После создания 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
значений