How to Change the Now Deprecated Dplyr::Funs() Which Includes an Ifelse Argument

How to change the now deprecated dplyr::funs() which includes an ifelse argument?

As of dplyr 0.8.0, the documentation states that we should use list instead of funs, giving the example:

Before:

funs(name = f(.))

After:

list(name = ~f(.))

So here, the call funs(ifelse(is.character(.), trimws(.),.)) can become instead list(~ifelse(is.character(.), trimws(.),.)). This is using the formula notation for anonymous functions in the tidyverse, where a one-sided formula (expression beginning with ~) is interpreted as function(x), and wherever x would go in the function is represented by .. You can still use full functions inside list.

Note the difference between the .funs argument of mutate_if and the funs() function which wrapped other functions to pass to .funs; i.e. .funs = gsub still works. You only needed funs() if you needed to apply multiple functions to selected columns or to name them something by passing them as named arguments. You can do all the same things with list().

You also are duplicating work by adding ifelse inside mutate_if; that line could be simplified to mutate_if(is.character, trimws) since if the column is character already you don't need to check it again with ifelse. Since you apply only one function, no need for funs or list at all.

Unique Challenge Replacing Soft-deprecated funs()

Remove the unnecessary training_imptd$ from the inside of your function. The pronoun . already refers to "the current column", so you can pass it to boxplot.stats() directly:

training_imptd %>%
mutate_all(
~ifelse(. %in% boxplot.stats(.)$out, NA, .)
)

Replacing dplyr::funs() within imap that refers to the .y value

I think it gets into using too many pronouns in the same function, R gets confused. I ran the function outside of the map and it worked as expected so there's nothing wrong with your call.

If you make the function explicit and replace .x and .y with a and b it works. R can be rather pedantic sometimes

    list("first_set" = df,
"second_set" = df)%>%
imap(.,
function(a,b){rename_at(a,
.vars = vars(c("Feature ID", "Resource", 'Name')),
.funs = ~{str_c(b, ., sep = "_")})})

In a named argument to dplyr::funs, can I reference the names of other arguments?

This seems a little convoluted, but it works:

scaled <- function(col_name, x, y) {
col_name <- deparse(substitute(col_name))
avg <- eval.parent(as.symbol(paste0(col_name, x)))
dev <- eval.parent(as.symbol(paste0(col_name, y)))
(eval.parent(as.symbol(col_name)) - avg) / dev
}

df %>%
mutate_all(funs(avg = mean(.), dev = sd(.), scaled = scaled(., "_avg", "_dev")))

Composite functions in mutate_at

We can use map2 , use coalesce and then toString.

library(dplyr)
library(purrr)

df1 <- df %>%
mutate_at(vars(two, three), ~map2(., one, ~toString(coalesce(.x, .y))))

df1

# one two three
# <chr> <list> <list>
#1 a <chr [1]> <chr [1]>
#2 b <chr [1]> <chr [1]>

df1$two
#[[1]]
#[1] "p1, p2, p3"

#[[2]]
#[1] "b"

df1$three
#[[1]]
#[1] "a"

#[[2]]
#[1] "z1, z2, z3"

In the above example, we use lambda style expression using ~ to use it as a function and it is true funs has been deprecated and has been replaced with list() instead. The answer and comments in this question provides more insight into this.

dplyr change many data types

You can use the standard evaluation version of mutate_each (which is mutate_each_) to change the column classes:

dat %>% mutate_each_(funs(factor), l1) %>% mutate_each_(funs(as.numeric), l2)

Using functions of multiple columns in a dplyr mutate_at call

This was answered by @eipi10 in @eipi10's comment on the question, but I'm writing it here for posterity.

The solution here is to use:

df %>%
mutate_at(.vars = vars(y, z),
.funs = list(~ ifelse(x, ., NA)))

You can also use the new across() function with mutate(), like so:

df %>%
mutate(across(c(y, z), ~ ifelse(x, ., NA)))

The use of the formula operator (as in ~ ifelse(...)) here indicates that ifelse(x, ., NA) is an anonymous function that is being defined within the call to mutate_at().

This works similarly to defining the function outside of the call to mutate_at(), like so:

temp_fn <- function(input) ifelse(test = df[["x"]],
yes = input,
no = NA)

df %>%
mutate_at(.vars = vars(y, z),
.funs = temp_fn)

Note on syntax changes in dplyr: Prior to dplyr version 0.8.0, you would simply write .funs = funs(ifelse(x, . , NA)), but the funs() function is being deprecated and will soon be removed from dplyr.

How to write a readable code for arithmetic operations on data frame using pipe/dplyr?

I think the issue is in the way you call subtract(). anyway, the latest version of dplyr 0.8.0 has a new way to handle these calls, with list() instead of funs(). With the new version, you obtain what you are trying to get with:

set.seed(12)
df <- data.frame(replicate(10,sample(1:100,10,rep=TRUE)))
df[1] <- 1:10
colnames(df) <- c("ID", "A", "B", "C", "D", "E", "F", "G", "H", "I")

library(magrittr)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
packageVersion("dplyr")
#> [1] '0.8.3'

## first attempt
df %>%
mutate_at(vars(-ID), list(~subtract(., 5)))
#> ID A B C D E F G H I
#> 1 1 86 52 21 86 78 68 9 70 11
#> 2 2 77 67 27 32 13 47 75 17 54
#> 3 3 77 38 79 82 80 51 69 62 33
#> 4 4 33 41 15 43 50 48 61 7 13
#> 5 5 85 25 59 66 53 34 7 0 61
#> 6 6 43 32 14 22 83 23 37 71 78
#> 7 7 8 29 79 72 8 35 35 62 5
#> 8 8 51 34 -1 79 27 54 32 0 45
#> 9 9 63 73 8 86 35 10 75 81 74
#> 10 10 19 66 72 0 83 2 0 37 1

Extract column name in mutate_if call

You have to use quo instead of enquo

#enquo(.) :
<quosure: empty>
~function (expr)
{
enexpr(expr)
}
...

#quo(.) :
<quosure: frame>
~x
<quosure: frame>
~y
<quosure: frame>
~z

With your example :

mutate_if(df, is.numeric, funs({
lookup_value <- df_lookup %>% pull(quo_name(quo(.)))
ifelse(is.na(.), lookup_value, .)
}))

# A tibble: 10 x 4
x y z a
<int> <int> <int> <chr>
1 1 1 8 a
2 2 2 2 b
3 3 3 3 c
4 4 5 8 d
5 5 1 8 e
6 6 2 2 a
7 7 3 3 b
8 8 5 8 c
9 9 1 8 d
10 10 2 2 e


Related Topics



Leave a reply



Submit