2010-10-28 16 views
27
x = seq(0.1,10,0.1) 
y <- if (x < 5) 1 else 2 

Quiero que el if funcione en cada caso en lugar de operar en todo el vector. ¿Qué debo cambiar?Declaración vectorizada de IF en R?

+0

¿es esto también es posible con un 'si (condición) { } 'else (condición) {} construcción? Si los argumentos sí no son un poco más complicados, a veces es difícil de leer. Tuve el mismo problema que Christian, usado en otro caso tal como lo sugerí aquí, que funciona bien, pero se ve feo. Hasta ahora estoy usando expression ({yes}), que está bien, pero aún me pregunto si hay algo que hacer con if y else. –

Respuesta

42
x <- seq(0.1,10,0.1) 

> x 
    [1] 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 
[16] 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0 
[31] 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 4.0 4.1 4.2 4.3 4.4 4.5 
[46] 4.6 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0 
[61] 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7.0 7.1 7.2 7.3 7.4 7.5 
[76] 7.6 7.7 7.8 7.9 8.0 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 9.0 
[91] 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 10.0 

> ifelse(x < 5, 1, 2) 
    [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 
[38] 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 
[75] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 
+2

Esto también es muy útil para solo reemplazar algunos valores: p. 'x = ...; x [x <5] = 1' es 'ifelse (x <5, 1, x)' –

+0

¿Qué sucede si deseo devolver los elementos de otro vector? – skan

+1

@skan try 'y [x <5]'. Asumiendo longitudes de 'x' y' y' son idénticas –

11

y <- if (x < 5) 1 else 2 no opera en todo el vector (el aviso que recibe le indica sólo el primer elemento de la condición será utilizado). ¿Quieres ifelse:

y <- ifelse(x < 5, 1, 2) 

ifelse opera en todo el vector de lógica, elemento por elemento. if solo acepta un valor lógico. Ver ?"if" y ?ifelse

11

Para completar: en los vectores grandes, puede usar los índices para acelerar las cosas (lo hacemos a menudo en simulaciones, donde las funciones suelen ejecutarse de 1000 a 10000 veces). Pero mientras no sea necesario, simplemente use ifelse. Esto se lee mucho más fácil.

> set.seed(100) 
> x <- runif(1000,1,10) 

> system.time(replicate(10000,{ 
+  y <- ifelse(x < 5,1,2) 
+ })) 
    user system elapsed 
    2.56 0.08 2.64 

> system.time(replicate(10000,{ 
+ y <- rep(2,length(x)) 
+ y[x < 5]<- 1 
+ })) 
    user system elapsed 
    0.48 0.00 0.48 
+3

Puede reducir ese tiempo aún más. Mi máquina hizo el segundo método en 0.436 (aunque fue más lento en el primer método), pero esto mejoró en otro 200%: system.time (replicar (10000, { y <- (y <5) + 2 * ! (y <5) })) sistema de usuario transcurrido 0.101 0.021 0.128 –

+0

@Dwin: ¡Muy buena solución! Gracias. Pero en mi máquina, solo funciona marginalmente más rápido (0.47 comparado a 0.48) –

+5

Cuidado: sus dos ejemplos no son equivalentes si 'x' contiene elementos' NA' (que permanecerían 'NA' en el primero pero se les asignaría' 1 'por el segundo). – jbaums

0

Siguiendo con la publicación anterior, puede usar y modificar los elementos de un vector que cumpla los criterios. En mi opinión, si no es más costoso calcular más rápido uno siempre debería hacerlo.

x = seq(0.1,10,0.1) 
y <- rep(2,length(x)) 
y[x<5] <- x[x<5]*2 

El código de la publicación anterior es el mejor para responder a la pregunta. Pero si tuviera que usar el código anterior que haría:

x = seq(0.1,10,0.1) 
y <- rep(2,length(x)) 
y[x<5] <- x[x<5]*0 +1 
+0

Las respuestas pueden subir o bajar dependiendo de los votos, por lo que en relación con "la publicación anterior" podría ser problemático. –

1

También puedes, simplemente crear un vector lógico y 1 a ella

x <- seq(0.1, 10, 0.1) # Your data set 
(x >= 5) + 1 
# [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 
# [92] 2 2 2 2 2 2 2 2 2 

Si le gustaría comparar el rendimiento, sería la solución más rápida

set.seed(100) 
x <- runif(1e6, 1, 10) 

RL <- function(x) y <- ifelse(x < 5,1,2) 
JM <- function(x) {y <- rep(2, length(x)); y[x < 5] <- 1} 
DA <- function(x) y <- (x >= 5) + 1 

library(microbenchmark) 
microbenchmark(RL(x), 
       JM(x), 
       DA(x)) 

# Unit: milliseconds 
# expr  min  lq  mean median  uq  max neval 
# RL(x) 331.83448 366.52940 378.89182 374.99741 381.08659 609.21218 100 
# JM(x) 38.72894 42.18745 44.36493 43.25086 44.09626 82.76168 100 
# DA(x) 10.01644 11.96482 14.21593 13.17825 14.12930 53.76923 100 
+0

Para un enfoque más simple, puede modificar su código a 'function (x) y <- (x > = 5L) + 1L', pero en general es una respuesta agradable e interesante para ver qué lento es, en comparación,' ifelse'. –

0
nzMean <- function(x) { mean(x[x!=-1],na.rm=TRUE)} 

nzMin <- function(x) {min(x[x!=-1],na.rm=TRUE)} 

nzMax <- function(x) { max(x[x!=-1],na.rm=TRUE)} 

nzRange<-function(x) {nzMax(x)-nzMin(x)} 

nzSD <- function(x) { SD(x[x!=-1],na.rm=TRUE)} 

#following function works 
nzN1<- function(x) {ifelse(x!=-1,(x-nzMin(x))/nzRange(x) ,x) } 

#following is bad as it returns only 4 not 5 elements of vector 
nzN2<- function(x) {ifelse(x!=-1,(x[x!=-1]-nzMin(x))/nzRange(x) ,x) } 

#following is bad as it returns 5 elements of vector but not correct answer 
nzN3<- function(x) {ifelse(x!=-1,(x[x!=-1]-nzMin(x))/nzRange(x) ,-1) } 

y<-c(1,-1,-20,2,4) 
a<-nzMean(y) 
b<-nzMin(y) 
c<-nzMax(y) 
d<-nzRange(y) 
# test the working function 
z<-nzN1(y) 

print(z) 
Cuestiones relacionadas