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
Coloring Boxplot Outlier Points in Ggplot2
Ternary Plot and Filled Contour
Generate Ggplot2 Boxplot with Different Colours for Multiple Groups
Element-Wise Concatenation of String Vectors
Export Fitted Regression Splines (Constructed by 'Bs' or 'Ns') as Piecewise Polynomials
Extract Part of String Before the First Semicolon
How to Calculate Any Negative Number to the Power of Some Fraction in R
In R Combine a List of Lists into One List
Using Variable Column Names in Dplyr Summarise
Best Practice: Should I Try to Change to Utf-8 as Locale or Is It Safe to Leave It as Is
Source Script to Separate Environment in R, Not the Global Environment
Find Consecutive Values in Vector in R
Number Format, Writing 1E-5 Instead of 0.00001
Remove Text After Final Period in String
Combinations of Multiple Vectors in R
Extend an Irregular Sequence and Add Zeros to Missing Values