tidyr использует separate_rows для нескольких столбцов

#r #apply #tidyr

#r #применить #tidyr

Вопрос:

У меня есть data.frame, где некоторые ячейки содержат строки значений, разделенных запятыми:

 d <- data.frame(a=c(1:3), 
       b=c("name1, name2, name3", "name4", "name5, name6"),
       c=c("name7","name8, name9", "name10" ))
  

Я хочу разделить те строки, где каждое имя разбивается на отдельную ячейку. Это легко с

 tidyr::separate_rows(d, b, sep=",") 
  

если это делается для одного столбца за раз. Но я не могу сделать это для обоих столбцов «b» и «c» одновременно, поскольку для этого требуется, чтобы количество имен в каждой строке было одинаковым. Вместо записи

 tidyr::separate_rows(d, b, sep=",") 
tidyr::separate_rows(d, c, sep=",") 
  

Есть ли способ сделать это в однострочной строке, например, с помощью apply ? Что — то вроде

 apply(d, 2, separate_rows(...)) 
  

Не уверен, как передать аргументы separate_rows() функции.

Ответ №1:

Вы можете использовать канал. Обратите внимание, что sep = ", " это определяется автоматически.

 d %>% separate_rows(b) %>% separate_rows(c)
#   a     b      c
# 1 1 name1  name7
# 2 1 name2  name7
# 3 1 name3  name7
# 4 2 name4  name8
# 5 2 name4  name9
# 6 3 name5 name10
# 7 3 name6 name10
  

Примечание: используется tidyr версии 0.6.0, где %>% оператор включен в пакет.


Обновление: используя комментарий @doscendodiscimus, мы могли бы использовать for() цикл и переназначать d на каждой итерации. Таким образом, у нас может быть столько столбцов, сколько нам нравится. Мы будем использовать символьный вектор имен столбцов, поэтому нам нужно переключиться на стандартную ознакомительную версию, separate_rows_ .

 cols <- c("b", "c")
for(col in cols) {
    d <- separate_rows_(d, col)
}
  

что дает обновленный d

   a     b      c
1 1 name1  name7
2 1 name2  name7
3 1 name3  name7
4 2 name4  name8
5 2 name4  name9
6 3 name5 name10
7 3 name6 name10
  

В качестве обновления: в tidyr1.2.0 separate_rows_ устарел. Вместо этого мы можем использовать следующий код:

 cols <- c("b", "c")
for (col in cols) {
  d <- separate_rows(d, all_of(col))
}
  

Комментарии:

1. Да, я мог бы просто написать одну и ту же строку для каждой строки, но есть ли способ сделать это для n столбцов? Это становится утомительным, если у вас 10 или более столбцов. Что касается аргумента cols, если вы включаете оба столбца одновременно, вы получаете сообщение об ошибке.

2. @user23413, вы могли бы попробовать цикл, например for(col in c("b", "c")) d <- separate_rows_(d, col, sep = ",") , если вы хотите придерживаться tidyr

3. @docendodiscimus — Вы хотите опубликовать это? Я согласен, что это правильный путь.

4. @RichScriven, нет, вы можете добавить это в свой пост, если вам это нравится 🙂

5. Также можно попробовать этот вариант: d %>% Reduce(f = separate_rows_, x = c("b", "c"))

Ответ №2:

Вот альтернативный подход, использующий splitstackshape::cSplit и zoo::na.locf .

 library(splitstackshape)
library(zoo)

df <- cSplit(d, 1:ncol(d), "long", sep = ",")
na.locf(df[rowSums(is.na(df)) != ncol(df),])
#    a     b      c
#1:  1 name1  name7
#2:  1 name2  name7
#3:  1 name3  name7
#4:  2 name4  name8
#5:  2 name4  name9
#6:  3 name5 name10
#7:  3 name6 name10
  

Ответ №3:

С tidyr версией 1.2.0 мы можем использовать everything для выбора всех столбцов для разделения строк , . Как упоминалось @RichScriven, разделителем по умолчанию является sep = ", " .

 library(tidyr)

d %>% 
  separate_rows(everything())
  

Вывод

       a b     c     
  <int> <chr> <chr> 
1     1 name1 name7 
2     1 name2 name7 
3     1 name3 name7 
4     2 name4 name8 
5     2 name4 name9 
6     3 name5 name10
7     3 name6 name10
  

В качестве альтернативы мы можем указать столбцы, в которых мы хотим разделить строки, или мы можем просто исключить столбцы, которые нам не нужны.

 d %>% 
  separate_rows(b, c)


d %>% 
  separate_rows(-a)