Как вычислить (i + 1 - i) в списке векторов - PullRequest
0 голосов
/ 27 ноября 2018

У меня есть список из 29 векторов (каждый вектор разной длины), например:

my_list
[1] 1 12 23 34 38 
[2] 2 12 21 38 47 56 71  
 .
 .
[29] 14 22 81 88 91 94   

Мне нужно вычислить ( i + 1 - i ) длякаждый вектор списка (my_list). Пример:

my_list
[1] (12-1) (23-12)  (34-23) (38-34)
[2] (12-2) (21-12)  (38-21) (47-38) (56-47) (71-56)
 .
 .
[29] (22-14) (81-22)  (88-81) (91-88) (94-91) 

Я пробовал цикл for:

res <- list()
for(i in 1:29) {
    for(j in 1:length(my_list[[i]])){
        my_res <- list(my_list[[i]][j+1] - my_list[[i]][j])
        res[i] <- my_res

Но результат дает только первое значение для каждого векторасписок:

res
[1] 11
[2] 10
 .
 .
[29] 8

Есть ли способ сделать это с помощью функций, похожих на применение?

1 Ответ

0 голосов
/ 27 ноября 2018

Я действительно не знаю о вашем двойном for цикле, но есть пара гораздо более эффективных способов решения этого типа проблемы.

Векторизация - это то, что R делает очень хорошо.Фактически, гораздо лучше, что методы грубой силы, которые являются естественными в некоторых языках, все еще могут работать в R, но значительно медленнее.

Примечание: у R * for циклов раньше было меньшеэффективнее, чем сейчас, так много людей все еще решительно не одобряют их использование в пользу функций из семейства apply.Два момента: этот факт больше не соответствует действительности;и это другой тип циклической конструкции, о котором я говорю здесь.Поэтому, когда я не одобряю циклы for в этом случае, это в пользу векторизации математики, а не apply ее.

Вот некоторые данные:

my_list <- list(
  c(1, 12, 23, 34, 38),
  c(2, 12, 21, 38, 47, 56, 71),
  c(14, 22, 81, 88, 91, 94)
)

Я продемонстрирую на одном векторе этого списка:

v <- my_list[[1]]
v

Я интерпретирую то, что вы сказали, как v[i+1] - v[i] для каждого i в последовательности индексов (кроме 1, посколькуv[0] не определено в R).Чтобы сделать это как вектор, это "начать со всех чисел, кроме первого, затем вычесть все числа, кроме последнего" .

v[-1]
# [1] 12 23 34 38
v[-length(v)]
# [1]  1 12 23 34
v[-1] - v[-length(v)]
# [1] 11 11 11  4

Это эффективно

c(12, 23, 34, 38) - c(1, 12, 23, 34)
c(12-1, 23-12, 34-23, 38-34)

Теперь, когда мы знаем, как сделать это эффективно один раз , давайте упростим эту операцию и сопоставим ее с каждым вектором в списке.В R есть функция, которая делает это для нас:

diff(v)
# [1] 11 11 11  4

, но в случае, если ваши будущие потребности включают более конкретные (не общие) операции, мы могли бы написать нашу собственную функцию для этой конкретной операции:

my_func <- function(vec) vec[-1] - vec[-length(vec)]

Теперь классическое использование одной из функций отображения: lapply применяет одну функцию к каждому элементу list и возвращает list одинаковой длины с возвращаемыми значениями.

Примечание: когда мне нужно выбрать между for и lapply (например), я спрашиваю себя, не волнуюсь о расчете по каждому элементу (например, в этом случае, где я хочуdiff вектора), или, если меня просто интересует побочный эффект (например, создание чего-либо, сохранение файлов).Если первое, то lapply или его родственник уместен;если последнее, часто for петли.Это не на 100% эвристика, но в целом это довольно хорошо.

lapply(my_list, my_func)
# [[1]]
# [1] 11 11 11  4
# [[2]]
# [1] 10  9 17  9  9 15
# [[3]]
# [1]  8 59  7  3  3

(аналогично, lapply(my_list, diff) работает.) Существуют аналогичные функции *apply* с немного различными преимуществами, требованиями иограничения.(Есть также несколько обучающих программ, которые уже входят в это, и SO не предназначен, чтобы быть учебным сайтом.)


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

  • , если вам нужно перебрать каждый элемент списка:
    • , предпочтительно не жесткий код 1:29, вместо этого используйте что-то, что зависит от самого вектора, например length(my_list), поэтому 1:length(my_list) может показаться подходящим (как вы правильно используете во втором цикле), но...
    • случилось, что этот список в какой-то момент имеет длину 0, но for (i in 1:0) делает , а не делает то, на что можно было бы надеяться.Чтобы было ясно, я хотел бы надеяться, что это ничего не сделает, но 1:0 разрешает в вектор, длину 2, значения 1 и 0 (и это просто неправильно в большинстве случаев, которые используют этот контроль потока).Я рекомендую заменить for (i in 1:length(my_list)) на for (i in seq_along(my_list)) или for (i in seq_len(length(my_list))) (seq_along предоставляет индексы для вектора / списка, он не будет давать чисел, если его список имеет длину 0; а seq_len разумно дает вектор 0-длины, еслиего аргумент равен 0. Оба можно найти в ?seq.)
  • , когда i равно 1, а j равно 2, вы храните list(12-1) в res[1];когда j равен 3, вы перезаписываете res[1] с помощью list(23-12), поэтому вы потеряли свои предыдущие вычисления в векторе 1. Именно поэтому каждый элемент в вашем списке имеет длину 1.
  • ваш внутренний цикл (j) проходит до конца вектора (length(my_list[[i]]));в этот момент my_list[[i]][j+1] указывает за конец вектора, поэтому он разрешается в NA (попробуйте my_list[[1]][999999]), поэтому все значения в res равны NA.Чтобы исправить это, используйте 1:(length(my_list[[i]])-1) или, предпочтительно, seq_length(my_list[[i]])[-1], чтобы отбросить первое (поэтому мы сделаем (j) - (j-1) вместо (j+1) - (j)).
    • Если вы должны сохранить логику индексации (j+1) - (j), тогда используйте что-то вроде seq_along(my_list[[i]])[-length(my_list[[i]])] или head(seq_along(my_list[[i]]),n=-1), где n=-1 означает все, кроме последнего.

Это исправленная версия вашего кода:

resouter <- list()
for (i in seq_along(my_list)) {
  resinner <- numeric(0)
  for (j in seq_along(my_list[[i]])[-1]) {
    resinner[j] <- my_list[[i]][j] - my_list[[i]][j-1]
  }
  resouter[[i]] <- resinner[-1] # since j starts at 2, first one is always NA
}
resouter
# [[1]]
# [1] 11 11 11  4
# [[2]]
# [1] 10  9 17  9  9 15
# [[3]]
# [1]  8 59  7  3  3

Но я думаю, что lapply(my_list, my_func) или даже lapply(my_list, diff) гораздо более лаконичны (и быстрее).

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