R: Further subset a selection using the pipe %% and placeholder
Since you're going from a bunch of data into one (row of) value(s), you're summarizing. in a dplyr pipeline you can then use the summarize function, within the summarize function you don't need to subset and can just call pre
and post
Like so:
dat %>% select(pre, post) %>% summarize(CD = cohensD(pre, post))
(The select statement isn't actually necessary in this case, but I left it in to show how this works in a pipeline)
How to subset a dataframe in r using placeholder
Use starts_with()
.
library(dplyr)
dat %>% select(-starts_with("x"))
There are other functions like this (ends_with
, matches
, contains
, one_of
). And if everything else fails, you can always use regular expressions and base R:
dat <- dat[ , !grepl("^x", colnames(dat)) ]
Explanation: grepl
returns a logical vector. The regular expression "^x"
matches anything that starts with an x
. This is matched against the column names of dat. We negate the logical vector with the bang (!
) and thus select everything that does not match our regex.
R Conditional evaluation when using the pipe operator %%
Here is a quick example that takes advantage of the .
and ifelse
:
X<-1
Y<-T
X %>% add(1) %>% { ifelse(Y ,add(.,1), . ) }
In the ifelse
, if Y
is TRUE
if will add 1, otherwise it will just return the last value of X
. The .
is a stand-in which tells the function where the output from the previous step of the chain goes, so I can use it on both branches.
Edit
As @BenBolker pointed out, you might not want ifelse
, so here is an if
version.
X %>%
add(1) %>%
{if(Y) add(.,1) else .}
Thanks to @Frank for pointing out that I should use {
braces around my if
and ifelse
statements to continue the chain.
How to pipe purely in base R ('base pipe')?
In R |>
is used as a pipe operator. (Since 4.1.0)
The left-hand side expression lhs is inserted as the first free argument in the call of to the right-hand side expression rhs.
mtcars |> head() # same as head(mtcars)
# mpg cyl disp hp drat wt qsec vs am gear carb
#Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
#Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
#Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
mtcars |> head(2) # same as head(mtcars, 2)
# mpg cyl disp hp drat wt qsec vs am gear carb
#Mazda RX4 21 6 160 110 3.9 2.620 16.46 0 1 4 4
#Mazda RX4 Wag 21 6 160 110 3.9 2.875 17.02 0 1 4 4
It is also possible to use a named argument with the placeholder _
in the rhs call to specify where the lhs is to be inserted. The placeholder can only appear once on the rhs. (Since 4.2.0)
mtcars |> lm(mpg ~ disp, data = _)
#mtcars |> lm(mpg ~ disp, _) #Error: pipe placeholder can only be used as a named argument
#Call:
#lm(formula = mpg ~ disp, data = mtcars)
#
#Coefficients:
#(Intercept) disp
# 29.59985 -0.04122
Alternatively explicitly name the argument(s) before the "one":
mtcars |> lm(formula = mpg ~ disp)
In case the placeholder is used more than once or used as a named or also unnamed argument on any position or for disabled functions: Use an (anonymous) function.
mtcars |> (\(.) .[.$cyl == 6,])()
#mtcars ->.; .[.$cyl == 6,] # Alternative using bizarro pipe
#local(mtcars ->.; .[.$cyl == 6,]) # Without overwriting and keeping .
# mpg cyl disp hp drat wt qsec vs am gear carb
#Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
#Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
#Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
#Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
#Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
#Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
#Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
mtcars |> (\(.) lm(mpg ~ disp, .))()
#Call:
#lm(formula = mpg ~ disp, data = .)
#
#Coefficients:
#(Intercept) disp
# 29.59985 -0.04122
1:3 |> setNames(object = _, nm = _)
#Error in setNames(object = "_", nm = "_") :
# pipe placeholder may only appear once
1:3 |> (\(.) setNames(., .))()
#1 2 3
#1 2 3
1:3 |> list() |> setNames(".") |> with(setNames(., .))
#1 2 3
#1 2 3
#The same but over a function
._ <- \(data, expr, ...) {
eval(substitute(expr), list(. = data), enclos = parent.frame())
}
1:3 |> ._(setNames(., .))
#1 2 3
#1 2 3
Some function are disabled.
mtcars |> `$`(cyl)
#Error: function '$' not supported in RHS call of a pipe
But some still can be called by placing them in brakes, call them via the function ::
, call it in a function or define a link to the function.
mtcars |> (`$`)(cyl)
# [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
mtcars |> base::`$`(cyl)
# [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
mtcars |> (\(.) .$cyl)()
# [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
fun <- `$`
mtcars |> fun(cyl)
# [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
An expression written as x |> f(y)
is parsed as f(x, y)
. While the code in a pipeline is written sequentially, regular R semantics for evaluation apply. So piped expressions will be evaluated only when first used in the rhs expression.
-1 |> sqrt() |> (\(x) 0)()
#[1] 0
. <- -1
. <- sqrt(.)
#Warning message:
#In sqrt(.) : NaNs produced
(\(x) 0)(.)
#[1] 0
x <- data.frame(a=0)
f1 <- \(x) {message("IN 1"); x$b <- 1; message("OUT 1"); x}
f2 <- \(x) {message("IN 2"); x$c <- 2; message("OUT 2"); x}
x|> f1() |> f2()
#IN 2
#IN 1
#OUT 1
#OUT 2
# a b c
#1 0 1 2
f2(f1(x))
#IN 2
#IN 1
#OUT 1
#OUT 2
# a b c
#1 0 1 2
. <- x
. <- f1(.)
#IN 1
#OUT 1
f2(.)
#IN 2
#OUT 2
# a b c
#1 0 1 2
%% .$column_name equivalent for R base pipe |
We can use getElement()
.
iris |> getElement('Sepal.Length') |> cut(5)
dplyr: group_by & mutate variable can't call mean/sd functions
You want the baseR function with()
.
mtcars %>%
group_by(cyl) %>%
mutate(group_pct = hp / sum(hp)) %>%
with(paste0('Words: ', mean(group_pct)))
[1] "Words: 0.09375"
The issue with your original attempt is that group_pct
is not defined in the global environment, so you get the error message, when it can't locate it in the lookup.
with
is the syntactic sugar that tells R to evaluate the paste0()
function call within the environment of the data frame being passed by pipe. So it finds group_pct
and returns your expected result.
Using table() in dplyr chain
This behavior is by design: https://github.com/tidyverse/magrittr/blob/00a1fe3305a4914d7c9714fba78fd5f03f70f51e/README.md#re-using-the-placeholder-for-attributes
Since you don't have a .
on it's own, the tibble is still being passed as the first parameter so it's really more like
... %>% table(., .$type, .$colour)
The official magrittr work-around is to use curly braces
... %>% {table(.$type, .$colour)}
R combinations with dot (.), ~, and pipe (%%) operator
That line uses the .
in three different ways.
[1] [2] [3]
aggregate(. ~ cyl, data = ., FUN = . %>% mean %>% round(2))
Generally speaking you pass in the value from the pipe into your function at a specific location with .
but there are some exceptions. One exception is when the .
is in a formula. The ~
is used to create formulas in R. The pipe wont change the meaning of the formula, so it behaves like it would without any escaping. For example
aggregate(. ~ cyl, data=mydata)
And that's just because aggregate
requires a formula with both a left and right hand side. So the .
at [1]
just means "all the other columns in the dataset." This use is not at all related to magrittr.
The .
at [2]
is the value that's being passed in as the pipe. If you have a plain .
as a parameter to the function, that's there the value will be placed. So the result of the subset()
will go to the data=
parameter.
The magrittr
library also allows you to define anonymous functions with the .
variable. If you have a chain that starts with a .
, it's treated like a function. so
. %>% mean %>% round(2)
is the same as
function(x) round(mean(x), 2)
so you're just creating a custom function with the .
at [3]
How to pipe an output tibble into further calculations without saving the tibble as a separate object in R?
In your caase, you can further manipulate the tibble you have generated using dplyr
functions.
Note the existence of mutate_at
and summarize_at
, that lets you transform a set of columns with the option to select them by column position.
This, using .
as a placeholder for the tibble you are currently manipulating, and calling an anonymous function inside mutate_at
, will give you the result you expect.
sr_df %>%
group_by(ResolutionViolated) %>%
tally() %>%
arrange(desc(n)) %>%
mutate(total = sum(n)) %>%
mutate_at(.cols = c(1, 2),
.funs = function(column) round(column / .$total * 100, digits = 2))
Related Topics
Predicting Probabilities for Gbm with Caret Library
Extract Columns from Data Table by Numeric Indices Stored in a Vector
How to Write Special Characters in Rmarkdown Latex Documents
What's the Easiest Way to Deploy an API Incorporating R Functions
Tukeys Post-Hoc on Ggplot Boxplot
Resetting Cumsum If Value Goes to Negative in R
Error While Using Install_Github | Devtools | Timeout Issue
Only Source Functions in a .R File
How to Pass Vector to Integrate Function
Ggplot2: Change Factor Order in Legend
Shiny Promises Future Is Not Working on Eventreactive
How to Load a Matlab Struct into a R Data Frame
How to Add a Title to Legend Scale Using Levelplot in R
Extract Date Elements from Posixlt and Put into Data Frame in R