Конкатенация строк с использованием функции apply в R



У меня есть следующий код, цель которого-расшифровать последовательность в кортежах из трех человек. Он выполняется правильно, но особенно медленно, когда применяется к очень большим наборам данных (т. е. миллионам строк).



Я подозреваю, что виновник - это "for - loops" через вектор (в частности, для y: цикла), и чувствую, что должен быть более эффективный метод, использующий одну из функций apply-к сожалению, я не слишком знаком с этим подходом и хотел бы запросить некоторые из них. помощь (пожалуйста!).



M.Order <- function(in.vector) {
return.str <- vector()
in.vector <- strsplit(in.vector, ' > ', fixed = T)
for (x in 1:length(in.vector)) {
output <- NULL
if(length(in.vector[[x]]) == 1) {
output <- paste0(in.vector[[x]], '|NULL|NULL')
} else if(length(in.vector[[x]]) == 2) {
output <- paste(c(in.vector[[x]][1], in.vector[[x]][2],'NULL'), collapse='|')
} else if(length(in.vector[[x]]) == 3) {
output <- paste(in.vector[[x]], collapse = '|')
} else for (y in 1:(length(in.vector[[x]])-2)) {
output <- ifelse(length(output) == 0
,paste(in.vector[[x]][y:(y+2)], collapse = '|')
,paste0(output, ' > ', paste(in.vector[[x]][y:(y+2)], collapse = '|'))
)
}
return.str[x] <- output
}
return (return.str)
}

orig.str <- rbind.data.frame(
'A > B > C > B > B > A > B > A > C',
'A > B',
'A > C > B',
'A',
'A > B > D > C')

colnames(orig.str) <- 'Original'
orig.str$Processed <- M.Order(as.character(orig.str$Original))
orig.str


Который возвращает (правильно)



                           Original                                             Processed
1 A > B > C > B > B > A > B > A > C A|B|C > B|C|B > C|B|B > B|B|A > B|A|B > A|B|A > B|A|C
2 A > B A|B|NULL
3 A > C > B A|C|B
4 A A|NULL|NULL
5 A > B > D > C A|B|D > B|D|C
676   3  

3 ответов:

EDIT: удалите функцию rollapply, так как она медленная, и создайте мой своя функция. Время выполнения на 327 680 строках:

  • мой код: 5.62 секунды
  • ваш код: 5,66 секунды.

Так что никакой существенной разницы.

Сначала разделите строки на символ ' > ' и добавьте нули к вектору, если он не содержит по крайней мере трех элементов. Затем используйте rollapply для объединения групп из трех символов, разделенных символом"|", и в конце сверните эти группы.

# sample data
df  = data.frame(Original=c("A > B > C > B > B > A > B > A > C","A > B","A > C > B","A","A > B > D > C"),stringsAsFactors = FALSE)
for(i in 1:16) df=rbind(df,df)

groups <- function(x)
{
  result <- vector("character", length(x)-2)
  for(k in 1:(length(x)-2) )
  {
    result[k] = paste(x[k:(k+2)],collapse="|")
  }
  return(paste(result,collapse=" > "))
}

array1 = lapply(strsplit(df$Original," > "), function(x) if (length(x) == 1) {c(x[1],"NULL","NULL")} else {if (length(x) == 2) {c(x[1:2],"NULL")} else {x}})
df$modified =  lapply(array1,groups)

Вывод: (Как список для удобочитаемости)

[[1]]
[1] "A|B|C > B|C|B > C|B|B > B|B|A > B|A|B > A|B|A > B|A|C"

[[2]]
[1] "A|B|NULL"

[[3]]
[1] "A|C|B"

[[4]]
[1] "A|NULL|NULL"

[[5]]
[1] "A|B|D > B|D|C"

Надеюсь, это поможет!

Фундаментальная логика, по-видимому, описывается следующим правилом:

  1. разделить строки на >
  2. Для каждой строки, начиная с каждой позиции, объедините следующие 3 символа с помощью '|'.
  3. объединить все полученные кортежи с пробелами.
Шаг 2 является наиболее сложным. Она может быть решена с помощью следующей обобщенной функции:
merge_tuples = function (str, len, sep) {
    start_positions = seq_len(max(length(str) - len + 1, 1))
    tuple_indices = lapply(start_positions, seq, length.out = len)
    lapply(tuple_indices, function (i) paste(str[i], collapse = sep))
}

Это было обобщено для работы с любым размером (не только 3) и каждым разделителем (не только '|').

Пример:

> merge_tuples(c('A', 'B', 'C'), 2, ':')
[[1]]
[1] "A:B"

[[2]]
[1] "B:C"

С этим на месте, res легко решается:

orig = c('A > B > C > B > B > A > B > A > C',
         'A > B',
         'A > C > B',
         'A',
         'A > B > D > C')

tuples = lapply(strsplit(orig, ' > '), merge_tuples, len = 3, sep = '|')
merged = sapply(tuples, paste, collapse = ' ')

Это выведет NA вместо NULL (как в вашем коде) в местах, где недостаточно элементов. Я предполагаю, что это не имеет большого значения. Если это так, замените вхождения на gsub.

Частичное решение...

Следующая функция преобразует одну строку:

makes = function (S) 
{
    L = strsplit(gsub(" > ", "", S), "")[[1]]
    m = outer(1:3, 0:(length(L) - 3), "+")
    m[] = L[m]
    paste(apply(m, 2, function(x) {
        paste0(x, collapse = "|")
    }), collapse = " > ")
}

Он работает с помощью outer, чтобы сделать матрицу смещений, а затем использовать ее, чтобы получить элементы из строки, как только строка была очищена только на буквы и разбита на вектор. Тогда это просто случай склеивания всего этого вместе:

> makes(orig.str$Original[1])
[1] "A|B|C > B|C|B > C|B|B > B|B|A > B|A|B > A|B|A > B|A|C"

Он делает хэш из тех, которые короче 3, хотя:

> makes(orig.str$Original[2])
[1] "A|B|NA > A|B|A"
Warning message:
In m[] = L[m] :
  number of items to replace is not a multiple of replacement length
> makes(orig.str$Original[3])
[1] "A|C|B"
> makes(orig.str$Original[4])
Error in L[m] : only 0's may be mixed with negative subscripts
> makes(orig.str$Original[5])
[1] "A|B|D > B|D|C"

Возможно, стоит явным образом обнаружить эти крайние случаи (length(L) < 3 в коде должно это делать) и обработка их отдельно.

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

Comments

    Ничего не найдено.