Adressing columns of a variable dataframe in for loop

Viewed 43

I'm having trouble combining writing in a certain column of a certain dataset in a for loop. Basically I have identically structered datasets w2,w4,w5,w6,w7,w8 and I want to perform the same operation on the same column of each dataset. For example, I want to recode the gender variable from 1 = male, 2 = female to 0 = male,1 = female and then factor it. Here is how I do it for w2.

w2$gender <- case_when(w2$gender == 1 ~ 0, w2$gender == 2 ~ 1)
w2$gender <- factor(w2$gender,
                    levels = c(0,1),
                    labels = c("Male","Female"))

I want to build a for loop that does it for all my dataframes, but I'm constantly running into issues as I can't seem to adress the column of the changing dataset.

for(i in w){
  z <- paste0("w",i)
  eval(call( "<-",eval(as.name(z))[,4], case_when(eval(as.name(z))[,4] == 1 ~ 0, eval(as.name(z))[,4] == 2 ~ 1)))
}

I get the error:

Error in c(2, 2, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2, 2, 1, : invalid (do_set) left-hand side to assignment

I tried sevral different approaches like using assign function etc. but nothing seems to work. Do you know of an easy way how this could be done?

2 Answers

If we want to use a loop, get the values of the objects (w1, w2, ..) in a list with mget, loop over the list with map and mutate the gender to change the values from 1, 2 to 'Male', 'Female' respectively (there is no need to first convert 1, 2 to 0, 1 and change it back as in R, indexing starts from 1)

library(dplyr)
library(purrr)
lst1 <- mget(ls(pattern = '^w\\d+$')) %>%
      map(~ .x %>%
                mutate(gender =  factor(c("Male", "Female")[gender], 
            levels = c("Male", "Female"))))

If we want to update the original objects, use list2env

list2env(lst1, .GlobalEnv)

We could save your dataframes in a list list_df then apply the function my_function to all elements (dataframes) of the list: @akrun: What do you think is this reasonable?

library(dplyr)
my_function <- function(x) {          # x is the dataframe you want to change
  x$gender <- case_when(x$gender == 1 ~ 0, 
                        x$gender == 2 ~ 1)
  x$gender <- factor(x$gender,
                     levels = c(0,1),
                     labels = c("Male", "Female"))
  return(x)
}

df1 <- structure(list(gender = c(1, 1, 2, 2)), row.names = c(NA, -4L
), class = c("tbl_df", "tbl", "data.frame"))

df2 <- structure(list(gender = c(2, 1, 1, 2)), row.names = c(NA, -4L
), class = c("tbl_df", "tbl", "data.frame"))

list_df <- as.list(c(df1, df2))

library(purrr)
list_df %>% lmap(my_function) 

Output:

$gender
[1] Male   Male   Female Female
Levels: Male Female

$gender
[1] Female Male   Male   Female
Levels: Male Female

Related