Как выполнить итерацию по фрейму данных с использованием символьного вектора и вычислить среднее значение для совпадающих элементов в R

#r #dataframe #dplyr

#r #фрейм данных #dplyr

Вопрос:

У меня есть character vector и хочу выполнить итерацию по некоторым dataframes , рассмотреть совпадающие символы и отметить соответствующие значения и, наконец, взять среднее значение всех значений и сохранить результаты в новом dataframe .

Ниже приведен пример примера:

 ip <- c("John", "Amanda", "Aaron", "Peter", "Jolie")

dfs <- data.frame("names" = c('John','Peter','jucy'), "value1" = c(21, 24, 26), "value2" = c(20, 23, 32))
dfg <- data.frame("names" = c('Justin','John','Jill'), "value1" = c(35, 11, 10), "value2" = c(10, 28, 27))
dft <- data.frame("names" = c('Louis','Chan','John'), "value1" = c(42, 74, 26), "value2" = c(26, 53, 54))
dfr <- data.frame("names" = c('Ale','Terry','Tom'), "value1" = c(61, 34, 76), "value2" = c(28, 63, 38))
dfm <- data.frame("names" = c('Sam','Jolie','Peter'), "value1" = c(11, 84, 86), "value2" = c(50, 13, 68))
  

Ожидаемый результат:

 names  value1  value2
John    19.33  34
Peter   55     45.5
Jolie   84     13
  

Для Джона value1 = mean(c(21, 11, 26)) = 19.33 и value2 = mean(c(20, 28, 54)) = 34

Аналогично, для Питера value1 = mean(c(24, 86)) = 55 и value2 = mean(c(23,68)) = 45.5

Ответ №1:

Мы можем получить наборы данных в list с mget помощью, связать их вместе с bind_rows , сгруппировать по mean

 library(dplyr)
out <- mget(ls(pattern= '^df[sgtrms]$')) %>%
    bind_rows %>% 
    group_by(names) %>%
    summarise(across(everything(), mean, na.rm = TRUE))

out
# A tibble: 12 x 3
#   names  value1 value2
#   <chr>   <dbl>  <dbl>
# 1 Ale      61     28  
# 2 Chan     74     53  
# 3 Jill     10     27  
# 4 John     19.3   34  
# 5 Jolie    84     13  
# 6 jucy     26     32  
# 7 Justin   35     10  
# 8 Louis    42     26  
# 9 Peter    55     45.5
#10 Sam      11     50  
#11 Terry    34     63  
#12 Tom      76     38 
  

Если нам нужно filter на основе ip

 mget(ls(pattern= '^df[sgtrms]$')) %>%
    bind_rows %>% 
    filter(names %in% ip) %>%
    group_by(names) %>%
    summarise(across(everything(), mean, na.rm = TRUE))
# A tibble: 3 x 3
#  names value1 value2
#  <chr>  <dbl>  <dbl>
#1 John    19.3   34  
#2 Jolie   84     13  
#3 Peter   55     45.5
  

Или с помощью base R с aggregate

 aggregate(.~ names, subset(do.call(rbind, 
      mget(ls(pattern = "^df[sgtrms]$"))),  names %in% ip), mean)
  

Ответ №2:

Другим base R подходом было бы привязать все ваши фреймы данных, применить фильтр на основе ip вектора и, наконец, объединить с mean() :

 #Vector
ip <- c("John", "Amanda", "Aaron", "Peter", "Jolie")
#Data
dfs <- data.frame("names" = c('John','Peter','jucy'),
                  "value1" = c(21, 24, 26), "value2" = c(20, 23, 32),stringsAsFactors = F)
dfg <- data.frame("names" = c('Justin','John','Jill'),
                  "value1" = c(35, 11, 10), "value2" = c(10, 28, 27),stringsAsFactors = F)
dft <- data.frame("names" = c('Louis','Chan','John'),
                  "value1" = c(42, 74, 26), "value2" = c(26, 53, 54),stringsAsFactors = F)
dfr <- data.frame("names" = c('Ale','Terry','Tom'),
                  "value1" = c(61, 34, 76), "value2" = c(28, 63, 38),stringsAsFactors = F)
dfm <- data.frame("names" = c('Sam','Jolie','Peter'),
                  "value1" = c(11, 84, 86), "value2" = c(50, 13, 68),stringsAsFactors = F)
#Bind all
dfmacro <- rbind(dfs,dfg,dft,dfr,dfm)
#Filter based on ip
dfmacro2 <- dfmacro[dfmacro$names %in% ip,]
#Aggregate
aggregate(cbind(value1,value2)~names,data=dfmacro2,mean)
  

Вывод:

   names   value1 value2
1  John 19.33333   34.0
2 Jolie 84.00000   13.0
3 Peter 55.00000   45.5
  

Ответ №3:

Для полноты картины вот два варианта, которые используют data.table .

OP запросил выполнить итерацию по фреймам данных, извлечь нужные строки путем сопоставления имен и вычислить среднее значение значений для каждого имени во всех извлеченных строках.

Все опубликованные до сих пор ответы предполагают другой порядок операций, который сначала объединяет фреймы данных, затем извлекает нужные строки и, наконец, агрегирует по имени.

Два варианта, предложенные в этом ответе, используют один и тот же маршрут.

 library(data.table)
rbindlist(list(dfs, dfg, dft, dfr, dfm))[
  names %chin% ip, lapply(.SD, mean), keyby = names]
  
    names   value1 value2
1:  John 19.33333   34.0
2: Jolie 84.00000   13.0
3: Peter 55.00000   45.5
  

rbindlist() объединяет все строки, names %chin% ip выбирает нужные строки, lapply(.SD, mean) вычисляет среднее значение по всем столбцам, кроме names столбца, который используется для группировки.

Альтернативный подход агрегируется в объединении:

 library(data.table)
rbindlist(list(dfs, dfg, dft, dfr, dfm))[
  .(ip), on = .(names = V1), nomatch = NULL, lapply(.SD, mean), keyby = .EACHI] 
  

Здесь объединенные строки объединяются с ip , не совпадающие строки игнорируются. В рамках объединения данные группируются и агрегируются одновременно.