2
votes

I want to divide each column in dataframe which satisfy a condition by one of the columns within the same dataframe

I believe it can be done in several ways, but I feel that it would be more efficient for me if I could do it using purrr package which is designed for this type of work.

for example, this works

t <- iris %>% 
  modify_if(is.numeric, ~./2)

but this does not

t <- iris %>% 
  modify_if(is.numeric, ~./Sepal.Length)

this results in error

Error in .f(.x[[i]], ...) : object 'Sepal.Length' not found

I think it wants me to specify .y as "Sepal.Length" but I could not figure out the right way.

I am confused between modify2, imodify, map2 and pmap

I would appreciate if you can point me to a proper guide on how to use purrr properly. The official guide seems to be okay for the concept and intuition
but I am struggling to put it in application. https://purrr.tidyverse.org/reference/modify.html

1
For me (dplyr-0.8.1), this works without error: iris %>% mutate_if(is.numeric, ~ ./Sepal.Length).r2evans
@r2evans that does work without error but if you examine the results, only the Sepal.Length column is correct. This is because it's the first column, so it divides by itself to give 1 and then all other columns are divided by 1 i.e. unchanged.neilfws
Oops, sorry, I read modify_if and thought mutate_if, ...r2evans
You could use mutate_if if the columns were reordered to place Sepal.Length last.neilfws

1 Answers

2
votes

To use modify_if in this case, we can do

library(dplyr)
library(purrr)

iris %>% modify_if(is.numeric, ~./iris$Sepal.Length)

#    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
#1              1   0.6862745    0.2745098  0.03921569     setosa
#2              1   0.6122449    0.2857143  0.04081633     setosa
#3              1   0.6808511    0.2765957  0.04255319     setosa
#4              1   0.6739130    0.3260870  0.04347826     setosa
#5              1   0.7200000    0.2800000  0.04000000     setosa
#6              1   0.7222222    0.3148148  0.07407407     setosa
#....

Or another version

iris %>%  modify_if(is.numeric, function(x) x/.$Sepal.Length)

Or as suggested by @Artem Sokolov

iris %>% modify_if(is.numeric, `/`, .$Sepal.Length)

Another way to do it would be using mutate_if , however for that you need to rearrange the columns as mentioned by @neilfws

iris %>%
  select(Sepal.Width, Petal.Length, Petal.Width, Species, Sepal.Length) %>%
  mutate_if(is.numeric, ~./Sepal.Length)

and finally using base R

iris[] <- lapply(iris, function(x) if(is.numeric(x)) x/iris$Sepal.Length else x)
#OR
cols <- sapply(iris, is.numeric)
iris[cols] <- lapply(iris[cols], function(x) x/iris$Sepal.Length)