【问题标题】:R: Convert vectors of arbitrary concatenated variable names and values to single data frameR:将任意连接变量名称和值的向量转换为单个数据框
【发布时间】:2020-08-01 14:58:54
【问题描述】:

我有一个包含两列和多行的数据框。

第一列是一个字符向量,其中每个元素 P 是一个字符串,它用逗号连接多个 (K) 字符串。 K 事先是未知的,并且可以跨行变化,例如第一行 K = 5,第二行 K = 3。连接起来的值在各行之间可能相同,也可能不同,尽管它们不会在一行内重复。我们可以称这些“变量名”。

第二列——我们可以称之为“变量值”——是一个字符向量,其中每个元素也是一个用逗号连接 K 个字符串的字符串。重要的是,连接的字符串数量与变量名称的数量相同。换句话说,变量名称列包含一个包含变量名称的字符串,变量值列包含与该行的变量名称对应的值。

这是我的数据的一个最小示例。请注意,例如,子字符串的数量var_names[i]values[i] 中的数字相同,但不必与 var_names[j] 相同:

# Example data
data <-
  data.frame(
    var_names = c(
      paste("a", "b", "c", "e", "j", sep = ","),
      paste("d", "a", "f", sep = ","),
      paste("f", "k", "b", "a", sep = ",")
    ),
    values = c(
      paste("212", "12", "sfd", "3", "1", sep = ","),
      paste("fds", "23", "g", sep = ","),
      paste("df", "sdf", "w2", "w", sep = ",")
    ),
    stringsAsFactors = FALSE
  )

鉴于这些数据,我正在尝试创建一个数据框,其中 var_names 中的每个唯一值都是一个列名,每列的值基于 values 中每一行的相应索引数据。具体来说,我希望制作:

data.frame(a = c("212","23","w"), 
           b = c("12",NA,"w2"),
           c = c("sfd",NA,NA),
           d = c(NA,"fds",NA),
           e = c("3", NA, NA),
           f = c(NA, "g", "df"),
           j = c("1"," NA, NA),
           k = c(NA,NA,"sdf"))

我能够使用以下内容制作我想要的东西。但是,我想知道是否有一些功能/包可以让我跳过其中一些步骤并更快地完成这项工作。目前,我创建了一个循环,为每一行生成整个数据框,然后将它们组合成一个数据框。我最初的想法是在我的代码中获取var_val 对象并使用tidyr::pivot_wider() 生成每一行的数据框,但由于规范错误,这不起作用。

# Split variable names and values into a list
# where each element is a row's values/names
vars_name_l <- strsplit(data$var_names, split = ",")
values_l <- strsplit(data$values, split = ",")

# Initialize a list to store each row's 
# data frame 
combined <- list()

# Loop through each row's data and generate a
# list of data frames 
for (i in 1:length(nrow(data))) {

# Get a row's variable names and values into
# a data frame. 
var_val <- data.frame(var_names = vars_name_l[[i]], 
                      values = values_l[[i]],
                      stringsAsFactors = FALSE)

# Create an empty data frame then add variable
# names and the values for the variables, store in
# our list
df <- as.data.frame(matrix(numeric(), nrow = 0, ncol = length(var_val$var_names)))
colnames(df) <- var_val$var_names
df[1, ] <- var_val$values
combined[[i]] <- df
}

# Collapse list to a single data frame, rearrange
result <- bind_rows(combined)
result[ ,order(colnames(result))]

【问题讨论】:

    标签: r list dataframe dplyr data-manipulation


    【解决方案1】:

    我们可以先从var_namesvalues 列中获取单独的行中的数据,然后获取宽格式的数据。

    library(dplyr)
    library(tidyr)
    
    data %>%
      mutate(row = row_number()) %>%
      separate_rows(var_names, values) %>%
      pivot_wider(names_from = var_names, values_from = values) %>%
      select(-row)
    
    #   a     b     c     e     j     d     f     k    
    #  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
    #1 212   12    sfd   3     1     NA    NA    NA   
    #2 23    NA    NA    NA    NA    fds   g     NA   
    #3 w     w2    NA    NA    NA    NA    df    sdf  
    

    【讨论】:

      【解决方案2】:

      我们可以通过bind_rows 轻松做到这一点

      library(dplyr)
      bind_rows(do.call(Map, c(f = setNames, lapply(unname(data)[2:1], strsplit, ","))))
      # A tibble: 3 x 8
      #  a     b     c     e     j     d     f     k    
      #* <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
      #1 212   12    sfd   3     1     <NA>  <NA>  <NA> 
      #2 23    <NA>  <NA>  <NA>  <NA>  fds   g     <NA> 
      #3 w     w2    <NA>  <NA>  <NA>  <NA>  df    sdf  
      

      也可以

      bind_rows(do.call(Map, c(f = function(x, y)
          setNames(as.list(x), y), lapply(unname(data)[2:1], strsplit, ","))))
      

      或者另一个选项是unnest_wider from tidyr

      library(tidyr)
      library(purrr)
      data %>%
           mutate_all(strsplit, ",") %>%
           transmute(new = map2(values, var_names, ~ set_names(as.list(.x), .y))) %>%
           unnest_wider(c(new))
      # A tibble: 3 x 8
      #  a     b     c     e     j     d     f     k    
      #  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
      #1 212   12    sfd   3     1     <NA>  <NA>  <NA> 
      #2 23    <NA>  <NA>  <NA>  <NA>  fds   g     <NA> 
      #3 w     w2    <NA>  <NA>  <NA>  <NA>  df    sdf  
      

      或者使用来自data.tablerbindlist

      library(data.table)
      rbindlist(do.call(Map, c(f = function(x, y)
           setNames(as.list(x), y), lapply(unname(data)[2:1], strsplit, ","))),
           fill = TRUE)
      #     a    b    c    e    j    d    f    k
      #1: 212   12  sfd    3    1 <NA> <NA> <NA>
      #2:  23 <NA> <NA> <NA> <NA>  fds    g <NA>
      #3:   w   w2 <NA> <NA> <NA> <NA>   df  sdf
      

      【讨论】:

      • @user3614648 它对我来说工作正常。可能是dplyr的包版本
      • @user3614648 你能试试更新的吗?我正在使用开发版本。所以可能是第一个有一些冲突
      • @user3614648 我看到的主要问题是第一个是命名向量,而第二个是命名列表,命名列表不应与bind_rows 产生错误
      • @user3614648 我添加了 3 个选项,希望其中一个对你有用
      猜你喜欢
      • 2020-01-01
      • 2019-06-20
      • 2018-03-31
      • 1970-01-01
      • 1970-01-01
      • 2021-06-02
      • 2013-11-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多