R - векторизованная условная замена

Здравствуйте, я пытаюсь манипулировать списком чисел, и я хотел бы сделать это без цикла for, используя быструю нативную операцию в R. Псевдокод для манипуляции:

By default the starting total is 100 (for every block within zeros)

From the first zero to next zero, the moment the cumulative total falls by more than 2% replace all subsequent numbers with zero.

Do this far all blocks of numbers within zeros

The cumulative sums resets to 100 every time

Например, если следующие данные были моими:

d <- c(0,0,0,1,3,4,5,-1,2,3,-5,8,0,0,-2,-3,3,5,0,0,0,-1,-1,-1,-1);

Результаты будут:

0 0 0 1 3 4 5 -1 2 3 -5 0 0 0 -2 -3 0 0 0 0 0 -1 -1 -1 0

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

Заранее спасибо.

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

d <- c(0,0,0,1,3,4,5,-1,2,3,-5,8,0,0,-2,-3,3,5,0,0,0,-1,-1,-1,-1);
ans <- d;
running_total <- 100;
count <- 1;
max <- 100;
toggle <- FALSE;
processing <- FALSE;

for(i in d){
  if( i != 0 ){  
       processing <- TRUE; 
       if(toggle == TRUE){
          ans[count] = 0;  
       }
       else{
         running_total = running_total + i;

          if( running_total > max ){ max = running_total;}
          else if ( 0.98*max > running_total){
              toggle <- TRUE;  
          }
      }
   }

   if( i == 0 && processing == TRUE )
   { 
       running_total = 100; 
       max = 100;
       toggle <- FALSE;
   }
   count <- count + 1;
}
cat(ans)
 user148092607 июл. 2012 г., 22:26
Спасибо погоня я обновил пост с кодом. Спасибо за предложение.
 Chase07 июл. 2012 г., 21:59
покажите нам свой цикл for и то, что вы уже пробовали
 42-08 июл. 2012 г., 00:05
Функция Reduce полезна для последовательной обработки векторов, но я не могу понять, что вы пытаетесь сделать.max=100 назначение намного выше, чем любое из чисел во входном векторе и "обработка" переменная никогда не инициализируется, так что, насколько я могу видеть, "toggle" остается ИСТИНОЙ навсегда после первой встречи с ненулевым. Это может помочь, если будут предложены некоторые предыстории проблемы.
 user148092608 июл. 2012 г., 00:35
Привет DWin Пожалуйста, примите мои искренние извинения. В попытке украсить код в формате stackoverflow я случайно удалил пару строк. Я обновил его рабочей версией, и переключение и обработка теперь работают, как и ожидалось. Вы должны быть в состоянии скопировать вставить и запустить его.

Ответы на вопрос(1)

как преобразовать ваш цикл в векторизованные операции. Однако есть два довольно простых варианта для значительного улучшения производительности. Во-первых, просто положить ваш цикл вR функция и использоватьcompiler пакет для предварительной компиляции. Второй немного более сложный вариант - перевестиR петля вc++ цикл и использоватьRcpp пакет, чтобы связать его сR функция. Тогда вы звонитеR функция, которая передает егоc++ код, который быстро. Я показываю оба эти варианта и время. Я хочу с благодарностью поблагодарить Александра Бужара из listserv Rcpp, который помог мне с проблемой указателя, которую я не понимал.

Во-первых, вот вашR цикл как функция,foo.r.

## Your R loop as a function
foo.r <- function(d) {
  ans <- d
  running_total <- 100
  count <- 1
  max <- 100
  toggle <- FALSE
  processing <- FALSE

  for(i in d){
    if(i != 0 ){
      processing <- TRUE
      if(toggle == TRUE){
        ans[count] <- 0
      } else {
        running_total = running_total + i;
        if (running_total > max) {
          max <- running_total
        } else if (0.98*max > running_total) {
          toggle <- TRUE
        }
      }
    }
    if(i == 0 && processing == TRUE) {
      running_total <- 100
      max <- 100
      toggle <- FALSE
    }
    count <- count + 1
  }
  return(ans)
}

Теперь мы можем загрузитьcompiler упаковать и скомпилировать функцию и вызвать ееfoo.rcomp.

## load compiler package and compile your R loop
require(compiler)
foo.rcomp <- cmpfun(foo.r)

Это все, что нужно для маршрута компиляции. Это всеR и, очевидно, очень легко. Теперь дляc++ подход, мы используемRcpp пакет, а такжеinline пакет, который позволяет нам «встроить»c++ код. То есть нам не нужно создавать исходный файл и компилировать его, мы просто включаем его вR код и компиляция обрабатывается для нас.

## load Rcpp package and inline for ease of linking
require(Rcpp)
require(inline)

## Rcpp version
src <- '
  const NumericVector xx(x);
  int n = xx.size();
  NumericVector res = clone(xx);
  int toggle = 0;
  int processing = 0;
  int tot = 100;
  int max = 100;

  typedef NumericVector::iterator vec_iterator;
  vec_iterator ixx = xx.begin();
  vec_iterator ires = res.begin();
  for (int i = 0; i < n; i++) {
    if (ixx[i] != 0) {
      processing = 1;
      if (toggle == 1) {
        ires[i] = 0;
      } else {
        tot += ixx[i];
        if (tot > max) {
          max = tot;
        } else if (.98 * max > tot) {
            toggle = 1;
          }
      }
    }

   if (ixx[i] == 0 && processing == 1) {
     tot = 100;
     max = 100;
     toggle = 0;
   }
  }
  return res;
'

foo.rcpp <- cxxfunction(signature(x = "numeric"), src, plugin = "Rcpp")

Теперь мы можем проверить, что мы получаем ожидаемые результаты:

## demonstrate equivalence
d <- c(0,0,0,1,3,4,5,-1,2,3,-5,8,0,0,-2,-3,3,5,0,0,0,-1,-1,-1,-1)
all.equal(foo.r(d), foo.rcpp(d))

Наконец, создайте гораздо большую версиюd повторив это 10e4 раза. Тогда мы можем запустить три разные функции, чистоR код, скомпилированныйR код иR функция связана сc++ код.

## make larger vector to test performance
dbig <- rep(d, 10^5)

system.time(res.r <- foo.r(dbig))
system.time(res.rcomp <- foo.rcomp(dbig))
system.time(res.rcpp <- foo.rcpp(dbig))

Который в моей системе дает:

> system.time(res.r <- foo.r(dbig))
   user  system elapsed 
  12.55    0.02   12.61 
> system.time(res.rcomp <- foo.rcomp(dbig))
   user  system elapsed 
   2.17    0.01    2.19 
> system.time(res.rcpp <- foo.rcpp(dbig))
   user  system elapsed 
   0.01    0.00    0.02 

СкомпилированныйR код занимает около 1/6 времени без компиляцииR Код занимает всего 2 секунды, чтобы оперировать вектором 2,5 миллиона.c++ код на порядки быстрее, чем скомпилированныйR код, требующий всего 0,02 секунды для завершения. Помимо начальной настройки, синтаксис для основного цикла почти идентичен вR а такжеc++ так что вы даже не теряете ясности. Я подозреваю, что даже если части или весь ваш цикл может быть векторизован вR, вам будет очень больно, чтобы побить производительностьR функция связана сc++, И наконец, просто для доказательства:

> all.equal(res.r, res.rcomp)
[1] TRUE
> all.equal(res.r, res.rcpp)
[1] TRUE

Различные функции возвращают одинаковые результаты.

 08 июл. 2012 г., 03:03
Хм, вроде как пробивает дыру во всей «не программируй R», как это на С ++, и ожидай, что она будет эффективной ». аргумент ... это хорошо - я кое-что узнал здесь. THhanks.
 user148092608 июл. 2012 г., 11:32
То же самое спасибо, что нашли время, чтобы объяснить, как использовать C ++
 08 июл. 2012 г., 17:51
Вы очень кстати. @ Я думаю, что "не программирую на R, как это на C ++" все еще держит хотя бы в общем. Сила R все еще в простоте создания прототипа. Если бы был хороший способ векторизации вопроса, держу пари, что все необходимые строки кода были бы сокращены пополам. Например, вы можете получить суммы строк матрицы, просматривая каждую строку и суммируя, или простоrowSums(), Я должен также указать, что дляRcpp решение для работы вам нужноc++ компилятор. Возможно встроенный в * nix, но в Windows вы можете получитьRtools.
 user148092608 июл. 2012 г., 22:03
Да, именно поэтому я думаю, что комбинация векторизации и компиляции в конечном итоге станет идеальным решением. Я все еще пытаюсь преобразовать некоторые элементы этого цикла, используя функцию Reduce, как предлагалось ранее DWin. Дам знать, если мне повезет. Еще раз спасибо за изучение этого всем.
 08 июл. 2012 г., 23:02
Я думаю, что сложность векторизации заключается в том, что все зависит от предыдущего шага. Это не означает, что, подумав, вы сможете переделать свой алгоритм другим способом, но нет ничего очевидного с точки зрения кодирования для векторизации. Вы не выполняете какую-либо операцию над каждым элементом вектора (который относительно легко векторизовать), вы выполняете различные операции в зависимости от предыдущих результатов, что требует наличия упомянутых предыдущих результатов.

Ваш ответ на вопрос