【问题标题】:R: Loop through list to compare column names and create new fields in data frameR:遍历列表以比较列名并在数据框中创建新字段
【发布时间】:2017-11-12 05:17:08
【问题描述】:

我使用jsonlite 扁平化了 json 数据,并添加了一些奇异的列名称。请参阅下面的示例数据:

df <- data.frame("ID" = c(1,2,3,4))
df$`events.location.John.round.1` = 
list(list("A","B","C"),list("B","C","E"),list("A","C"),list("D","E","B"))
df$`events.location.John.round.2` = 
list(list("A","D","E"),NA,list("B","C"),list("B","E","C"))
df$`events.location.Mary.round.1` = list(NA,NA,list("B","C"),list("E","A"))
df$`events.location.Mary.round.2` = list(list("A","B","E"),NA,list("B","A"),list("D","E","C"))

LocationList <- c("A","B","C","D","E")
PersonList <- c("John", "Mary") 

我想遍历我的位置和人员列表,以在我的数据框中生成新变量。这是一些示例输出:

df$`NumLocationsJohnRound1` = c(3,3,2,3)
df$`NumLocationsMaryRound1` = c(0,0,2,2)
df$`B.JohnRound1` = c(1,1,0,1)
df$`B.MaryRound1` = c(0,0,1,0)

在英语中,第一个操作是“对于 PersonList 中的每个人,查找包含人名和可能其他文本的列名,并返回该单元格列表的长度”

第二个操作是“对于 PersonList 中的每个人,查找包含该人姓名的列名,并为 LocationList 中的每个位置创建一个新的二进制字段,如果该列包含该字段,则为 1位置。

基本上,我需要的所有新变量要么将函数应用于匹配列,要么在单元格中的列表中搜索某个值。这里的关键是迭代列表、按名称查找列以及根据列表生成新列的灵活方法。

我认为解决方案取决于Regex/grep(),但我不知道如何将列表项插入正则表达式字符串(可能使用paste?)。 select(contains()) 可能是其中的步骤之一。

解决方案可能涉及purrr::map()dplyr::mutate(),或者可能涉及这些的自定义函数。我想避免完全依赖 for 循环。

我知道这是一个具有挑战性的问题。深入了解其中的任何部分(如何查找名称中包含列表项的列,如何根据列表项创建具有名称的新列,如何搜索列表列)会很有帮助。

【问题讨论】:

    标签: r list dataframe purrr


    【解决方案1】:

    尚不完全清楚,但根据“Numlocations”输出,这可能会有所帮助

    library(dplyr)
    library(purrr)
    nm1 <- sub("events", "Num", names(df)[-1])
    df[nm1] <-  df[-1] %>% 
                      map(., ~lengths(.) *map_lgl(., ~ !all(is.na(.))))
    

    对于第二部分,我们可以用mtabulate得到二进制输出

    library(qdapTools)
    nm2 <-rep(paste0(names(df)[2:5], sub("events.location", "", names(df)[2:5])), each = 5) 
    df[nm2] <- df[2:5] %>% 
                    map(mtabulate) %>% 
                    bind_cols
    

    或者这一切都可以在链内完成

    nm3 <- sub("events.location", "", names(df)[2:5])
    df[-1] %>%
      map_df(., ~lengths(.) *map_lgl(., ~ !all(is.na(.)))) %>% 
               rename_all(~nm1) %>%
               bind_cols(df, ., 
                 df[-1] %>%
                   map(., ~map(., ~factor(., levels = LETTERS[1:5]))) %>% 
                   map(~as.data.frame.matrix(table(melt(.)[2:1]))) %>% 
                   map2(., nm3, ~setNames(.x,  paste0(names(.x), .y))))
    

    给出输出

    #ID events.location.John.round.1 events.location.John.round.2 events.location.Mary.round.1 events.location.Mary.round.2 Num.location.John.round.1
    #1  1                      A, B, C                      A, D, E                           NA                      A, B, E                         3
    #2  2                      B, C, E                           NA                           NA                           NA                         3
    #3  3                         A, C                         B, C                         B, C                         B, A                         2
    #4  4                      D, E, B                      B, E, C                         E, A                      D, E, C                         3
    #  Num.location.John.round.2 Num.location.Mary.round.1 Num.location.Mary.round.2 A.John.round.1 B.John.round.1 C.John.round.1 D.John.round.1 E.John.round.1
    #1                         3                         0                         3              1              1              1              0              0
    #2                         0                         0                         0              0              1              1              0              1
    #3                         2                         2                         2              1              0              1              0              0
    #4                         3                         2                         3              0              1              0              1              1
    #  A.John.round.2 B.John.round.2 C.John.round.2 D.John.round.2 E.John.round.2 A.Mary.round.1 B.Mary.round.1 C.Mary.round.1 D.Mary.round.1 E.Mary.round.1 A.Mary.round.2
    #1              1              0              0              1              1              0              0              0              0              0              1
    #2              0              0              0              0              0              0              0              0              0              0              0
    #3              0              1              1              0              0              0              1              1              0              0              1
    #4              0              1              1              0              1              1              0              0              0              1              0
    #  B.Mary.round.2 C.Mary.round.2 D.Mary.round.2 E.Mary.round.2
    #1              1              0              0              1
    #2              0              0              0              0
    #3              1              0              0              0
    #4              0              1              1              1
    

    【讨论】:

      【解决方案2】:

      使用dplyrpurrr 的解决方案

      首先,使用mutate_at 计算所有以“events”开头的列的列表长度。

      library(dplyr)
      library(purrr)
      
      df2 <- df %>%
        mutate_at(vars(starts_with("events")), funs(`Len` = map(., ~length(.x[!is.na(.x)]))))
      

      之后,设计一个函数来报告二进制结果。将该函数应用于LocationList 中的所有元素。将结果存储在loc_results

      match_fun <- function(Location, df){
        df2 <- df %>%
          mutate_at(vars(starts_with("events")), 
                    funs(!!Location := map_int(., ~as.integer(Location %in% unlist(.x))))) %>%
          select(ID, contains("_"))
        return(df2)
      }
      
      loc_results <- map(LocationList, match_fun, df = df)
      

      最后将loc_results中的所有数据框合并为df3,然后将df2df3合并为df4df4 是最终输出。

      df3 <- reduce(loc_results, left_join, by = "ID")
      df4 <- df2 %>% left_join(df3, by = "ID")
      

      此解决方案考虑了命名约定。下面是生成的数据框。如您所见,以_Len 结尾的列显示列表的长度,而以_A_B_C_D_E 结尾的列显示二进制结果。

      df4
      
        ID events.location.John.round.1 events.location.John.round.2 events.location.Mary.round.1 events.location.Mary.round.2
      1  1                      A, B, C                      A, D, E                           NA                      A, B, E
      2  2                      B, C, E                           NA                           NA                           NA
      3  3                         A, C                         B, C                         B, C                         B, A
      4  4                      D, E, B                      B, E, C                         E, A                      D, E, C
        events.location.John.round.1_Len events.location.John.round.2_Len events.location.Mary.round.1_Len events.location.Mary.round.2_Len
      1                                3                                3                                0                                3
      2                                3                                0                                0                                0
      3                                2                                2                                2                                2
      4                                3                                3                                2                                3
        events.location.John.round.1_A events.location.John.round.2_A events.location.Mary.round.1_A events.location.Mary.round.2_A
      1                              1                              1                              0                              1
      2                              0                              0                              0                              0
      3                              1                              0                              0                              1
      4                              0                              0                              1                              0
        events.location.John.round.1_B events.location.John.round.2_B events.location.Mary.round.1_B events.location.Mary.round.2_B
      1                              1                              0                              0                              1
      2                              1                              0                              0                              0
      3                              0                              1                              1                              1
      4                              1                              1                              0                              0
        events.location.John.round.1_C events.location.John.round.2_C events.location.Mary.round.1_C events.location.Mary.round.2_C
      1                              1                              0                              0                              0
      2                              1                              0                              0                              0
      3                              1                              1                              1                              0
      4                              0                              1                              0                              1
        events.location.John.round.1_D events.location.John.round.2_D events.location.Mary.round.1_D events.location.Mary.round.2_D
      1                              0                              1                              0                              0
      2                              0                              0                              0                              0
      3                              0                              0                              0                              0
      4                              1                              0                              0                              1
        events.location.John.round.1_E events.location.John.round.2_E events.location.Mary.round.1_E events.location.Mary.round.2_E
      1                              0                              1                              0                              1
      2                              1                              0                              0                              0
      3                              0                              0                              0                              0
      4                              1                              1                              1                              1
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-02-20
        • 1970-01-01
        • 2019-08-26
        • 2018-09-05
        • 2021-03-31
        • 1970-01-01
        • 2020-12-16
        • 2013-03-04
        相关资源
        最近更新 更多