I am trying to recode variables, which lie in columns of multiple dataframes. These dataframes also lie within lists. I have an attempt using a loop which works. I wonder if there is an even easier possibility using lapply.
My lapply attempt did not work, because the recode command tried accessing the dataframe, but it needs the columns. Is there an easy lapply way to do it?
This is an example of how my data (dataframes within list) looks like using the structure command str():
> str(Q1_dfcl)
List of 19
$ Q1_Ques_01Dep:'data.frame': 384 obs. of 20 variables:
..$ Q001_01: num [1:384] 3 2 2 1 4 2 1 1 3 1 ...
..$ Q001_02: num [1:384] 2 1 3 1 5 1 1 1 1 1 ...
..$ Q001_05: num [1:384] 3 1 3 1 2 2 2 1 1 1 ...
..$ Q001_06: num [1:384] 2 2 5 2 5 1 2 1 1 1 ...
..$ Q001_08: num [1:384] 3 1 5 1 4 1 1 1 1 1 ...
..$ Q001_09: num [1:384] 3 1 2 2 3 3 1 2 3 1 ...
..$ Q001_11: num [1:384] 4 2 4 1 1 3 1 1 2 2 ...
..$ Q001_13: num [1:384] 1 1 3 1 2 1 1 1 1 1 ...
..$ Q001_21: num [1:384] 3 1 5 3 5 1 2 1 2 1 ...
..$ Q001_26: num [1:384] 3 2 5 1 4 2 1 1 1 1 ...
..$ Q001_27: num [1:384] 4 1 4 1 5 2 2 2 1 2 ...
..$ Q001_30: num [1:384] 2 3 5 2 4 1 1 2 1 1 ...
..$ Q001_31: num [1:384] 3 1 5 2 5 1 1 1 1 2 ...
..$ Q001_40: num [1:384] 2 1 5 2 5 1 2 1 1 1 ...
..$ Q001_48: num [1:384] 4 1 5 1 4 2 2 1 2 1 ...
..$ Q001_51: num [1:384] 3 1 5 3 4 1 1 1 2 1 ...
..$ Q001_52: num [1:384] 1 1 2 1 2 1 1 1 1 1 ...
..$ Q001_57: num [1:384] 1 1 1 1 1 1 1 1 1 1 ...
..$ Q001_61: num [1:384] 2 2 2 1 2 2 1 1 1 1 ...
..$ Q001_64: num [1:384] 4 4 5 3 5 3 2 2 5 3 ...
$ Q1_Ques_02Dys:'data.frame': 384 obs. of 10 variables:
..$ Q001_02: num [1:384] 2 1 3 1 5 1 1 1 1 1 ...
..$ Q001_05: num [1:384] 3 1 3 1 2 2 2 1 1 1 ...
..$ Q001_08: num [1:384] 3 1 5 1 4 1 1 1 1 1 ...
..$ Q001_09: num [1:384] 3 1 2 2 3 3 1 2 3 1 ...
..$ Q001_21: num [1:384] 3 1 5 3 5 1 2 1 2 1 ...
..$ Q001_31: num [1:384] 3 1 5 2 5 1 1 1 1 2 ...
..$ Q001_40: num [1:384] 2 1 5 2 5 1 2 1 1 1 ...
..$ Q001_48: num [1:384] 4 1 5 1 4 2 2 1 2 1 ...
..$ Q001_57: num [1:384] 1 1 1 1 1 1 1 1 1 1 ...
..$ Q001_61: num [1:384] 2 2 2 1 2 2 1 1 1 1 ...
$ Q1_Ques_03Las:'data.frame': 384 obs. of 6 variables:
..$ Q001_06: num [1:384] 2 2 5 2 5 1 2 1 1 1 ...
..$ Q001_29: num [1:384] 3 1 2 2 5 1 1 1 1 1 ...
..$ Q001_30: num [1:384] 2 3 5 2 4 1 1 2 1 1 ...
..$ Q001_43: num [1:384] 3 2 2 2 1 2 1 1 1 1 ...
..$ Q001_54: num [1:384] 1 2 3 1 4 1 1 1 1 2 ...
..$ Q001_55: num [1:384] 2 2 5 1 5 1 1 1 3 1 ...
This is what my code looks like:
### Preparing the values for for recoding
level_key_difficulty <- c("1" = "0", "2" = "1", "3" = "2", "4" = "3", "5" = "4")
### This works.
for (i in 1:length(Q1_list)){
for (j in 1:length(Q1_list[[i]])){
Q1_dfcl[[i]][j] <- lapply(Q1_dfcl[[i]][j], function(x) recode(x, !!!level_key_difficulty))[[1]]
names(Q1_dfcl[[i]][j]) <- names(Q1_list[[i]][j])
}
}
### This does not work, because it only tries to do it on the WHOLE dataframe within
# Q1_dfcl[[1]]
lapply(Q1_dfcl, \(x) recode(x, !!!level_key_difficulty))
CodePudding user response:
Actually you could just subtract 1
.
dlst ## before
# [[1]]
# V1 V2 V3 V4
# 1 1 1 2 4
# 2 5 2 2 1
# 3 1 4 1 5
#
# [[2]]
# V1 V2 V3 V4
# 1 4 3 3 5
# 2 2 1 4 5
# 3 2 1 5 4
dlst_new <- lapply(dlst, `-`, 1)
dlst_new ## after
# [[1]]
# V1 V2 V3 V4
# 1 0 0 1 3
# 2 4 1 1 0
# 3 0 3 0 4
#
# [[2]]
# V1 V2 V3 V4
# 1 3 2 2 4
# 2 1 0 3 4
# 3 1 0 4 3
To change just the values of specific columns, we can do
sset <- c('V1', 'V2') ## define subset
dlst_new1 <- lapply(dlst, \(x) {x[sset] <- x[sset] - 1; x})
dlst_new1
# [[1]]
# V1 V2 V3 V4
# 1 0 0 2 4
# 2 4 1 2 1
# 3 0 3 1 5
#
# [[2]]
# V1 V2 V3 V4
# 1 3 2 3 5
# 2 1 0 4 5
# 3 1 0 5 4
Data:
dlst <- list(structure(list(V1 = c(1L, 5L, 1L), V2 = c(1L, 2L, 4L), V3 = c(2L,
2L, 1L), V4 = c(4L, 1L, 5L)), class = "data.frame", row.names = c(NA,
-3L)), structure(list(V1 = c(4L, 2L, 2L), V2 = c(3L, 1L, 1L),
V3 = 3:5, V4 = c(5L, 5L, 4L)), class = "data.frame", row.names = c(NA,
-3L)))
CodePudding user response:
It can be done when you nest your lapply such as:
lapply(list(mtcars), \(a) lapply(a, \(b) recode(b, !!!level_key_difficulty, .default = "def")))
Purrr has a convenient function called map_depth
that allows you to map over a list of many depths, in this case you want to go to depth two since the first depth is just a data.frame. The second depth is the columns of interest
purrr::map_depth(list(mtcars), 2, ~ recode(.x, !!!level_key_difficulty, .default = "def"))
There is also rapply
a function I rarely use but is similar to map_depth
rapply(list(mtcars), \(a) recode(a, !!!level_key_difficulty, .default = "def"), how = "list")