R: условно объединить текст из соседних строк, сохранив при этом связанную информацию - PullRequest
1 голос
/ 30 января 2020

Сценарий должен:

a) объединять текст в смежные строки, количество смежных строк может варьироваться, группировка строк, которые нужно объединить, определяется первой строкой, которой предшествует NA, и последней строка, за которой следует NA,

b) сохранить идентификаторы строк для дальнейшей проверки

c) сохранить переменную Numberri c, связанную с одной строкой из каждой из смежных строк, которые нужно объединить

d) сохранить общий порядок

Before and after tables

Я добился этого, используя для l oop и загрузки данных с dplyr и stringer.

Для l oop не элегантно, так как я борюсь с логикой c, чтобы последовательно идентифицировать соседние строки. Это не важно, так как группирующая переменная - всего лишь помощник - но это меня раздражает.

Мне также интересно, может ли быть более эффективный способ сделать это вообще, возможно, используя строку и мутировать с опережением или запаздыванием.

Будем благодарны за любые указания или указатели.

library(tidyverse)

tib <- tibble(id = 1:11,
              var = c("a", NA, NA, "b", "c" , NA, "d", NA, NA, NA, "e"),
              txt = c( NA, "the", "cat",  NA,  NA, "sat", NA, "on", "the", "mat", NA),
              nr = c( NA,  NA, 5, NA, NA, 10, 7, NA, NA, 15, 11),
              txt_group = NA_integer_)

# txt_group = helper column for text grouping variable

txt_group_counter <- 1L


for(i in seq_len(nrow(tib))){

  if (!is.na(tib$txt[i]) | !is.na(lag(tib$txt[i]))){

    tib$txt_group[i] <- txt_group_counter
   } 

  if(is.na(tib$txt[i]) | !is.na(lead(tib$txt[i]))){

    txt_group_counter <- txt_group_counter + 1
  }

}


tib1 <- 
  tib %>%
  filter(!is.na(txt_group)) %>% 
  group_by(txt_group) %>% 
  mutate(id_comb = paste(id, collapse = ", "),
         txt = paste(txt, collapse = " "),
         nr = paste(nr, collapse = "")) %>% 
  select(-id) %>% 
  distinct() %>% 
  ungroup() %>% 
  mutate(id = as.numeric(str_extract(id_comb, "^\\d")),
         nr = as.numeric(str_remove_all(nr, "[NA]"))) %>% 
  select(id, id_comb, everything()) %>% 
  bind_rows(tib %>% filter(is.na(txt_group))) %>% 
  arrange(id) %>% 
  select(-txt_group)

1 Ответ

1 голос
/ 30 января 2020

Далее используется вспомогательная переменная, созданная со стандартным приемом cumsum/diff, для определения групп, а затем paste строк вместе.

Код выполняет следующие действия:

  1. Создает логическую переменную eq, которая сообщает, равны ли два последовательных значения var. Поскольку первое не может быть равным предыдущему (до того, как оно не существует), я дополняю FALSE.
  2. Некоторые значения NA, замените их на FALSE, все NA Это отличается от всего остального, включая другие NA.
  3. Теперь трюк cumsum, чтобы иметь точки останова, где есть TRUE (var отличается от следующего значения см. пункт 1 выше), действуйте как счетчик Это дает группам изменение var.
  4. И то, что в основном относится к is.na(var). cumsum очень полезен для создания группирующих векторов и заслуживает места в сумке с трюками R.
  5. mutate_at удаляет значения NA, они будут повторяться NA NA, когда строки будут объединены. Таким образом, это "" пустые строки, которые объединяются.
  6. Группируйте по ok, eq и объединяйте с paste. trimws, вероятно, не требуется, но это не повредит, если только набор данных не очень большой и код не оптимизирован для времени.
  7. Разгруппировать и удалить созданные временные столбцы; замените пробелы в результатах запятыми.

Вот оно:

tib %>% 
  mutate(eq = c(FALSE, var[-length(var)] != var[-1]),
         eq = ifelse(is.na(eq), FALSE, eq),
         eq = cumsum(abs(c(diff(eq), 0))),
         ok = cumsum(abs(c(0, diff(is.na(var)))))) %>%
  mutate_at(vars(var:txt_group), list(function(x) ifelse(is.na(x), "", x))) %>%
  group_by(ok, eq) %>% 
  summarise_all(funs(trimws(paste(., collapse = " ")))) %>%
  ungroup() %>%
  select(-ok, -eq) %>%
  mutate(id = gsub(" ", ",", id),
         var = gsub(" ", ",", var))
## A tibble: 8 x 5
#  id     var   txt          nr    txt_group
#  <chr>  <chr> <chr>        <chr> <chr>    
#1 1      "a"   ""           ""    ""       
#2 2,3    ""    "the cat"    "5"   ""       
#3 4      "b"   ""           ""    ""       
#4 5      "c"   ""           ""    ""       
#5 6      ""    "sat"        "10"  ""       
#6 7      "d"   ""           "7"   ""       
#7 8,9,10 ""    "on the mat" "15"  ""       
#8 11     "e"   ""           "11"  ""  
...