Разделить строку с разделителями на новые строки с преобразованием типа, установленным в TRUE

#r #string #split #data.table

#r #строка #разделить #data.table

Вопрос:

data.table::tstrsplit имеет полезный аргумент type.convert. Но он выдает ошибку, когда после разделения каждая строка преобразуется в другой класс, см. Пример:

 library(data.table)

x <- fread("CHROM POS REF ALT TYPE AF
chr1 1 A T MISSENSE 0.23
chr2 1 A T,G MISSENSE 0.17,0.09")
 

В столбце ALT у нас есть «T» и «T, G», поэтому первая строка преобразуется в логическое «TRUE», а вторая строка разбивается и преобразуется в символы «T» и «G». В результате мы получаем ошибку ниже:

 x[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed = TRUE, type.convert = TRUE))),
  by = .(CHROM, POS, REF, TYPE)]

# Error in `[.data.table`(x, , lapply(.SD, function(x) unlist(tstrsplit(x,  : 
#   Column 1 of result for group 2 is type 'character' but expecting type
#   'logical'. Column types must be consistent for each group.
 

Мы могли бы избежать автоматического преобразования и преобразовать позже вручную, все отлично:

 x[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed = TRUE))),
  by = .(CHROM, POS, REF, TYPE)][, .(CHROM, POS, REF, ALT, TYPE, AF = as.numeric(AF))]
#    CHROM POS REF ALT     TYPE   AF
# 1:  chr1   1   A   T MISSENSE 0.23
# 2:  chr2   1   A   T MISSENSE 0.17
# 3:  chr2   1   A   G MISSENSE 0.09
 

Но у tidyr::separate нет этой проблемы:

 tidyr::separate_rows(x, ALT, AF, convert = TRUE)
# # A tibble: 3 x 6
#   CHROM   POS REF   ALT   TYPE        AF
#   <chr> <int> <chr> <chr> <chr>    <dbl>
# 1 chr1      1 A     T     MISSENSE  0.23
# 2 chr2      1 A     T     MISSENSE  0.17
# 3 chr2      1 A     G     MISSENSE  0.09
 

Вопрос: есть ли лучший способ data.table для достижения этой цели? Мне нужно использовать преобразование типов, поскольку столбец AF должен быть числовым. Я бы хотел разделить столбец с разделителями одновременно. В реальных данных может быть более 2 столбцов с разделителями.

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

1. @akrun хороший, но все же хотелось бы избежать этого «дополнительного» шага. separate_rows выглядит намного аккуратнее. Если ничего другого, это лучшее на данный момент, спасибо.

Ответ №1:

Это можно сделать проще с помощью cSplit

 library(splitstackshape)
cSplit(x, c("ALT", "AF"), ",", "long")
#   CHROM POS REF ALT     TYPE   AF
#1:  chr1   1   A   T MISSENSE 0.23
#2:  chr2   1   A   T MISSENSE 0.17
#3:  chr2   1   A   G MISSENSE 0.09
 

Что касается data.table опции, другой способ — добавить пробел

 x[, lapply(.SD, function(x) 
 trimws(unlist(tstrsplit(gsub("([TF]) ", " \1", x), ",", 
    fixed = TRUE, type.convert = TRUE)))),
    by = .(CHROM, POS, REF, TYPE)]
#   CHROM POS REF     TYPE ALT   AF
#1:  chr1   1   A MISSENSE   T 0.23
#2:  chr2   1   A MISSENSE   T 0.17
#3:  chr2   1   A MISSENSE   G 0.09
 

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

1. Еще один отличный вариант с trimws, но все же я надеялся на более прямой способ сделать это.

Ответ №2:

Поскольку проблема заключается в том, что type.convert T первая группа идентифицируется как логическая TRUE , вы могли бы явно использовать type.convert результирующую data.table where у вас также G есть в переменной, так что у вас нет этой проблемы:

 x[, lapply(.SD, function(x) unlist(str_split(x,","))),
           by = .(CHROM, POS, REF, TYPE)] %>% 
   type.convert(.,as.is = T)

   CHROM POS REF     TYPE ALT   AF
1:  chr1   1   A MISSENSE   T 0.23
2:  chr2   1   A MISSENSE   T 0.17
3:  chr2   1   A MISSENSE   G 0.09


$CHROM
[1] "character"

$POS
[1] "integer"

$REF
[1] "character"

$TYPE
[1] "character"

$ALT
[1] "character"

$AF
[1] "numeric"
 

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

1. извините, я не видел вашего комментария (его здесь больше нет?)

2. все в порядке. Я удалил комментарии. Но потом я подумал, что не стоит включать это в ответы, поскольку OP выглядел более прямым. Хорошо, что вы пришли к тому же решению. Я просто сказал. Вы можете сохранить это решение

3. Вы намного быстрее и лучше меня (и другого), приятно, что вы добровольно подумали о том, чтобы не включать все ответы в свой 🙂

4. Хорошо, что у вас есть такая опция из вашего ответа. Я думаю, что нет прямого способа, tstrsplit если мы не внесем некоторые изменения в части ‘T’, ‘F’

5. Да, это было предложено akrun уже в комментариях, я бы хотел избежать этого шага.