代码之家  ›  专栏  ›  技术社区  ›  Paul van Oppen

使用apply函数迭代两个列表

  •  1
  • Paul van Oppen  · 技术社区  · 7 年前

    我有一个问题,我有一个数据帧列表,其中数据帧的每列在第一行有一个名称,在列中的某些位置有x-s。如果有x,则第一行中的名称被视为选中。 在实际问题中,我阅读了一个包含许多工作表的xlsx文件,其中每个工作表都包含一个大矩阵:每列的第一行有一个名称,在一个稀疏的矩阵中有许多x-s。每个工作表都成为数据帧列表中的一个数据帧。行名称包含一个标识符,该标识符与查找相关,但与此处描述的我的问题无关。

    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”,则该列是第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,] ""     ""     ""     ""     ""      ""   
    

    目前,我分两步解决这个问题:

    1. 在嵌套的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 = "; ")
        }
      }
    }
    

    对我来说,这很笨拙,我想使用lappy和mapply同时遍历两个列表来实现这一点,但我不知道如何做到这一点。此外,创建position对象并将x-s的列索引折叠成字符串,然后在循环中将其分离,这过于复杂。

    2 回复  |  直到 7 年前
        1
  •  2
  •   akrun    7 年前

    我们可以使用 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”,。。

        2
  •  1
  •   conor    7 年前

    作为替代方案,可能值得整理这些数据,以便更容易操作和推理。此外,您想要的输出可能并不总是理想的,因为它返回了完整的行 NAs . 这里的代码重构了数据帧,使列名成为人名。然后对数据进行整形,使其有两列 name row_ix )其中,该名称列显示“x”。然后我放下 NAs 然后将名称粘贴在一起,返回更整洁的数据帧。

    我很欣赏这一点,但从长远来看,以更整洁的方式存储数据可能会为您节省问题。

    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