R lapply для списка списков, чтобы применить ту же функцию к предопределенным столбцам

#r #list #function #lapply

#r #Список #функция #lapply

Вопрос:

У меня есть список списков (2 df), и я хочу использовать lapply для выполнения той же функции для предопределенных столбцов в каждом df.

В частности, я хотел бы использовать функцию winsorize из DescTools пакета. На данный момент я знаю, как это сделать, указав все отдельные столбцы в команде function (x), что, однако, утомительно, если у меня много столбцов (см. Пример).

После применения функции должен быть возвращен весь список списков (все столбцы) с преобразованными переменными. В идеале преобразованные переменные дополняются «_w» (например, «price_w») или аналогичными, чтобы указать, что это переменные winsorized.

Мои данные выглядят следующим образом, хотя я хочу применить функцию только к предопределенным столбцам «цена» и «качество».

 id <- c(1, 5, 7, 9, 12)
country <- c("A", "A", "C", "E", "E")
price <- c(2.1, 4.6, 3.7, 2.9, 1.8)
quality <- c(3.1, 5.2, 3.3, 1.7, 0.9)
df1 <- cbind.data.frame(id, country, price, quality)

id <- c(2, 3, 4, 10, 14)
country <- c("F", "F", "A", "Z", "X")
price <- c(1.8, 5.2, 2.9, 4.6, 3.9)
quality <- c(4.3, 2.5, 6.9, 1.9, 0.8)
df2 <- cbind.data.frame(id, country, price, quality)

my.list <- list(df1, df2)

cols <- c("price", "quality")
  

Это то, что у меня есть до сих пор, что будет работать только для небольшого количества столбцов из-за необходимых изменений вручную:

 my.list <- lapply(my.list, function(x) {
  x$price_w <- DescTools::Winsorize(x$price, probs = c(.01, .99), na.rm = TRUE)
  x$quality_w <- DescTools::Winsorize(x$quality, probs = c(.01, .99), na.rm = TRUE)
  return(x)
})
  

Ответ №1:

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

 lapply(my.list, function(x) {
  x[paste0(cols, '_w')] <- lapply(x[cols], DescTools::Winsorize)
  x
})

#[[1]]
#  id country price quality price_w quality_w
#1  1       A   2.1     3.1    2.10      3.10
#2  5       A   4.6     5.2    4.42      4.82
#3  7       C   3.7     3.3    3.70      3.30
#4  9       E   2.9     1.7    2.90      1.70
#5 12       E   1.8     0.9    1.86      1.06

#[[2]]
#  id country price quality price_w quality_w
#1  2       F   1.8     4.3    2.02      4.30
#2  3       F   5.2     2.5    5.08      2.50
#3  4       A   2.9     6.9    2.90      6.38
#4 10       Z   4.6     1.9    4.60      1.90
#5 14       X   3.9     0.8    3.90      1.02
  

Комментарии:

1. Это работает отлично. Однако у меня есть короткий вопрос. В DescTools::Winsorize я должен указать x и соответствующие задачи, которые я хочу использовать. Должен ли я инициализировать функцию, а затем использовать ее внутри вложенного lapply, или есть способ ввести правильный x, потому что ни x, ни x [cols] не работают?

2. Вы можете передать probs следующим образом x[paste0(cols, '_w')] <- lapply(x[cols], DescTools::Winsorize, probs = c(0.1, 0.9))

Ответ №2:

Одним из вариантов purrr и dplyr может быть:

 map(.x = my.list,
    ~ .x %>%
     mutate(across(all_of(cols), 
                   list(w = ~ DescTools::Winsorize(., probs = c(.01, .99), na.rm = TRUE)))))

[[1]]
  id country price quality price_w quality_w
1  1       A   2.1     3.1   2.100     3.100
2  5       A   4.6     5.2   4.564     5.124
3  7       C   3.7     3.3   3.700     3.300
4  9       E   2.9     1.7   2.900     1.700
5 12       E   1.8     0.9   1.812     0.932

[[2]]
  id country price quality price_w quality_w
1  2       F   1.8     4.3   1.844     4.300
2  3       F   5.2     2.5   5.176     2.500
3  4       A   2.9     6.9   2.900     6.796
4 10       Z   4.6     1.9   4.600     1.900
5 14       X   3.9     0.8   3.900     0.844
  

Ответ №3:

вот data.table решение

 library( data.table )
library( DescTools )
#make df1 and df2 a data.table
my.list <- lapply( my.list, setDT )
#rund function on columns
lapply( my.list, function(x) {
  x[, paste0( (cols), "_w" ) := DescTools::Winsorize( .SD, 
                                                      probs = c(0.1, 0.9), 
                                                      na.rm = TRUE ), .SDcols = cols]
})

# 
# [[1]]
#    id country price quality price_w quality_w
# 1:  1       A   2.1     3.1     2.1      3.10
# 2:  5       A   4.6     5.2     4.6      4.66
# 3:  7       C   3.7     3.3     3.7      3.30
# 4:  9       E   2.9     1.7     2.9      1.70
# 5: 12       E   1.8     0.9     1.8      1.62
# 
# [[2]]
#    id country price quality price_w quality_w
# 1:  2       F   1.8     4.3     1.8      4.30
# 2:  3       F   5.2     2.5     5.2      2.50
# 3:  4       A   2.9     6.9     2.9      5.37
# 4: 10       Z   4.6     1.9     4.6      1.90
# 5: 14       X   3.9     0.8     3.9      1.70