此代码:
tips <- data.frame(
gender = c("female", "male", "male")
,smoker = c("yes", "no", "no")
)
tblFun <- function(x) {
tbl <- table(x)
res <- cbind(tbl, round(prop.table(tbl) * 100, 2))
colnames(res) <- c('Count', 'Percentage')
res
}
do.call(rbind, lapply(tips[1:2], tblFun))
产生此:
Count Percentage
female 1 33.33
male 2 66.67
no 2 66.67
yes 1 33.33
太好了。但是,我想产生这个:
key_value_pair Count Percentage
gender=female 1 33.33
gender=male 2 66.67
smoker=no 2 66.67
smoker=yes 1 33.33
请问有人那么友好并提出解决方案?谢谢!
答案 0 :(得分:3)
我会使用tidyverse
和一些数据操作:
library(tidyverse)
tips %>%
gather(key_value, value) %>% # wide to long
count(key_value, value) %>%
group_by(key_value) %>%
mutate(percentage = n / sum(n)) %>%
unite(key_value_pair, key_value, value, sep = "=") # convert 2 cols into 1
# key_value_pair n percentage
# <chr> <int> <dbl>
# 1 gender=female 1 0.333
# 2 gender=male 2 0.667
# 3 smoker=no 2 0.667
# 4 smoker=yes 1 0.333
答案 1 :(得分:2)
一种方法是扩展您的tblFun
函数以接受类别名称并将其添加到标签之前。
tblFun <- function(x, nm = character(0)) {
tbl <- table(x)
if (length(nm)) names(tbl) <- paste(nm[[1]], names(tbl), sep = "=")
res <- cbind(tbl, round(prop.table(tbl) * 100, 2))
colnames(res) <- c('Count', 'Percentage')
res
}
不进行任何更改,其行为与以前相同:
do.call(rbind, lapply(tips[1:2], tblFun))
# Count Percentage
# female 1 33.33
# male 2 66.67
# no 2 66.67
# yes 1 33.33
为了在每一列中传递每一列的名称,您需要使用lapply
,Map
的多参数版本:
do.call(rbind, Map(tblFun, tips[1:2], names(tips[1:2])))
# Count Percentage
# gender=female 1 33.33
# gender=male 2 66.67
# smoker=no 2 66.67
# smoker=yes 1 33.33
一种替代方法是使用purrr::imap
,它将对象及其名称(作为第二个参数)传递给函数:
do.call(rbind, purrr::imap(tips[1:2], tblFun))
# Count Percentage
# gender=female 1 33.33
# gender=male 2 66.67
# smoker=no 2 66.67
# smoker=yes 1 33.33
我看到的一个优点是不需要同时包含tips[1:2]
和names(tips[1:2])
,尽管如果您尚未使用purrr
或tidyverse-packages,则为此添加另一个软件包可能并不需要(特别是当Map
与显式names()
做相同的事情时)。
作为Map
在做什么的简要说明:它将参数“压缩”在一起。
作为比较,lapply
(和族)对其输入向量/列表的每个元素运行一次函数。因此lapply(1:3, myfunc)
“展开”到
list(
myfunc(1),
myfunc(2),
myfunc(3)
)
但是,如果您尝试提供多个向量,它就不会表现为一种“可能”的需求/思考:lapply(1:3, myfunc, 11:13)
会展开为:
list(
myfunc(1, 11:13),
myfunc(2, 11:13),
myfunc(3, 11:13)
)
Map
对任意数量的引导程序/列表执行此操作,因此Map(myfunc, 1:3, 11:13, 21:23, 99)
展开至
list(
myfunc(1, 11, 21, 99),
myfunc(2, 12, 22, 99),
myfunc(3, 13, 23, 99)
)
(请注意如何循环使用长度为一的向量。尽管它确实对长度在1到最长向量之间的长度进行了循环,但是除非您严格控制较短的向量应该相乘的事实,否则我不建议您依赖它到最长的长度,没有余数。)
在这种情况下, myfunc
必须接受(至少)三个参数。 lapply
和Map
之间的两个显着区别:
lapply
将数据放在第一位,将功能放在第二位;由于Map
接受一个或多个向量/列表,因此将函数放在第一位,将一个或多个数据放在第二个+; Map
将循环使用一个奇异的list
参数,因此Map(myfunc, 1:3, list(11:13)
会展开到list(myfunc(1, 11:13), myfunc(2, 11:13), myfunc(3, 11:13))
,从表面上看,它与lapply(1:3, myfunc, 11:13)
非常相似,但很方便当您有两个以上的输入向量时。Map
向do.call
发送任意/未知长度的向量/列表,如下所示:
l <- list(1:3, 11:13, 21:21)
do.call("Map", c(f = myfunc, l))
(只要myfunc
可以通过...
机制接受任意数量的参数)。 Map
的第一个也是唯一的命名参数是函数的f=
。不需要在这里命名,但我想澄清。lapply
具有可选的简化版本sapply
,Map
具有可选的简化版本mapply
。我倾向于偏向于露骨-没有比期望的矢量输出更令人沮丧的了,但是一个输入导致输出为list
...