Pipe in Magrittr Package Is Not Working for Function Load()

Pipe in magrittr package is not working for function load()

The envir argument in load() needs to be specified as either globalenv() or parent.frame(3).

# in a fresh R session ...
a <- 1
b <- 1
save(list = ls(), file = file.path(tempdir(), 'tmp.RData'))

# in another fresh session ...
ls()
# character(0)
tempdir() %>% file.path("tmp.RData") %>% load(envir = globalenv())
ls()
# [1] "a" "b"

The following also works:

tempdir() %>% file.path("tmp.RData") %>% load(envir = parent.frame(3))

I'll try to explain why. When you call load() from any environment, the function loads the new objects in the parent environment.

Now, the global environment globalenv() is your R workspace. So, if you call load from the global environment (i.e. the workspace) everything works as you expect. Visualise this:

  • Global environment

    • load()

However, if you call load() from inside a function, then you've inserted an environment in between load and the global environment. Visualise this:

  • Global environment

    • function

      • load()

This is exactly what happens when you put %>% into the mix:

  • Global environment

    • %>%
      • load()

There are two solutions for resolving this. Either explicitly point to globalenv() or walk 3 steps up the chain to the global environment using parent.frame(3).


Note: There was an issue reported on GitHub for this. Not sure what the resolution was, or if there is one yet. The issue was just reported in September.

Many thanks to @Andrie for improving this explanation.

Pipe in magrittr package is not working for function rm()

Use the %<>% operator for assigning the value to NULL

x %<>% 
rm()

In the pipe, we are getting the value instead of the object. So, by using the %<>% i.e. in place compound assignment operator, the value of 'x' is assigned to NULL

x
#NULL

If we need the object to be removed, pass it as character string, feed it to the list argument of rm which takes a character object and then specify the environment

x <- 10
"x" %>%
rm(list = ., envir = .GlobalEnv)

When we call 'x'

x

Error: object 'x' not found

The reason why the ... doesn't work is that the object . is not evaluated within the rm

x <- 10
"x" %>%
rm(envir = .GlobalEnv)

Warning message: In rm(., envir = .GlobalEnv) : object '.' not found


Another option is using do.call

x <- 10
"x" %>%
list(., envir = .GlobalEnv) %>%
do.call(rm, .)
x

Error: object 'x' not found

Why isn't the magrittr %% assignment pipe working with R's native pipe (|)

The reason that the assignment pipe, %<>% is no longer is due operator precedence. The %<>% occurs before the |>, see below eacmple:

library(magrittr)
library(tidyverse)

a <- tibble(a = 1:3)
a %<>%
mutate(b = a * 2) |>
mutate(c = a * 3) |>
filter(a <= 2)
a

Returns

# A tibble: 3 × 2
a b
<int> <dbl>
1 1 2
2 2 4
3 3 6

Thus the

a %<>% 
mutate(b = a * 2)

Is the only section that was saved. You can also get a feeling that this may be the case as you get the intended table printed instead which should never be the case with a tibble assignment.

Error: could not find function %%

You need to load a package (like magrittr or dplyr) that defines the function first, then it should work.

install.packages("magrittr") # package installations are only needed the first time you use it
install.packages("dplyr") # alternative installation of the %>%
library(magrittr) # needs to be run every time you start R and want to use %>%
library(dplyr) # alternatively, this also loads %>%

The pipe operator %>% was introduced to "decrease development time and to improve readability and maintainability of code."

But everybody has to decide for himself if it really fits his workflow and makes things easier.
For more information on magrittr, click here.

Not using the pipe %>%, this code would return the same as your code:

words <- colnames(as.matrix(dtm))
words <- words[nchar(words) < 20]
words

EDIT:
(I am extending my answer due to a very useful comment that was made by @Molx)

Despite being from magrittr, the pipe operator is more commonly used
with the package dplyr (which requires and loads magrittr), so
whenever you see someone using %>% make sure you shouldn't load dplyr
instead.

R: use magrittr pipe operator in self written package

It should have worked correctly if you had magrittr listed in Depends. However, this is not advised. Instead, you leave magrittr in Imports and add the following line to NAMESPACE:

importFrom(magrittr,"%>%")

I suggest reading Writing R extensions. Your question is covered in paragraphs 1.1.3 and 1.5.1.

Error in magrittr pipe when using ``magrittr::`%%` ``

The pipe arguments (%>%, %$%, etc...) are all actually the same pipe() function in magrittr. One of the first things that function does is to split the call into its constituent parts using an internal, non-exported function split_chain.

split_chain() takes the first element of the call (the function used, in this case, one of the pipe operators) and runs it through another internal, non-exported function called is_pipe() which looks like:

function(pipe)
{
identical(pipe, quote(`%>%`)) ||
identical(pipe, quote(`%T>%`)) ||
identical(pipe, quote(`%<>%`)) ||
identical(pipe, quote(`%$%`))
}

if this doesn't come back as true, the function exits returning a list that is missing the pipe type and the right-handed side of the argument which causes problems. When scoping, a la magrittr::'%>%' the first part of the call includes the explicit scoping and so it fails these hard coded checks.

R: transition from magrittr to native pipe and translation of a function

complete_data_native_wrong():

complete_data_native_wrong <- function(data){

res <- data |> (\(x) filter(x, complete.cases(x)))()

return(res)

}

Data masking is the reason that this lovely function doesn't work as expected.

"So, what actually happens?", you ask.

dplyr::filter() checks for a column named x, it indeed finds it, then passes the contents of that column to complete.cases(). The same happens when you use y instead of x.

complete.cases() ends up acting on a "vector" instead of a data.frame, hence the results.

"But... How do I ensure dplyr::filter() doesn't act that way?", you enquire.

That's where the bang-bang operator !! comes in. And we can now have complete_data_native_right():

complete_data_native_right <- function(data){

res <- data |> (\(x) filter(x, complete.cases(!!x)))()
# res <- data |> (\(y) filter(y, complete.cases(!!y)))()

return(res)

}


move_row_native_attempt():

For this one you can use the shorthand function notation without any hiccups:

move_row_native_attempt <-  function(df, ini_pos, fin_pos){
row_pick <- slice(df, ini_pos)

if (fin_pos=="last"){
res <- df |>
slice(-ini_pos) |>
(\(x) add_row(x, row_pick, .before = nrow(x)))()

} else{
res <- df |>
slice(-ini_pos) |>
add_row(row_pick, .before = fin_pos)
}

return(res)
}

Why is a command not working in a pipe operator in R, but outside the pipe it works perfectly fine?

Problem: not all functions are pipe-friendly

{magrittr} pipes work best with functions written to be "pipe-friendly." These generally take a dataframe as a first argument, and may use data masking to let you refer to columns within that dataframe without prefixing them. e.g., many {dplyr} verbs are pipe-friendly.

which.min isn't pipe-friendly. Your code,

ir_OEX_data %>% group_by(quotedate) %>% which.min(abs(moneyness_call  - 1))

is actually equivalent to

which.min(
group_by(ir_OEX_data, quotedate),
abs(moneyness_call - 1)
)

but which.min expects only one argument, so throws an error.

Solution 1: the exposition pipe (%$%)

There are a few ways to deal with this. One is the {magrittr} exposition pipe, %$%, which makes your column names available to the next function without passing the data:

library(magrittr)
library(dplyr)

ir_OEX_data %>%
group_by(quotedate) %$%
which.min(abs(moneyness_call - 1))

Solution 2: use inside a pipe-friendly function

If you wanted to add the result of which.min to your dataset, you'd just need to use it inside summarize or mutate:

ir_OEX_data %>% 
group_by(quotedate) %>%
summarize(call_which_min = which.min(abs(moneyness_call - 1)))

Solution 3: write a pipe-friendly wrapper

You can also put a non-friendly function in a pipe-friendly wrapper. This would probably be overkill here, but can be useful in more complex cases.

which_min_pipe <- function(.data, x) {
.data %>% summarize(out = which.min({{ x }})) %>% pull(out)
}

ir_OEX_data %>%
group_by(quotedate) %>%
which_min_pipe(abs(moneyness_call - 1))


Related Topics



Leave a reply



Submit