使用 apply 函数遍历两个列表
Iterate through two lists with apply functions
我有一个数据框列表,其中数据框的每一列在第一行都有一个名称,在列的某些位置有 x-s。如果有 x,则第一行中的名称被视为已选中。
在现实世界的问题中,我读取了一个包含许多 sheet 的 xlsx 文件,其中每个 sheet 包含一个大矩阵:每一列在第一行都有一个名称,在一个有点稀疏的矩阵中有许多 x-s。每个 sheet 成为数据帧列表中的一个数据帧。行名称包含与查找相关但与我的问题无关的标识符,如此处所述。
data1 <- data.frame(Col1 = c("Mark", "x", "", "x", "", ""),
Col2 = c("Paul", "", "", "", "x", ""),
Col3 = c("Jane", "", "", "", "", ""),
Col4 = c("Mary", "x", "x", "x", "", ""),
Col5 = c("Peter", "x", "x", "x", "", ""),
stringsAsFactors = FALSE)
data2 <- data.frame(Col1 = c("Mark", "x", "x", "", "", ""),
Col2 = c("Paul", "", "", "", "", ""),
Col3 = c("Jane", "", "", "", "", ""),
Col4 = c("Mary", "x", "", "x", "", ""),
Col5 = c("Peter", "x", "x", "", "", ""),
stringsAsFactors = FALSE)
data <- list(data1 = data1, data2 = data2)
列表中的每个数据框都具有以下结构(为方便起见显示为矩阵),其中列表中每个数据框的名称都相同。只有 x-s 不同:
> as.matrix(data1)
Col1 Col2 Col3 Col4 Col5
[1,] "Mark" "Paul" "Jane" "Mary" "Peter"
[2,] "x" "" "" "x" "x"
[3,] "" "" "" "x" "x"
[4,] "x" "" "" "x" "x"
[5,] "" "x" "" "" ""
[6,] "" "" "" "" ""
如果列中有 'x',我想向列表中的每个数据框添加一列 ("Approvers"),这是第 1 行中名称的串联:
Col1 Col2 Col3 Col4 Col5 Approvers
[1,] "Mark" "Paul" "Jane" "Mary" "Peter" ""
[2,] "x" "" "" "x" "x" "Mark; Mary; Peter"
[3,] "" "" "" "x" "x" "Mary; Peter"
[4,] "x" "" "" "x" "x" "Mark; Mary; Peter"
[5,] "" "x" "" "" "" "Paul"
[6,] "" "" "" "" "" ""
目前我分两步解决这个问题:
- 我创建了另一个列表列表,其中包含每个 x
的列位置
- 在嵌套的 for 循环中,我查找第一行中的所有名称并将它们连接起来。
代码如下:
position <- lapply(data, function(x) apply(x, 1, function(y) which(y %in% "x")))
position <- lapply(position, function(x) lapply(x, function(y) {if (length(y) == 0L) return(0) else return(y)})) # remove int(0) and replace with 0
position <- lapply(position, function(x) lapply(x, function(x) paste(x, collapse = ","))) # flatten second level list into string
for (i in 1:length(data)) {
for (j in 1:nrow(data[[i]])) {
if (as.numeric(unlist(strsplit(position[[i]][[j]], ",")))[[1]] == 0) {
data[[i]][j, "Approvers"] <- ""
} else {
data[[i]][j, "Approvers"] <- paste(data[[i]][1, as.numeric(unlist(strsplit(position[[i]][[j]], ",")))], collapse = "; ")
}
}
}
对我来说这很笨拙,我想使用 lapply 和 mapply 通过同时循环遍历两个列表来做到这一点,但我不知道如何做到这一点。此外,创建位置对象并将 x-s 的列索引折叠成一个字符串并在循环中将它们分开过于复杂。
我们可以使用 lapply
遍历 list
,然后使用 apply
遍历行和 paste
第一行的元素,其中值为 x
:
res <- lapply(data, function(x) {
x$Approvers <- apply(x, 1, FUN = function(y) paste(x[1,][y =="x"], collapse=";"))
x})
res
#$data1
# Col1 Col2 Col3 Col4 Col5 Approvers
#1 Mark Paul Jane Mary Peter
#2 x x x Mark;Mary;Peter
#3 x x Mary;Peter
#4 x x x Mark;Mary;Peter
#5 x Paul
#6
#$data2
# Col1 Col2 Col3 Col4 Col5 Approvers
#1 Mark Paul Jane Mary Peter
#2 x x x Mark;Mary;Peter
#3 x x Mark;Peter
#4 x Mary
#5
#6
注意:数据集的 names
似乎应该是“Mark”、'Paul' 等而不是 'Col1'、'Col2'、..
作为替代方案,整理这些数据可能是值得的,这样更容易操作和推理。此外,您想要的输出可能并不总是令人满意,因为它 returns 整行 NAs
。此处的代码重组了您的数据框,以便列名成为人名。然后它重塑数据,以便有两列,name
和来自原始数据框 (row_ix
) 的行索引,其中 "x" 出现在该名称列中。然后我删除 NAs
,按 row_ix
分组并将名称粘贴在一起,返回更整洁的数据帧。
我很欣赏这有点复杂,但以更整洁的方式存储数据可能会在较长的 运行 中为您解决问题。
library(dplyr)
library(purrr)
library(tidyr)
library(magrittr)
data %>%
map(function(x) #map function to all dataframes in list
x %>% set_colnames(.[1, ]) %>% # set column names equal to first row values
dmap(~ifelse(. == "x", seq_along(.), NA)) %>% # check for "x" in all rows of all columns
gather(name, row_ix) %>% # reshape from wide to long, call new columns name and row_ix
drop_na() %>% # drop NAs in the dataframe
group_by(row_ix) %>% # group by row index
summarise(approvers = paste0(name, collapse = ";")) # concatenate names from each group
)
$data1
# A tibble: 4 × 2
row_ix approvers
<int> <chr>
1 2 Mark;Mary;Peter
2 3 Mary;Peter
3 4 Mark;Mary;Peter
4 5 Paul
$data2
# A tibble: 3 × 2
row_ix approvers
<int> <chr>
1 2 Mark;Mary;Peter
2 3 Mark;Peter
3 4 Mary
我有一个数据框列表,其中数据框的每一列在第一行都有一个名称,在列的某些位置有 x-s。如果有 x,则第一行中的名称被视为已选中。 在现实世界的问题中,我读取了一个包含许多 sheet 的 xlsx 文件,其中每个 sheet 包含一个大矩阵:每一列在第一行都有一个名称,在一个有点稀疏的矩阵中有许多 x-s。每个 sheet 成为数据帧列表中的一个数据帧。行名称包含与查找相关但与我的问题无关的标识符,如此处所述。
data1 <- data.frame(Col1 = c("Mark", "x", "", "x", "", ""),
Col2 = c("Paul", "", "", "", "x", ""),
Col3 = c("Jane", "", "", "", "", ""),
Col4 = c("Mary", "x", "x", "x", "", ""),
Col5 = c("Peter", "x", "x", "x", "", ""),
stringsAsFactors = FALSE)
data2 <- data.frame(Col1 = c("Mark", "x", "x", "", "", ""),
Col2 = c("Paul", "", "", "", "", ""),
Col3 = c("Jane", "", "", "", "", ""),
Col4 = c("Mary", "x", "", "x", "", ""),
Col5 = c("Peter", "x", "x", "", "", ""),
stringsAsFactors = FALSE)
data <- list(data1 = data1, data2 = data2)
列表中的每个数据框都具有以下结构(为方便起见显示为矩阵),其中列表中每个数据框的名称都相同。只有 x-s 不同:
> as.matrix(data1)
Col1 Col2 Col3 Col4 Col5
[1,] "Mark" "Paul" "Jane" "Mary" "Peter"
[2,] "x" "" "" "x" "x"
[3,] "" "" "" "x" "x"
[4,] "x" "" "" "x" "x"
[5,] "" "x" "" "" ""
[6,] "" "" "" "" ""
如果列中有 'x',我想向列表中的每个数据框添加一列 ("Approvers"),这是第 1 行中名称的串联:
Col1 Col2 Col3 Col4 Col5 Approvers
[1,] "Mark" "Paul" "Jane" "Mary" "Peter" ""
[2,] "x" "" "" "x" "x" "Mark; Mary; Peter"
[3,] "" "" "" "x" "x" "Mary; Peter"
[4,] "x" "" "" "x" "x" "Mark; Mary; Peter"
[5,] "" "x" "" "" "" "Paul"
[6,] "" "" "" "" "" ""
目前我分两步解决这个问题:
- 我创建了另一个列表列表,其中包含每个 x 的列位置
- 在嵌套的 for 循环中,我查找第一行中的所有名称并将它们连接起来。
代码如下:
position <- lapply(data, function(x) apply(x, 1, function(y) which(y %in% "x")))
position <- lapply(position, function(x) lapply(x, function(y) {if (length(y) == 0L) return(0) else return(y)})) # remove int(0) and replace with 0
position <- lapply(position, function(x) lapply(x, function(x) paste(x, collapse = ","))) # flatten second level list into string
for (i in 1:length(data)) {
for (j in 1:nrow(data[[i]])) {
if (as.numeric(unlist(strsplit(position[[i]][[j]], ",")))[[1]] == 0) {
data[[i]][j, "Approvers"] <- ""
} else {
data[[i]][j, "Approvers"] <- paste(data[[i]][1, as.numeric(unlist(strsplit(position[[i]][[j]], ",")))], collapse = "; ")
}
}
}
对我来说这很笨拙,我想使用 lapply 和 mapply 通过同时循环遍历两个列表来做到这一点,但我不知道如何做到这一点。此外,创建位置对象并将 x-s 的列索引折叠成一个字符串并在循环中将它们分开过于复杂。
我们可以使用 lapply
遍历 list
,然后使用 apply
遍历行和 paste
第一行的元素,其中值为 x
:
res <- lapply(data, function(x) {
x$Approvers <- apply(x, 1, FUN = function(y) paste(x[1,][y =="x"], collapse=";"))
x})
res
#$data1
# Col1 Col2 Col3 Col4 Col5 Approvers
#1 Mark Paul Jane Mary Peter
#2 x x x Mark;Mary;Peter
#3 x x Mary;Peter
#4 x x x Mark;Mary;Peter
#5 x Paul
#6
#$data2
# Col1 Col2 Col3 Col4 Col5 Approvers
#1 Mark Paul Jane Mary Peter
#2 x x x Mark;Mary;Peter
#3 x x Mark;Peter
#4 x Mary
#5
#6
注意:数据集的 names
似乎应该是“Mark”、'Paul' 等而不是 'Col1'、'Col2'、..
作为替代方案,整理这些数据可能是值得的,这样更容易操作和推理。此外,您想要的输出可能并不总是令人满意,因为它 returns 整行 NAs
。此处的代码重组了您的数据框,以便列名成为人名。然后它重塑数据,以便有两列,name
和来自原始数据框 (row_ix
) 的行索引,其中 "x" 出现在该名称列中。然后我删除 NAs
,按 row_ix
分组并将名称粘贴在一起,返回更整洁的数据帧。
我很欣赏这有点复杂,但以更整洁的方式存储数据可能会在较长的 运行 中为您解决问题。
library(dplyr)
library(purrr)
library(tidyr)
library(magrittr)
data %>%
map(function(x) #map function to all dataframes in list
x %>% set_colnames(.[1, ]) %>% # set column names equal to first row values
dmap(~ifelse(. == "x", seq_along(.), NA)) %>% # check for "x" in all rows of all columns
gather(name, row_ix) %>% # reshape from wide to long, call new columns name and row_ix
drop_na() %>% # drop NAs in the dataframe
group_by(row_ix) %>% # group by row index
summarise(approvers = paste0(name, collapse = ";")) # concatenate names from each group
)
$data1
# A tibble: 4 × 2
row_ix approvers
<int> <chr>
1 2 Mark;Mary;Peter
2 3 Mary;Peter
3 4 Mark;Mary;Peter
4 5 Paul
$data2
# A tibble: 3 × 2
row_ix approvers
<int> <chr>
1 2 Mark;Mary;Peter
2 3 Mark;Peter
3 4 Mary