web-dev-qa-db-fra.com

Remplacer les valeurs négatives par zéro

Nous voulons définir toutes les valeurs d'un tableau zéro qui sont négatives.

J'ai essayé beaucoup de choses mais je n'ai pas encore trouvé de solution de travail. J'ai pensé à une boucle for avec condition, mais cela ne semble pas fonctionner.

#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]}
}
21
Fabian Stolz

Merci pour l'exemple reproductible. C'est un truc R assez basique. Vous pouvez attribuer aux éléments sélectionnés d'un vecteur (notez qu'un tableau a des dimensions, et ce que vous avez donné est un vecteur et non un tableau):

> 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

Guerres de référence!

@James a trouvé une méthode encore plus rapide et l'a laissée dans un commentaire. Je l'ai voté, ne serait-ce que parce que je sais que sa victoire sera de courte durée.

Tout d'abord, j'essaie de compiler, mais cela ne semble aider personne:

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

compiled comparison

Mais attendez! Dirk a écrit ce truc Rcpp. Un incompétent complet en C++ peut-il lire son article JSS, adapter son exemple et écrire la fonction la plus rapide de tous? Restez à l'écoute, chers auditeurs.

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

with rcpp comparison

C'est affirmatif, capitaine.

Cela modifie l'entrée p même si vous ne l'affectez pas. Si vous voulez éviter ce comportement, vous devez cloner:

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")

Ce qui tue malheureusement l'avantage de la vitesse.

49
Ari B. Friedman

J'utiliserais pmax car ifelse peut parfois être un peu lent et le remplacement de sous-ensemble crée un vecteur supplémentaire (ce qui peut être un problème avec les grands ensembles de données).

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

Le remplacement de sous-ensemble est de loin le plus rapide:

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

16
Joshua Ulrich

Alternativement, vous pouvez également utiliser ifelse:

ifelse(pred_precipitation < 0, 0, pred_precipitation)
10
johannes

Si votre objet principal est un tibble ou un dataframe, vous pouvez également utiliser le package tidy. En comparaison avec le remplacement proposé par Ari B. Friedman, le remplacement pourrait être écrit "à la volée" et combiné avec d'autres mutations.

Un exemple utilisant dplyr et les tuyaux %>% Ressemblerait à ceci:

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

Vous pouvez ajouter d'autres mutations (c'est-à-dire de nouvelles variables) dans l'instruction mutate(). Un avantage que je vois dans ce type de codage est que vous ne courez pas le risque d'ignorer ou de réexécuter une étape de transformation individuelle, car ils sont tous regroupés dans une seule instruction. Par exemple, en ajoutant %>% View() dans RStudio, vous pouvez déjà prévisualiser le résultat. Cependant, le résultat n'est encore stocké nulle part ("à la volée"). De cette façon, vous gardez votre espace de noms/environnement propre lors de la modification du code.

2
Simon Stolz