计算水平百分比-添加列名

时间:2019-05-23 16:15:25

标签: r

此代码:

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

请问有人那么友好并提出解决方案?谢谢!

2 个答案:

答案 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

为了在每一列中传递每一列的名称,您需要使用lapplyMap的多参数版本:

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必须接受(至少)三个参数。 lapplyMap之间的两个显着区别:

  • 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)非常相似,但很方便当您有两个以上的输入向量时。
  • 因为它并不关心有多少个向量/列表,所以可以使用Mapdo.call发送任意/未知长度的向量/列表,如下所示:
    l <- list(1:3, 11:13, 21:21)
    do.call("Map", c(f = myfunc, l))
    
    (只要myfunc可以通过...机制接受任意数量的参数)。 Map的第一个也是唯一的命名参数是函数的f=。不需要在这里命名,但我想澄清。
  • 就像lapply具有可选的简化版本sapplyMap具有可选的简化版本mapply。我倾向于偏向于露骨-没有比期望的矢量输出更令人沮丧的了,但是一个输入导致输出为list ...