2017-02-22 23 views
0

У меня есть список строк (очень больших, миллионов строк), из которых я хочу извлечь определенные части.R: Извлечь часть строки с переменной длиной

Сначала я разделил строку на точку с запятой, а затем извлек ее в определенные разделы. Это сделано немного сложнее, поскольку иногда бывает 3, иногда 4 сегмента в одной строке. Но части, которые меня интересуют, - это всегда последний и второй-последний сегмент.

Пример кода:

dataStr = c("secAlways; secExtr1; secExtr2", 
      "secSometimes; secAlways; secExtr1; secExtr2", 
      "secSometimes; secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secSometimes; secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2") 

splStr <- strsplit(dataStr, ";") 
extr1 <- list() 
extr2 <- list() 

for (i in 1:length(splStr)) { 
    extr1[i] <- head(tail(splStr[[i]], n=2), n=1) 
    extr2[i] <- tail(splStr[[i]], n = 1) 
} 

Это работает, но это слишком медленно. Я был бы признателен за любые идеи о том, как сделать это быстрее. Я подозреваю, что это может быть сделано с apply, но я не мог обвести вокруг него голову.


Этот вопрос был поднят, если это может быть дубликатом вопрос this вопрос. Я думаю, что это немного по-другому, поскольку я хочу извлечь последние два элемента и количество разделов отличается. Кроме того, у меня нет решения с vapply, и я уже начал работать над своим образцом реального мира.

+0

Возможный дубликат [Как получить последний подэлемент каждого элемента списка в R] (http://stackoverflow.com/questions/36143119/how-to-get-last-subelement-of-every -element-of-a-list-in-r) –

ответ

3

Я думаю, что вам лучше только с помощью регулярных выражений здесь:

sub(".+; (.+?); (.+?)$", "\\2", dataStr) 

Это будет захватить последний пункт.

sub(".+; (.+?); (.+?)$", "\\1", dataStr) 

Это будет захватывать предмет до последнего элемента.

+0

Улучшена скорость в 27 раз по моему образцу реального мира. –

0

Это может быть быстрее сделать:

str_list <- lapply(splStr, tail, 2) 
do.call(rbind, str_list) 

     [,1]   [,2]  
[1,] " secExtr1" " secExtr2" 
[2,] " secExtr1" " secExtr2" 
[3,] " secExtr1" " secExtr2" 
[4,] " secExtr1" " secExtr2" 
[5,] " secExtr1" " secExtr2" 
[6,] " secExtr1" " secExtr2" 
[7,] " secExtr1" " secExtr2" 
[8,] " secExtr1" " secExtr2" 
[9,] " secExtr1" " secExtr2" 
[10,] " secExtr1" " secExtr2" 
+0

Ну, это то же самое. splStr - это split dataStr. – JohannesNE

+0

Метод regex на самом деле быстрее (по крайней мере, в этом примере набора). – JohannesNE

1

Мы можем использовать stringi, чтобы сделать это быстрее вместе с vapply

library(stringi) 
vapply(stri_split(dataStr, regex=';\\s*'), function(x) tail(x, 2), character(2)) 
+0

Это работает на примере здесь, но неудачно на моем примере с реальным миром: 'Ошибка в vapply (splStr, function (x) tail (x, 2), character (2)): значения должны быть длиной 2, , но FUN (X [[5487]]) результат - длина 1' –

+0

@ ulima2_ Неясно, когда у вас менее 2 случаев, что делать – akrun

2

word из stringr раствора,

stringr::word(dataStr, -2, -1, sep = ';') 

Вы может затем strsplit, чтобы получить их в 2-х разных слов, т.е.

do.call(rbind, strsplit(trimws(word(dataStr, -2, -1, sep = ';')), '; ')) 
#  [,1]  [,2]  
# [1,] "secExtr1" "secExtr2" 
# [2,] "secExtr1" "secExtr2" 
# [3,] "secExtr1" "secExtr2" 
# [4,] "secExtr1" "secExtr2" 
# [5,] "secExtr1" "secExtr2" 
# [6,] "secExtr1" "secExtr2" 
# [7,] "secExtr1" "secExtr2" 
# [8,] "secExtr1" "secExtr2" 
# [9,] "secExtr1" "secExtr2" 
#[10,] "secExtr1" "secExtr2" 
+0

Я думаю, что это не делает работу: обе секции все еще сосредоточены вместе в такой же клетка. –

+0

Отредактировано. Я также использовал 'trimws' для удаления ведущих/конечных пробелов – Sotos

+0

Отлично, теперь работает - спасибо! Однако регулярное выражение значительно быстрее. –

0
> str_list <- lapply(dataStr, tail, 2) 

> do.call(rbind, str_list) 


     [,1]           
[1,] "secAlways; secExtr1; secExtr2"    
[2,] "secSometimes; secAlways; secExtr1; secExtr2" 
[3,] "secSometimes; secAlways; secExtr1; secExtr2" 
[4,] "secAlways; secExtr1; secExtr2"    
[5,] "secAlways; secExtr1; secExtr2"    
[6,] "secAlways; secExtr1; secExtr2"    
[7,] "secSometimes; secAlways; secExtr1; secExtr2" 
[8,] "secAlways; secExtr1; secExtr2"    
[9,] "secAlways; secExtr1; secExtr2"    
[10,] "secAlways; secExtr1; secExtr2" 

Я не уверен, что это работает?

0

Предполагая, что последний и второй последние сегменты всегда имеют одинаковое количество символов, это может быть достигнуто следующим образом с помощью библиотеки stringi.

Я также предположил, что вы хотели бы получить два списка в качестве вывода.

library(stringi) 

dataStr = c("secAlways; secExtr1; secExtr2", 
      "secSometimes; secAlways; secExtr1; secExtr2", 
      "secSometimes; secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secSometimes; secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2", 
      "secAlways; secExtr1; secExtr2") 

extr1 <- as.list(stringi::stri_sub(dataStr, from=-18, to=-11)) 
extr2 <- as.list(stringi::stri_sub(dataStr, from= -8)) 
+0

К сожалению, для меня это не то же самое количество дел, извините за вводящий в заблуждение пример. –