Как использовать lapply для преобразования определенных значений в список фреймов данных - PullRequest
0 голосов
/ 18 октября 2018

Я ищу помощь для преобразования цикла for в lapply или аналогичную функцию.

У меня есть list подобных data.frame с, каждый из которых содержит

  • столбец индикатора ('a')
  • столбец значения ('b')

Я хочу инвертировать значения в столбце b для каждого фрейма данных, но только для конкретных индикаторов.Например, инвертируйте все значения в 'b', которые имеют показатель 2 в столбце a.

Вот некоторые примеры данных:

x = data.frame(a = c(1, 2, 3, 2),  b = (seq(from = .1, to = 1, by = .25)))
y = data.frame(a = c(1, 2, 3, 2),  b = (seq(from = 1, to = .1, by = -.25)))
my_list <- list(x = , y = y)

my_list
$x
  a    b
1 1 0.10
2 2 0.35
3 3 0.60
4 2 0.85

$y
  a    b
1 1 1.00
2 2 0.75
3 3 0.50
4 2 0.25

Мой желаемый результат выглядит следующим образом:

my_list
$x
  a    b
1 1 0.10
2 2 0.65
3 3 0.60
4 2 0.15

$y
  a    b
1 1 1.00
2 2 0.25
3 3 0.50
4 2 0.75

Я могу получить желаемый результат с помощью следующего цикла:

for(i in 1:length(my_list)){
    my_list[[i]][my_list[[i]]['a'] == 2, 'b'] <-
        1 - my_list[[i]][my_list[[i]]['a'] == 2, 'b']
}

НО.Когда я пытаюсь свернуть это в форму счастья следующим образом:

    invertfun <- function(inputDF){
    inputDF[inputDF['a'] == 2, 'b'] <- 1 - inputDF[inputDF['a'] == 2, 'b']
    }
resultList <- lapply(X = my_list, FUN = invertfun)

, я получаю список только с инвертированными значениями:

resultList
$x
[1] 0.65 0.15

$y
[1] 0.25 0.75

Чего мне здесь не хватает?Я пытался применить (каламбур) выводы из:

как использовать lapply вместо цикла for, чтобы выполнить вычисление списка кадров данных в R

Буду признателен за любые идеи или альтернативные решения.Я пытаюсь поднять свои навыки R на следующий уровень, и apply и подобные функции, похоже, являются ключевыми.

Ответы [ 3 ]

0 голосов
/ 19 октября 2018

См. Ответ Ронака выше для довольно элегантного решения, использующего transform() или map(), но для тех, кто следует по моим стопам, мое оригинальное решение будет работать, если я добавлю строку в пользовательскую функцию для возврата полных данныхфрейм выглядит так:

invertfun <- function(inputDF){
    inputDF[inputDF['a'] == 2, 'b'] <- 1 - inputDF[inputDF['a'] == 2, 'b']
return(inputDF)    
}

resultList <- lapply(X = my_list, FUN = invertfun)

ОБНОВЛЕНИЕ - При дальнейшем тестировании это решение выдает Error in x[[jj]][iseq] <- vjj : replacement has length zero, когда нужное значение 'a' не существует ни в одном из фреймов данных.Поэтому лучше не идти по этому пути и использовать принятый ответ выше.

0 голосов
/ 20 октября 2018

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

for (i in seq_along(my_list)) {
    my_list[[i]] <- within(my_list[[i]], {
        b[a==2] <- 1 - b[a==2]
    })}

Если мы заменим within на with в примере выше,мы получаем вывод из вашего исходного решения, т.е. lapply(X = my_list, FUN = invertfun).

То есть вместо изменения list на месте последние решения заменяют list элементов новыми vector s.

0 голосов
/ 18 октября 2018

Мы можем использовать lapply для циклического перебора каждого списка и изменения столбца b на основе значения в столбце a.

my_list[] <- lapply(my_list, function(x) transform(x, b = ifelse(a==2, 1-b, b)))

my_list
#[[1]]
#  a    b
#1 1 0.10
#2 2 0.65
#3 3 0.60
#4 2 0.15

#[[2]]
#  a    b
#1 1 1.00
#2 2 0.25
#3 3 0.50
#4 2 0.75

То же самое можно сделать с помощью map от purrr

library(purrr)
map(my_list, function(x) transform(x, b = ifelse(a==2, 1-b, b)))
...