Использование применять семейство и несколько функций в списках в R - PullRequest
0 голосов
/ 23 апреля 2019

У меня есть вопрос после моего ответа на этот вопрос по этому вопросу Соответствие атрибутов вершин по списку крайних списков R

Мое решение состояло в том, чтобы использовать для циклов, но мы всегда должны пытатьсяоптимизировать (векторизовать), когда мы можем.

Я пытаюсь понять, как я бы векторизовал решение, которое я сделал в посте.

Мое решение было

for(i in 1:length(graph_list)){
  graph_list[[i]]=set_vertex_attr(graph_list[[i]],"gender", value=attribute_df$gender[match(V(graph_list[[i]])$name, attribute_df$names)])
}

В идеале мы могли бы векторизовать это с помощью lapply, но у меня возникли некоторые затруднения с представлением, как это сделать.Вот что у меня есть

graph_lists_new=lapply(graph_list, set_vertex_attr, value=attribute_df$gender[match(V(??????????)$name, attribute_df$names)]))

Что мне неясно, так это то, что я бы добавил в часть с ??????.Внутри функции V() должен присутствовать каждый элемент в списке, но я не получаю то, что помещаю внутрь, когда использую lapply.

Все данные можно найти по ссылке, которую я разместил, но в любом случае вот эти данные

attribute_df<- structure(list(names = structure(c(6L, 7L, 5L, 2L, 1L, 8L, 3L, 
4L), .Label = c("Andy", "Angela", "Eric", "Jamie", "Jeff", "Jim", 
"Pam", "Tim"), class = "factor"), gender = structure(c(3L, 2L, 
3L, 2L, 3L, 1L, 1L, 2L), .Label = c("", "F", "M"), class = "factor"), 
    happiness = c(8, 9, 4.5, 5.7, 5, 6, 7, 8)), class = "data.frame", row.names = c(NA, 
-8L))



edgelist<-list(structure(list(nominator1 = structure(c(3L, 4L, 1L, 2L), .Label = c("Angela", 
"Jeff", "Jim", "Pam"), class = "factor"), nominee1 = structure(c(1L, 
2L, 3L, 2L), .Label = c("Andy", "Angela", "Jeff"), class = "factor")), class = "data.frame", row.names = c(NA, 
-4L)), structure(list(nominator2 = structure(c(4L, 1L, 2L, 3L
), .Label = c("Eric", "Jamie", "Oscar", "Tim"), class = "factor"), 
    nominee2 = structure(c(1L, 3L, 2L, 3L), .Label = c("Eric", 
    "Oscar", "Tim"), class = "factor")), class = "data.frame", row.names = c(NA, 
-4L)))

graph_list<- lapply(edgelist, graph_from_data_frame)

1 Ответ

2 голосов
/ 23 апреля 2019

Поскольку вам нужно использовать graph_list[[i]] несколько раз в вызове, чтобы использовать lapply, вам нужно написать пользовательскую функцию, такую ​​как эта анонимная функция. (Это тот же код, что и ваш цикл, я просто обернул его в function(x) и заменил все экземпляры graph_list[[i]] на x.)

graph_list = lapply(graph_list, function(x)
  set_vertex_attr(x, "gender", value = attribute_df$gender[match(V(x)$name, attribute_df$names)])
)

(Обратите внимание, что я не проверял это, но оно должно работать, если я не сделал опечатку.)

lapply это не векторизация --- это просто "скрытие петли". В этом случае, я думаю, что ваш цикл for - лучший способ сделать что-то, чем lapply. Тем более, что вы изменяете существующих объектов, ваш простой цикл for будет, вероятно, более эффективным, чем решение lapply, а также более читабельным.

Когда мы говорим о векторизации для эффективности, мы почти всегда имеем в виду атомные векторы, а не list с. (Это векторизация , в конце концов, не lisizing .) Причина использования lapply и связанных функций (sapply, vapply, Map, большая часть * Пакет 1029 *) - это не эффективность компьютера, это удобочитаемость и человеческая эффективность при написании.

Допустим, у вас есть список фреймов данных, my_list = list(iris, mtcars, CO2). Если вы хотите получить количество строк для каждого из фреймов данных в списке и сохранить его в переменной, мы можем использовать цикл sapply или for:

# easy to write, easy to read
rows_apply = sapply(my_list, nrow)

# annoying to read and write
rows_for = integer(length(my_list))
for (i in seq_along(my_list)) rows_for[i] = nrow(my_list[[i]])

Но чем сложнее становится ваша задача, тем более читаемым становится цикл for по сравнению с подобной альтернативой. В вашем случае я бы предпочел цикл for.


Подробнее об этом см. Старый вопрос Применяется ли больше, чем синтаксический сахар? . С тех пор, как эти ответы были написаны, R был обновлен и теперь включает компилятор "точно в срок", который еще больше ускоряет циклы for относительно применения. Из почти 10-летних ответов вы увидите, что иногда *apply на немного быстрее, чем цикл for. Начиная с JIT-компилятора, я думаю, вы найдете обратное: в большинстве случаев for цикл немного быстрее, чем *apply.

Но в обоих этих случаях, если вы не делаете что-то абсолютно тривиально внутри for / apply, все, что вы делаете внутри для / apply, будет доминировать над временем .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...