使用 summarize 和 for 循环从字符向量中获取列名

Use summarize and a for loop taking column names from a character vector

我有一个无法在此处共享的数据集,但我需要使用 for 循环创建列,并且列名应来自字符向量。下面我尝试使用 nycflights13 包中的航班数据集来复制我想要实现的目标。

install.packages("nycflights13")
library(nycflights13)

flights <- nycflights13::flights
flights <- flights[c(10, 16, 17)]

var_interest <- c("distance", "hour")

for (i in 1:length(var_interest)) {
  flights %>% group_by(carrier) %>%
    summarize(paste(var_interest[i], "n", sep = "_") = sum(paste(var_interest[i])))
}

此代码生成以下错误:

Error: unexpected '=' in:
"  flights %>% group_by(carrier) %>%
    summarize(paste(var_interest[i], "n", sep = "_") ="
> }
Error: unexpected '}' in "}"

我的实际数据集比这个例子更复杂,因此,我需要遵循这种方法。因此,如果您能帮我找到我在这里缺少的东西,将不胜感激!

代码可以修改为在将字符串转换为symbol之后评估(!!)列,而在赋值的lhs上(:=)对字符串进行评估(!!

out <- vector('list', length(var_interest))
for (i in seq_along(var_interest)) {
out[[i]] <- flights %>%
   group_by(carrier) %>%
   summarize(!! paste(var_interest[i], "n", sep = "_") := 
       sum(!! rlang::sym(var_interest[i])), .groups = 'drop')
 }


lapply(out, head, 3)
#[[1]]
# A tibble: 3 x 2
#  carrier distance_n
#  <chr>        <dbl>
#1 9E         9788152
#2 AA        43864584
#3 AS         1715028

#[[2]]
# A tibble: 3 x 2
#  carrier hour_n
#  <chr>    <dbl>
#1 9E      266419
#2 AA      413361
#3 AS        9013

有多种方法可以传递字符串列名并对其求值。

  1. 如上所述,转换为 symbol 并计算 (!!)。
  2. 利用 across,它可以将不带引号的、字符串或列索引作为整数,即在这种情况下,我们甚至不需要任何循环

flights %>%
      group_by(carrier) %>%
      summarise(across(all_of(var_interest), ~ 
               sum(., na.rm = TRUE), .names = '{.col}_n'), 
            .groups = 'drop') 
# A tibble: 16 x 3
#   carrier distance_n hour_n
#   <chr>        <dbl>  <dbl>
# 1 9E         9788152 266419
# 2 AA        43864584 413361
# 3 AS         1715028   9013
# 4 B6        58384137 747278
# 5 DL        59507317 636932
# 6 EV        30498951 718187
# 7 F9         1109700   9441
# 8 FL         2167344  43960
# 9 HA         1704186   3324
#10 MQ        15033955 358779
#11 OO           16026    550
#12 UA        89705524 754410
#13 US        11365778 252595
#14 VX        12902327  63876
#15 WN        12229203 151366
#16 YV          225395   9300

一个简洁的方法可能是将它堆叠得更长而不是更宽:

install.packages("nycflights13")
library(nycflights13)

flights <- nycflights13::flights %>%
  select(carrier,distance,hour)

by_carrier <- purrr::map_dfr( c('distance','hour'), function(x) {
  flights %>% 
    dplyr::group_by(carrier) %>%
    dplyr::summarize(n = sum(!!as.name(x))) %>%
    dplyr::mutate(key = x)
})

如果您仍希望 for 循环附加列,您可以使用 !!as.name() 功能两次,例如

by_carrier <- NULL
for ( i in c('distance','hour')) {   
  df <- 
    flights %>%
    dplyr::group_by(carrier) %>%
    dplyr::summarize(!!as.name(i) := sum(!!as.name(i) ))
  by_carrier <- bind_cols(by_carrier,df)
}

尽管您必须在该列之后清理 carrier 列。