R: Преобразовать векторы произвольных объединенных имен и значений переменных в один фрейм данных. - PullRequest
1 голос
/ 19 апреля 2020

У меня есть фрейм данных с двумя столбцами и множеством строк.

Первый столбец - это символьный вектор, где каждый элемент 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
  )

. Учитывая эти данные, я пытаюсь создать данные frame, где каждое из уникальных значений в 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"))

Я смог произвести то, что хотел, используя нижеприведенное. Однако мне было интересно, может ли быть какая-нибудь функция / пакет, который позволил бы мне пропустить некоторые из этих шагов и выполнить sh быстрее. В настоящее время я создаю al oop, который генерирует весь фрейм данных для каждой строки, а затем объединяет их в один фрейм данных. Сначала я хотел взять в своем коде объект var_val и использовать tidyr::pivot_wider() для генерации фрейма данных каждой строки, но это не сработало из-за ошибки spe c.

# 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))]

Ответы [ 2 ]

1 голос
/ 19 апреля 2020

Мы можем сделать это с помощью 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 из 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  

Или используя rbindlist из data.table

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
0 голосов
/ 19 апреля 2020

Сначала мы можем получить данные в отдельных строках из столбцов var_names и values, а затем получить данные в широком формате.

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  
...