2013-10-24 1 views
2

Я написал некоторый код R для анализа строк, подсчета вхождения подстрок и заполнения таблицы подстрок. Он отлично работает, но на реальных данных, которые я использую (это довольно большой), действительно очень медленно, и я знаю много, потому что я использую циклы, а не функции из семейства приложений. Я пытался получить этот код в функциональную форму, и мне не повезло, может ли кто-нибудь помочь? Моя самая большая проблема заключается в том, что я не могу определить способ использования имен столбцов для соответствия значениям в конструкции apply. Вот код с некоторыми данными о игрушке:Преобразование R-циклов в функциональную форму с применением

#Create toy data, list of unique substrings 
code_frame<-matrix(c(c('a|a|b|c|d'),c('a|b|b|c|c'),c('a|b|c|d|d')),nrow=3,ncol=1) 
all_codes_list<-c('a','b','c','d') 

#create data frame with a column for each code and a row for each job 
code_count<-as.data.frame(matrix(0, ncol = length(all_codes_list), nrow = nrow(code_frame))) 
colnames(code_count)<-all_codes_list 

#fill in the code_count data frame with entries where codes occur 
for(i in 1:nrow(code_frame)){ 
    test_string<-strsplit(code_frame[i,1],split="|",fixed=TRUE)[[1]] 
    for(j in test_string){ 
     for(g in 1:ncol(code_count)){ 
      if(j == all_codes_list[g]){ 
       code_count[i,g]<-code_count[i,g]+1 
       } 
      } 
     } 
    } 

Спасибо.

+0

Действительно ли это выглядит как code_frame? Как вы ответите, будет зависеть от совета, который я даю. –

+0

Да, в сущности. Это длинная строка с длинными строками, которые мне нужно разбить на подстроки. Это не то, как он создан (он уже сформирован как длинный канал delim str из базы данных, из которого я его вытаскиваю), но форма идентична. – TomR

ответ

5

Oneliner, разделить на 3 линии:

do.call(rbind, 
     lapply(strsplit(code_frame[,1], "|", fixed=TRUE), 
       function(x) table(factor(x, levels=all_codes_list)))) 

Обратите внимание, что strsplit является vectorised, так что вам не нужен внешний цикл по всем строкам. Ваши внутренние петли в основном подсчитывают появление каждого кода, который представляет собой приложение table. Наконец, do.call(rbind, *) является стандартной идиомой для превращения списка строк в один кадр данных.

+0

Это очень здорово. Спасибо за все ответы, и я обязательно запомню функцию «table». – TomR

4

qdap package есть инструмент, который идеально подходит для этого и должны быть очень быстро и немного кодирования, называется mtabulate:

library(qdap)  
mtabulate(strsplit(code_frame, "\\|")) 

## a b c d 
## 1 2 1 1 1 
## 2 1 2 2 0 
## 3 1 1 1 2 

В основном он принимает списки векторов (выход из strsplit) и делает ряд табулиро- информации для каждого вектора.

EDIT: Если скорость действительно ваша вещь здесь ориентиры на 1000 репликаций (microbenchmark package на Win 7 машин):

Unit: microseconds 
    expr  min  lq median  uq  max neval 
    HONG() 592.458 620.448 632.111 644.706 4650.560 1000 
    TYLER() 324.220 342.413 351.743 361.073 3556.613 1000 
HENRIK() 1527.329 1560.450 1578.177 1614.331 4828.297 1000 

И визуальный выход: enter image description here

+0

Я дам ему вихрь. – TomR

+1

Спасибо за вашу функцию «mtabulate» и тайминги, +1! – Henrik

2

base альтернатива:

df <- read.table(text = code_frame, sep = "|") 

tt <- apply(df, 1, function(x){ 
    x2 <- factor(x, levels = letters[1:4]) 
    table(x2) 
    }) 

t(tt) 

#  a b c d 
# [1,] 2 1 1 1 
# [2,] 1 2 2 0 
# [3,] 1 1 1 2 
Смежные вопросы