Заменить отрицательные значения на ноль

Мы хотим установить все отрицательные значения в нулевом массиве.

Я много чего перепробовал, но пока не нашел рабочего решения. Я думал о цикле for с условием, но это, похоже, не работает.

#pred_precipitation is our array
pred_precipitation <-rnorm(25,2,4)     

for (i in nrow(pred_precipitation))
{
  if (pred_precipitation[i]<0) {pred_precipitation[i] = 0}
  else{pred_precipitation[i] = pred_precipitation[i]}
}

person Fabian Stolz    schedule 30.06.2012    source источник


Ответы (4)


Спасибо за воспроизводимый пример. Это довольно простая вещь для R. Вы можете назначить выбранным элементам вектора (обратите внимание, что массив имеет размеры, и то, что вы указали, является вектором, а не массивом):

> pred_precipitation[pred_precipitation<0] <- 0
> pred_precipitation
 [1] 1.2091281 0.0000000 7.7665555 0.0000000 0.0000000 0.0000000 0.5151504 0.0000000 1.8281251
[10] 0.5098688 2.8370263 0.4895606 1.5152191 4.1740177 7.1527742 2.8992215 4.5322934 6.7180530
[19] 0.0000000 1.1914052 3.6152333 0.0000000 0.3778717 0.0000000 1.4940469

Контрольные войны!

@James нашел еще более быстрый метод и оставил его в комментарии. Я поддержал его хотя бы потому, что знаю, что его победа будет недолгой.

Сначала я пытаюсь скомпилировать, но это, похоже, никому не помогает:

p <- rnorm(10000)
gsk3 <- function(x) { x[x<0] <- 0; x }
jmsigner <- function(x) ifelse(x<0, 0, x)
joshua <- function(x) pmin(x,0)
james <- function(x) (abs(x)+x)/2
library(compiler)
gsk3.c <- cmpfun(gsk3)
jmsigner.c <- cmpfun(jmsigner)
joshua.c <- cmpfun(joshua)
james.c <- cmpfun(james)

microbenchmark(joshua(p),joshua.c(p),gsk3(p),gsk3.c(p),jmsigner(p),james(p),jmsigner.c(p),james.c(p))
           expr      min        lq    median        uq      max
1     gsk3.c(p)  251.782  255.0515  266.8685  269.5205  457.998
2       gsk3(p)  256.262  261.6105  270.7340  281.3560 2940.486
3    james.c(p)   38.418   41.3770   43.3020   45.6160  132.342
4      james(p)   38.934   42.1965   43.5700   47.2085 4524.303
5 jmsigner.c(p) 2047.739 2145.9915 2198.6170 2291.8475 4879.418
6   jmsigner(p) 2047.502 2169.9555 2258.6225 2405.0730 5064.334
7   joshua.c(p)  237.008  244.3570  251.7375  265.2545  376.684
8     joshua(p)  237.545  244.8635  255.1690  271.9910  430.566

скомпилированное сравнение

Но ждать! Дирк написал эту вещь Rcpp. Может ли полный некомпетентный специалист по C ++ прочитать свою статью JSS, адаптировать свой пример и написать самую быструю функцию из всех? Следите за обновлениями, дорогие слушатели.

library(inline)
cpp_if_src <- '
  Rcpp::NumericVector xa(a);
  int n_xa = xa.size();
  for(int i=0; i < n_xa; i++) {
    if(xa[i]<0) xa[i] = 0;
  }
  return xa;
'
cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp")
microbenchmark(joshua(p),joshua.c(p),gsk3(p),gsk3.c(p),jmsigner(p),james(p),jmsigner.c(p),james.c(p), cpp_if(p))
         expr      min        lq    median        uq       max
1   cpp_if(p)    8.233   10.4865   11.6000   12.4090    69.512
2     gsk3(p)  170.572  172.7975  175.0515  182.4035  2515.870
3    james(p)   37.074   39.6955   40.5720   42.1965  2396.758
4 jmsigner(p) 1110.313 1118.9445 1133.4725 1164.2305 65942.680
5   joshua(p)  237.135  240.1655  243.3990  250.3660  2597.429

со сравнением rcpp

Это утвердительно, капитан.

Это изменяет ввод p, даже если вы его не назначаете. Если вы хотите избежать такого поведения, вам необходимо клонировать:

cpp_ifclone_src <- '
  Rcpp::NumericVector xa(Rcpp::clone(a));
  int n_xa = xa.size();
  for(int i=0; i < n_xa; i++) {
    if(xa[i]<0) xa[i] = 0;
  }
  return xa;
'
cpp_ifclone <- cxxfunction(signature(a="numeric"), cpp_ifclone_src, plugin="Rcpp")

Что, к сожалению, убивает преимущество в скорости.

person Ari B. Friedman    schedule 30.06.2012
comment
Ари и @DirkEddelbuettel: Действительно ли он изменяет p без назначения? Не похоже, когда я пробую. - person Aaron left Stack Overflow; 06.07.2012
comment
@Aaron См. Объяснение Дирка здесь: stackoverflow.com/questions/11300048/ - person Ari B. Friedman; 06.07.2012

Я бы использовал pmax, потому что ifelse иногда может быть немного медленным, а замена подмножества создает дополнительный вектор (что может быть проблемой с большими наборами данных).

set.seed(21)
pred_precipitation <- rnorm(25,2,4)
p <- pmax(pred_precipitation,0)

Замена подмножества, безусловно, самая быстрая:

library(rbenchmark)
gsk3 <- function(x) { x[x<0] <- 0; x }
jmsigner <- function(x) ifelse(x<0, 0, x)
joshua <- function(x) pmin(x,0)
benchmark(joshua(p), gsk3(p), jmsigner(p), replications=10000, order="relative")
         test replications elapsed relative user.self sys.self
2     gsk3(p)        10000   0.215 1.000000     0.216    0.000
1   joshua(p)        10000   0.444 2.065116     0.416    0.016
3 jmsigner(p)        10000   0.656 3.051163     0.652    0.000

autoplot microbenchmark

person Joshua Ulrich    schedule 30.06.2012
comment
+1 за тесты. Добавлен график таймингов (с использованием autoplot.microbenchmark в пакете taRifx) - person Ari B. Friedman; 30.06.2012
comment
@ gsk3: вау, что ты сделал, чтобы мое решение стало хуже? :П - person Joshua Ulrich; 30.06.2012
comment
Квит твой кветчинг или лицо мой талонз. Если серьезно, то я запустил его еще раз, и действительно оказалось, что разница между результатом rbenchmark и результатом microbenchmark, по крайней мере, в моей системе, является устойчивой. ~ 2x против ~ 3x разницы во времени. - person Ari B. Friedman; 30.06.2012
comment
@ gsk3: Я еще не начал кветчить! - person Joshua Ulrich; 30.06.2012
comment
Что ж, я бы использовал rbenchmark результаты для построения графика, за исключением того, что для них нет подходящего метода построения графика. - person Ari B. Friedman; 30.06.2012
comment
@ gsk3: тогда кто-нибудь должен написать один ... ‹ahem› - person Joshua Ulrich; 30.06.2012
comment
Не вини меня. Я написал микробенчмарк в taRifx и общую функцию автоплота в ggplot2, чтобы заставить его работать. - person Ari B. Friedman; 01.07.2012
comment
(abs(p)+p)/2 кажется еще быстрее - person James; 02.07.2012
comment
pmax также известен как медленный - person hadley; 19.10.2013
comment
Я бы больше доверял результатам микробенчмарка, чем rbenchmark - он использует намного более точный таймер и рандомизирует порядок репликаций, чего не может rbenchmark. - person hadley; 19.10.2013

В качестве альтернативы вы также можете использовать ifelse:

ifelse(pred_precipitation < 0, 0, pred_precipitation)
person johannes    schedule 30.06.2012

Если ваш основной объект - это тиббл или фрейм данных, вы также можете использовать пакет tidy. По сравнению с заменой, предложенной Ари Б. Фридманом, замену можно было бы написать «на лету» и скомбинировать с другими мутациями.

Пример использования dplyr и %>% каналов будет выглядеть так:

df %>% mutate(varA = if_else(varA < 0, 0, varA))

Вы можете добавить дополнительные мутации (т. Е. Новые переменные) в оператор mutate(). Преимущество, которое я вижу в этом типе кодирования, заключается в том, что вы не рискуете пропустить или повторно выполнить отдельный шаг преобразования, поскольку все они сгруппированы в одном операторе. Например, добавив %>% View() в RStudio, вы уже можете предварительно просмотреть результат. Однако результат пока нигде не сохраняется («на лету»). Таким образом вы сохраняете чистоту пространства имен / среды при изменении кода.

person Simon Stolz    schedule 12.02.2020