Как сгенерировать несколько строк из каждой отдельной строки фрейма данных с функцией (ами), а затем присоединить / объединить их обратно?

Я начинаю с фрейма данных, где каждая строка имеет длинную строку, представляющую 2D-среду (назовем ее ландшафтом) в 1D. В реальном случае это примерно 6 значений в высоту и 80 значений в длину, и, таким образом, в 1D строки имеют длину 480 символов. Я сократил их в примере. Каждая строка также имеет уникальное имя, которое является сокращенным идентификатором для каждого ландшафта.

У меня есть функция, которая берет каждую строку, разрезает ее на 6 полос и анализирует каждую. В этом примере основным действием функции является сжатие полос и получение длины сжатия. Эта функция приводит к созданию фрейма данных с 6 строками, который мне нужно объединить с исходным фреймом данных, в результате чего конечный фрейм данных имеет 6 строк на каждую строку оригинала.

library(dplyr)
library(tibble)

master_df <- tribble(~land_id, ~land_string,
                     "v1-few_bs", "aaaaaaaaaabaaaaabaaabababaabab",
                     "v2-more_bs", "aaaaaaaaaababbabbbabbababaabbb")

compress_it <- function(txt) {
  len.raw <- sum(nchar(txt))
  len.gz <- length(memCompress(txt, "g"))
  return(list("len_raw" = len.raw,
              "len_gz" = len.gz))
}

get_strip_data <- function(land_id, land_string) {
    with_spaces <- gsub("(.{5})", "\\1 ", land_string)
    chars_on_lines <- str_replace_all(with_spaces, pattern = " ", "\n")
    prob_matrix <- read.table(text = chars_on_lines, header=FALSE, sep = " ",
                              stringsAsFactors = FALSE)
    prob_matrix <- mutate(prob_matrix, 
                          land_id = land_id,
                          substr_id = 1:nrow(prob_matrix) )
    prob_matrix <- rename(prob_matrix, land_substring = V1)

    mutate(prob_matrix, new = map(land_substring, compress_it)) %>%
    unnest_wider(c(new))
}

get_strip_data(master_df$land_id[[2]], master_df$land_string[[2]]) # to test the above function

Здесь мы переходим к псевдокоду / klugecode для того, что я пытаюсь сделать.

Сначала я создаю пустой фрейм данных.

subchunks_df <- 
  tribble(~land_id, ~land_string, ~land_substring, ~substr_id, ~len_raw, ~len_gz,
          "", "", "", NA, NA, NA)

Попытка цикла for:

for ( i in 1:nrow(master_df) ) {
  subchunks_df[i, ] <- get_strip_data(master_df$land_id[[i]], master_df$land_string[[i]])
}

Вместо этого попробуйте mapply:

subchunks_df <- mapply(get_strip_data, 
                       land_id = master_df$land_id, 
                       land_string = master_df$land_string)

Неа. Я стараюсь, чтобы быть щедрым, «близко, но без сигары».

Если бы я мог получить subchunks_df в правильной форме, я бы тогда right_join:

final_df <- right_join(master_df, subchunks_df, by = "land_id")

Это желаемый результат, учитывая, что master_df выполняет функции:

final_df <- 
  tribble(~land_id, ~land_string, ~land_substring, ~substr_id, ~len_raw, ~len_gz, 
          "v1-few_bs", "aaaaaaaaaabaaaaabaaabababaabab", "aaaaa", 1, 5, 11,     
          "v1-few_bs", "aaaaaaaaaabaaaaabaaabababaabab", "aaaaa", 2, 5, 11,     
          "v1-few_bs", "aaaaaaaaaabaaaaabaaabababaabab", "baaaa", 3, 5, 11,     
          "v1-few_bs", "aaaaaaaaaabaaaaabaaabababaabab", "abaaa", 4, 5, 13,     
          "v1-few_bs", "aaaaaaaaaabaaaaabaaabababaabab", "babab", 5, 5, 13,     
          "v1-few_bs", "aaaaaaaaaabaaaaabaaabababaabab", "aabab", 6, 5, 13,
          "v2-more_bs", "aaaaaaaaaababbabbbabbababaabbb", "aaaaa", 1, 5, 11,        
          "v2-more_bs", "aaaaaaaaaababbabbbabbababaabbb", "aaaaa", 2, 5, 11,        
          "v2-more_bs", "aaaaaaaaaababbabbbabbababaabbb", "babba", 3, 5, 13,        
          "v2-more_bs", "aaaaaaaaaababbabbbabbababaabbb", "bbbab", 4, 5, 13,        
          "v2-more_bs", "aaaaaaaaaababbabbbabbababaabbb", "babab", 5, 5, 13,        
          "v2-more_bs", "aaaaaaaaaababbabbbabbababaabbb", "aabbb", 6, 5, 13)

Как всегда, я был бы признателен за это как с точки зрения dplyr, так и с точки зрения базы R. Я не уверен, что моя функция по снятию полосок и получению длины сжатия так же хороша. Я не мог найти ничего проще. Но эта последняя миля была настоящей проблемой.


person Stan Rhodes    schedule 30.04.2020    source источник


Ответы (2)


Функционал карты - это тидиверсия семейства apply. Функция map_dfr использует индекс vector master_df $ land_id. Подумайте об этом как о цикле for. Это дает вам фрейм данных, который вы ищете для вызова right_join.

library(tidyverse)
subchunks_df <- map_dfr(seq_along(master_df$land_id), function(i){
                     get_strip_data(master_df$land_id[[i]], 
                                    master_df$land_string[[i]])})

final_df <- right_join(master_df, subchunks_df, by = "land_id")
person NotThatKindODr    schedule 30.04.2020
comment
Я также не мог заставить цикл for работать правильно, поэтому я, наконец, опубликовал. Я мельком увидел ваш первый пример с использованием subchunks_df <-i map_dfr(master_df$land_id, ~ get_strip_data(master_df$land_id[[.y]], master_df$land_string[[.y]]) ), который работает, но кажется медленнее, чем ваш последний отредактированный пример. Надеюсь, кто-то также предоставит базовый пример R, у меня проблемы с пониманием как map, так и apply. И понимание синтаксиса семейств for циклов, map и apply для R кажется важным для меня (и других), чтобы хорошо освоить R! - person Stan Rhodes; 01.05.2020

Вы можете использовать Map, а затем связать данные вместе с do.call + rbind

subchunks_df <- do.call(rbind, Map(get_strip_data, master_df$land_id, 
                                    master_df$land_string))

Или, если вы предпочитаете tidyverse, вы можете использовать map2_df

subchunks_df <- purrr::map2_df(master_df$land_id, master_df$land_string, 
                               get_strip_data)

# A tibble: 12 x 5
#   land_substring land_id    substr_id len_raw len_gz
#   <chr>          <chr>          <int>   <int>  <int>
# 1 aaaaa          v1-few_bs          1       5     11
# 2 aaaaa          v1-few_bs          2       5     11
# 3 baaaa          v1-few_bs          3       5     11
# 4 abaaa          v1-few_bs          4       5     13
# 5 babab          v1-few_bs          5       5     13
# 6 aabab          v1-few_bs          6       5     13
# 7 aaaaa          v2-more_bs         1       5     11
# 8 aaaaa          v2-more_bs         2       5     11
# 9 babba          v2-more_bs         3       5     13
#10 bbbab          v2-more_bs         4       5     13
#11 babab          v2-more_bs         5       5     13
#12 aabbb          v2-more_bs         6       5     13

а затем right_join:

final_df <- dplyr::right_join(master_df, subchunks_df, by = "land_id")
person Ronak Shah    schedule 01.05.2020
comment
В вашем первом примере map также не входит в purrr, и, следовательно, это tidyverse пример? map определенно не похоже на базу R. - person Stan Rhodes; 01.05.2020
comment
@StanRhodes map отличается от Map. map находится в purrr, тогда как Map - это база R. R чувствителен к регистру. - person Ronak Shah; 02.05.2020
comment
Mea culpa, мои извинения. Эту функцию сложно найти в документации R. Я нашел кое-что в funprog {base}: Map - это простая оболочка для mapply, которая не пытается упростить результат, подобно mapcar Common Lisp (однако, аргументы повторно используются). - person Stan Rhodes; 04.05.2020