sentencia - R Evaluación condicional cuando se usa el operador de tubería%>%
operador ternario javascript ejemplo (5)
Aquí hay un ejemplo rápido que aprovecha el
.
y si
ifelse
:
X<-1
Y<-T
X %>% add(1) %>% { ifelse(Y ,add(.,1), . ) }
En el caso
ifelse
, si
Y
es
TRUE
, agregará 1; de lo contrario, solo devolverá el último valor de
X
El
.
es un sustituto que le dice a la función dónde va la salida del paso anterior de la cadena, por lo que puedo usarla en ambas ramas.
Edite
Como señaló @BenBolker, es posible que no desee
ifelse
, por lo que aquí hay una versión
if
.
X %>%
add(1) %>%
{if(Y) add(.,1) else .}
Gracias a @Frank por señalar que debería usar
{
llaves entre mis declaraciones
if
y
ifelse
para continuar la cadena.
Cuando uso el operador de tubería
%>%
con paquetes como
dplyr
,
ggvis
,
dycharts
, etc., ¿cómo hago un paso condicionalmente?
Por ejemplo;
step_1 %>%
step_2 %>%
if(condition)
step_3
Estos enfoques no parecen funcionar:
step_1 %>%
step_2
if(condition) %>% step_3
step_1 %>%
step_2 %>%
if(condition) step_3
Hay un largo camino:
if(condition)
{
step_1 %>%
step_2
}else{
step_1 %>%
step_2 %>%
step_3
}
¿Hay una mejor manera sin toda la redundancia?
Aquí hay una variación en la respuesta proporcionada por @JohnPaul.
Esta variación utiliza la función
`if`
lugar de un compuesto
if ... else ...
library(magrittr)
X <- 1
Y <- TRUE
X %>% `if`(Y, . + 1, .) %>% multiply_by(2)
# [1] 4
Tenga en cuenta que en este caso las llaves no son necesarias alrededor de la función
`if`
, ni alrededor de una función
`if`
, solo alrededor de la declaración
if ... else ...
Sin embargo, si el marcador de posición de punto aparece solo en una llamada de función anidada, entonces
magrittr canalizará
por defecto el lado izquierdo en el primer argumento del lado derecho.
Este comportamiento se anula al encerrar la expresión entre llaves.
Tenga en cuenta la diferencia entre estas dos cadenas:
X %>% `if`(Y, . + 1, . + 2)
# [1] TRUE
X %>% {`if`(Y, . + 1, . + 2)}
# [1] 4
El marcador de posición de punto está anidado dentro de una llamada de función las dos veces que aparece en la función
`if`
desde entonces
. + 1
. + 1
y
. + 2
. + 2
se interpretan como
`+`(., 1)
y
`+`(., 2)
, respectivamente.
Entonces, la primera expresión devuelve el resultado de
`if`(1, TRUE, 1 + 1, 1 + 2)
, (curiosamente,
`if`
no se queja de argumentos adicionales no utilizados), y la segunda expresión está devolviendo el resultado de
`if`(TRUE, 1 + 1, 1 + 2)
, que es el comportamiento deseado en este caso.
Para obtener más información sobre cómo el operador del tubo
magrittr
trata el marcador de posición de punto, consulte el
archivo de ayuda
para
%>%
, en particular la sección sobre "Uso del punto para fines secundarios".
Creo que ese es un caso para
purrr::when
.
Resumamos algunos números si su suma es inferior a 25; de lo contrario, devuelve 0.
library("magrittr")
1:3 %>%
purrr::when(sum(.) < 25 ~ sum(.),
~0
)
#> [1] 6
when
devuelve el valor resultante de la acción de la primera condición válida.
Coloque la condición a la izquierda de
~
, y la acción a la derecha.
Arriba, solo usamos una condición (y luego un caso más), pero puede tener muchas condiciones.
Puede integrarlo fácilmente en una tubería más larga.
Me gusta
purrr::when
y las otras soluciones básicas proporcionadas aquí son geniales, pero quería algo más compacto y flexible, así que diseñé la función
pif
(pipe if), vea el código y el documento al final de la respuesta.
Los argumentos pueden ser expresiones de funciones (se admite la notación de fórmula) y la entrada se devuelve sin cambios por defecto si la condición es
FALSE
.
Usado en ejemplos de otras respuestas:
## from Ben Bolker
data.frame(a=1:2) %>%
mutate(b=a^2) %>%
pif(~b[1]>1, ~mutate(.,b=b^2)) %>%
mutate(b=b^2)
# a b
# 1 1 1
# 2 2 16
## from Lorenz Walthert
1:3 %>% pif(sum(.) < 25,sum,0)
# [1] 6
## from clbieganek
1 %>% pif(TRUE,~. + 1) %>% `*`(2)
# [1] 4
# from theforestecologist
1 %>% `+`(1) %>% pif(TRUE ,~ .+1)
# [1] 3
Otros ejemplos :
## using functions
iris %>% pif(is.data.frame, dim, nrow)
# [1] 150 5
## using formulas
iris %>% pif(~is.numeric(Species),
~"numeric :)",
~paste(class(Species)[1],":("))
# [1] "factor :("
## using expressions
iris %>% pif(nrow(.) > 2, head(.,2))
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 5.1 3.5 1.4 0.2 setosa
# 2 4.9 3.0 1.4 0.2 setosa
## careful with expressions
iris %>% pif(TRUE, dim, warning("this will be evaluated"))
# [1] 150 5
# Warning message:
# In inherits(false, "formula") : this will be evaluated
iris %>% pif(TRUE, dim, ~warning("this won''t be evaluated"))
# [1] 150 5
Función
#'' Pipe friendly conditional operation
#''
#'' Apply a transformation on the data only if a condition is met,
#'' by default if condition is not met the input is returned unchanged.
#''
#'' The use of formula or functions is recommended over the use of expressions
#'' for the following reasons :
#''
#'' /itemize{
#'' /item If /code{true} and/or /code{false} are provided as expressions they
#'' will be evaluated wether the condition is /code{TRUE} or /code{FALSE}.
#'' Functions or formulas on the other hand will be applied on the data only if
#'' the relevant condition is met
#'' /item Formulas support calling directly a column of the data by its name
#'' without /code{x$foo} notation.
#'' /item Dot notation will work in expressions only if `pif` is used in a pipe
#'' chain
#'' }
#''
#'' @param x An object
#'' @param p A predicate function, a formula describing such a predicate function, or an expression.
#'' @param true,false Functions to apply to the data, formulas describing such functions, or expressions.
#''
#'' @return The output of /code{true} or /code{false}, either as expressions or applied on data as functions
#'' @export
#''
#'' @examples
#''# using functions
#''pif(iris, is.data.frame, dim, nrow)
#''# using formulas
#''pif(iris, ~is.numeric(Species), ~"numeric :)",~paste(class(Species)[1],":("))
#''# using expressions
#''pif(iris, nrow(iris) > 2, head(iris,2))
#''# careful with expressions
#''pif(iris, TRUE, dim, warning("this will be evaluated"))
#''pif(iris, TRUE, dim, ~warning("this won''t be evaluated"))
pif <- function(x, p, true, false = identity){
if(!requireNamespace("purrr"))
stop("Package ''purrr'' needs to be installed to use function ''pif''")
if(inherits(p, "formula"))
p <- purrr::as_mapper(
if(!is.list(x)) p else update(p,~with(...,.)))
if(inherits(true, "formula"))
true <- purrr::as_mapper(
if(!is.list(x)) true else update(true,~with(...,.)))
if(inherits(false, "formula"))
false <- purrr::as_mapper(
if(!is.list(x)) false else update(false,~with(...,.)))
if ( (is.function(p) && p(x)) || (!is.function(p) && p)){
if(is.function(true)) true(x) else true
} else {
if(is.function(false)) false(x) else false
}
}
Me parece más fácil retroceder un poco de las tuberías (aunque me interesaría ver otras soluciones), por ejemplo:
library("dplyr")
z <- data.frame(a=1:2)
z %>% mutate(b=a^2) -> z2
if (z2$b[1]>1) {
z2 %>% mutate(b=b^2) -> z2
}
z2 %>% mutate(b=b^2) -> z3
Esta es una ligera modificación de la respuesta de @ JohnPaul (es posible que no desee
ifelse
, que evalúa sus dos argumentos y está vectorizado).
Sería bueno modificar esto para volver
.
automáticamente si la condición es falsa ... (
precaución
: creo que esto funciona, pero realmente no lo he probado / pensado demasiado ...)
iff <- function(cond,x,y) {
if(cond) return(x) else return(y)
}
z %>% mutate(b=a^2) %>%
iff(cond=z2$b[1]>1,mutate(.,b=b^2),.) %>%
mutate(b=b^2) -> z4