繁体   English   中英

在数据框中的因子水平上应用自定义函数

[英]Apply a custom function over levels of a factor in a dataframe

我正在尝试应用基于 tidyverse 的方法,或者至少是一个整洁的解决方案,以在数据帧中的因子级别上应用自定义函数。

考虑以下测试数据集:

df <- tibble(LINE=rep(c(1,2),each=6), FOUND=c(1,1,1,0,1,1,0,0,1,0,0,1))

#    LINE FOUND
#   <dbl> <dbl>
# 1     1     1
# 2     1     1
# 3     1     1
# 4     1     0
# 5     1     1
# 6     1     1
# 7     2     0
# 8     2     0
# 9     2     1
#10     2     0
#11     2     0
#12     2     1

例如,我想知道按 LINE 因子级别找到的结果(例如 FOUND==1)的比例。 现在,我正在使用以下代码,但我确实在尝试更简洁的代码。

# This is the function to calculate the proportion "found"
get_prop <- function (data) {
  tot <- data %>% nrow()
  found <- data %>% dplyr::filter(FOUND==1) %>% nrow
  found / tot
}

# This is the code to generate the expected result
lines <- df$LINE %>% unique %>% sort
v_line <- vector()
v_prop <- vector()
for (i in 1:length(lines)) {
  tot <- df %>% dplyr::filter(LINE==lines[i])
  v_line[i] <- lines[i]
  v_prop[i] <- get_prop(tot)
}
df_line = data.frame(LINE = v_line, CALL = v_prop)

我希望以下内容有效,但它没有,因为它返回每个级别的结果,但数值解是整个数据集的结果,而不是特定于级别的:

df %>% dplyr::group_by(LINE) %>% dplyr::summarise(get_prop(.))

编辑:请注意,我正在寻找的是一种解决方案,用于在数据帧中的因子级别上应用自定义函数 它不一定是特定值出现的次数或比例,如所示示例中所示。

编辑 2 :也就是说,我正在寻找一种使用上面的get_prop函数的解决方案。 这不是因为它是解决这个特定问题的最佳方法,而是因为它更具有普遍性

如果要按组应用自定义函数,可以使用group_split命令。 这会将您的数据框拆分为列表的元素。 每个列表元素都是 df 的子集。 然后,您可以使用map将您的函数应用于每个级别(请注意,您可以使用group_map一步进行group_splitmap )。 我添加了最后一行以获得原始方法的形式。

df %>% 
  group_by(LINE) %>% 
  group_split() %>% 
  map_dbl(get_prop) %>% 
  tibble(LINE = seq_along(.), CALL = .) # optional to get back to a df
#> # A tibble: 2 x 2
#>    LINE  CALL
#>   <int> <dbl>
#> 1     1 0.833
#> 2     2 0.333

reprex 包(v0.3.0) 创建于 2020-01-20

现在我担心这个解决方案的一件事是group_split删除分组变量(如果它保留为列表名称或属性,我会更喜欢)。 所以如果你想要一个 tibble 作为结果,预先保存分组变量可能是有意义的:

groups <- unique(df$LINE)

df %>% 
  group_by(LINE) %>% 
  group_split() %>% 
  map_dbl(get_prop) %>% 
  tibble(group = groups, result = .)

更新

我认为整体最干净的方法是这样的(使用更一般的例子):

library(tidyverse)
df <- tibble(LINE=rep(c("a", "b"),each=6), FOUND=c(1,1,1,0,1,1,0,0,1,0,0,1))

lvls <- unique(df$LINE)

df %>% 
  group_by(LINE) %>% 
  group_map(~ get_prop(.x)) %>% 
  setNames(lvls) %>% 
  unlist() %>% 
  enframe()
#> # A tibble: 2 x 2
#>   name  value
#>   <chr> <dbl>
#> 1 a     0.833
#> 2 b     0.333

reprex 包(v0.3.0) 创建于 2020-01-20

另一种选择是使用group_map然后使用 tibble tibble::enframe

library(dplyr)

df %>% 
group_by(LINE) %>% 
group_map(~get_prop(.)) %>% 
unlist() %>% 
tibble::enframe()

#  name value
#  <int> <dbl>
#1     1 0.833
#2     2 0.333

您还可以使用group_modify来保留组名(使用@JBGruber 的数据)

df %>%
    group_by(LINE) %>%
    group_modify(~ tibble::enframe(get_prop(.), name = NULL))

# LINE  value
#  <chr> <dbl>
#1 a     0.833
#2 b     0.333

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM