Как продолжать сбрасывать первое значение, пока сумма вектора не станет меньше 20? - PullRequest
0 голосов
/ 16 января 2019

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

Я пробовал и цикл for, и цикл while, и не могу найти решение.

vec <- c(3,5,3,4,3,9,1,8,2,5)

short <- function(vec){

 for (i in 1:length(vec)){
    while (!is.na((sum(vec)) < 20)){
      vec <- vec[i+1:length(vec)]
      #vec.remove(i)
  }
}

Ожидаемый результат должен быть: 1,8,2,5 что меньше 20.

Ответы [ 4 ]

0 голосов
/ 16 января 2019

Я бы пошел с Reduce

vec[Reduce(f = "+", x = vec, accumulate = T, right = T) < 20]
##[1] 1 8 2 5

В качестве альтернативы, определите Reduce с помощью функции sum с условным аргументом na.rm = T для того, чтобы при желании получить NA:

vec2 <- c(3, 2, NA, 4, 5, 1, 2, 3, 4, 9, NA, 1, 2)
vec2[Reduce(f = function(a,b) sum(a, b, na.rm = T), x = vec2, accumulate = TRUE, right = T) < 20]
##[1]  3  4  9 NA  1  2

Я считаю, что опция Reduce начинается справа (конец целочисленного вектора), и, следовательно, нет необходимости сначала переворачивать ее, удобно.

0 голосов
/ 16 января 2019

Вам необходимо каждый раз удалять первое значение, поэтому ваш цикл while должен быть

while (sum(x, na.rm = TRUE) >= 20) {
    x <- x[-1]
}

#[1] 1 8 2 5
0 голосов
/ 16 января 2019

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

rev( rev(vec)[cumsum( replace( rev(vec), is.na( rev(vec) ), 0 ) ) < 20] )
#[1] 1 8 2 5

примечание: «позаимствовал» обработку NA из ответа @ Ronak

образец данных
vec = c(3, 2, NA, 4, 5, 1, 2, 3, 4, 9, NA, 1, 2)

тесты

microbenchmark::microbenchmark(
  Sotos = { 
    while (sum(vec, na.rm = TRUE) >= 20) {
      vec <- vec[-1] 
    } 
  },
  Ronak = tail(vec, sum(cumsum(replace(rev(vec), is.na(rev(vec)), 0)) < 20)),
  Wimpel = rev( rev(vec)[cumsum( replace( rev(vec), is.na( rev(vec) ), 0 ) ) < 20]),
  WimpelMarkus = vec[rev(cumsum(rev(replace(vec, is.na(vec), 0))) < 20)]
  )


# Unit: microseconds
#         expr      min       lq       mean    median        uq      max neval
#        Sotos 2096.795 2127.373 2288.15768 2152.6795 2425.4740 3071.684   100
#        Ronak   30.127   33.440   42.54770   37.2055   49.4080  101.827   100
#       Wimpel   13.557   15.063   17.65734   16.1175   18.5285   38.261   100
# WimpelMarkus    7.532    8.737   12.60520   10.0925   15.9680   45.491   100
0 голосов
/ 16 января 2019

Глядя на ожидаемый результат, похоже, что вы хотите сбросить значения до тех пор, пока сумма оставшихся значений не станет меньше 20.

Мы можем создать функцию

drop_20 <- function(vec) {
  tail(vec, sum(cumsum(rev(vec)) < 20))
}

drop_20(vec)
#[1] 1 8 2 5

Попытка на другом входе

drop_20(1:10)
#[1]  9 10

Сломая функцию, сначала vec

vec = c(3,5,3,4,3,9,1,8,2,5)

Мы тогда rev ошибаемся

rev(vec)
#[1] 5 2 8 1 9 3 4 3 5 3

возьмите накопленную сумму (cumsum)

cumsum(vec)
#[1]  3  8 11 15 18 27 28 36 38 43

Выясните, сколько колбасных изделий меньше 20

cumsum(rev(vec)) < 20
 #[1]  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE

sum(cumsum(rev(vec)) < 20)
#[1] 4

и, наконец, подмножество этих последних записей, используя tail.


Небольшое изменение в коде, и оно должно обрабатывать NA с

drop_20 <- function(vec) {
   tail(vec, sum(cumsum(replace(rev(vec), is.na(rev(vec)), 0)) < 20))
}

vec = c(3, 2, NA, 4, 5, 1, 2, 3, 4, 9, NA, 1, 2)
drop_20(vec)
#[1]  3  4  9 NA  1  2

Логика заключается в том, что мы replace NA с нулями, а затем принять cumsum

...