2013-05-20 16 views
6

ファクタ名をデリミタで分割して3つのファクタ列に分割する必要があるファクタ列を持つ大きなデータフレームがあります。ここでは、大規模なデータフレームと非常に遅い私の現在のアプローチ、(時には数百万行)です:出力が可能なときに `strsplit`を高速化します

data <- readRDS("data.rds") 
data.df <- reshape2:::melt.array(data) 
head(data.df) 
## Time Location Class Replicate Population 
##1 1  1 LIDE.1.S   1 0.03859605 
##2 2  1 LIDE.1.S   1 0.03852957 
##3 3  1 LIDE.1.S   1 0.03846853 
##4 4  1 LIDE.1.S   1 0.03841260 
##5 5  1 LIDE.1.S   1 0.03836147 
##6 6  1 LIDE.1.S   1 0.03831485 

Rprof("str.out") 
cl <- which(names(data.df)=="Class") 
Classes <- do.call(rbind, strsplit(as.character(data.df$Class), "\\.")) 
colnames(Classes) <- c("Species", "SizeClass", "Infected") 
data.df <- cbind(data.df[,1:(cl-1)],Classes,data.df[(cl+1):(ncol(data.df))]) 
Rprof(NULL) 

head(data.df) 
## Time Location Species SizeClass Infected Replicate Population 
##1 1  1 LIDE   1  S   1 0.03859605 
##2 2  1 LIDE   1  S   1 0.03852957 
##3 3  1 LIDE   1  S   1 0.03846853 
##4 4  1 LIDE   1  S   1 0.03841260 
##5 5  1 LIDE   1  S   1 0.03836147 
##6 6  1 LIDE   1  S   1 0.03831485 

summaryRprof("str.out") 

$by.self 
       self.time self.pct total.time total.pct 
"strsplit"   1.34 50.00  1.34  50.00 
"<Anonymous>"   1.16 43.28  1.16  43.28 
"do.call"    0.04  1.49  2.54  94.78 
"unique.default"  0.04  1.49  0.04  1.49 
"data.frame"   0.02  0.75  0.12  4.48 
"is.factor"   0.02  0.75  0.02  0.75 
"match"    0.02  0.75  0.02  0.75 
"structure"   0.02  0.75  0.02  0.75 
"unlist"    0.02  0.75  0.02  0.75 

$by.total 
         total.time total.pct self.time self.pct 
"do.call"     2.54  94.78  0.04  1.49 
"strsplit"     1.34  50.00  1.34 50.00 
"<Anonymous>"    1.16  43.28  1.16 43.28 
"cbind"      0.14  5.22  0.00  0.00 
"data.frame"     0.12  4.48  0.02  0.75 
"as.data.frame.matrix"  0.08  2.99  0.00  0.00 
"as.data.frame"    0.08  2.99  0.00  0.00 
"as.factor"     0.08  2.99  0.00  0.00 
"factor"      0.06  2.24  0.00  0.00 
"unique.default"    0.04  1.49  0.04  1.49 
"unique"      0.04  1.49  0.00  0.00 
"is.factor"     0.02  0.75  0.02  0.75 
"match"      0.02  0.75  0.02  0.75 
"structure"     0.02  0.75  0.02  0.75 
"unlist"      0.02  0.75  0.02  0.75 
"[.data.frame"    0.02  0.75  0.00  0.00 
"["       0.02  0.75  0.00  0.00 

$sample.interval 
[1] 0.02 

$sampling.time 
[1] 2.68 

は、この操作をスピードアップする方法はありますか?私は、 "Species"、 "SizeClass"、 "Infected"というカテゴリのそれぞれに小さい(< 5)個の番号があり、これらがあらかじめわかっています。

  • stringr::str_split_fixed行い、このタスクではなく、任意の速い
  • データフレームが実際に最初Class及びその関連レベルがディメンションされたアレイ上reshape::meltを呼び出すことによって生成されます。そこからここに至るより速い方法があれば、素晴らしい。 http://dl.getdropbox.com/u/3356641/data.rds

答えて

5

  • data.rdsこれはおそらくかなりの増加を提供する必要があります:

    library(data.table) 
    DT <- data.table(data.df) 
    
    
    DT[, c("Species", "SizeClass", "Infected") 
         := as.list(strsplit(Class, "\\.")[[1]]), by=Class ] 
    

    増加の理由:

    1. data.table前に列
    2. にメモリを割り当て
    3. data.frameのすべての列割り当てがデータ全体を再割り当てします(対照ではdata.table)
    4. byステートメントでは、一意の値ごとにstrsplitタスクを1回実装できます。ここで

    全体のプロセスのための素晴らしい迅速な方法です。

    # Save the new col names as a character vector 
    newCols <- c("Species", "SizeClass", "Infected") 
    
    # split the string, then convert the new cols to columns 
    DT[, c(newCols) := as.list(strsplit(as.character(Class), "\\.")[[1]]), by=Class ] 
    DT[, c(newCols) := lapply(.SD, factor), .SDcols=newCols] 
    
    # remove the old column. This is instantaneous. 
    DT[, Class := NULL] 
    
    ## Have a look: 
    DT[, lapply(.SD, class)] 
    #  Time Location Replicate Population Species SizeClass Infected 
    # 1: integer integer integer numeric factor factor factor 
    
    DT 
    
  • +0

    これは高速です!あなたは 'as.character(Class)'を使うことにしましたが。同じコマンドで列を要素として返すことはできますか? –

    +0

    factorに変換することはできますが、後に2行目として変換します。 'by'引数を含む同じ呼び出しでas.factorを使用すると、プロセスが遅くなることがあります。 –

    +0

    @NoamRoss、 'as.character'をうまくキャッチします。更新されたコードといくつかの追加ステップ –

    3

    あなただけgsubを使用しての代わりに、すべてのものを分割し、一緒に戻ってそれを置くしようとしているあなたが必要とする文字列の部分を抽出して高速でまともな増加を得ることができます:

    data <- readRDS("~/Downloads/data.rds") 
    data.df <- reshape2:::melt.array(data) 
    
    # using `strsplit` 
    system.time({ 
    cl <- which(names(data.df)=="Class") 
    Classes <- do.call(rbind, strsplit(as.character(data.df$Class), "\\.")) 
    colnames(Classes) <- c("Species", "SizeClass", "Infected") 
    data.df <- cbind(data.df[,1:(cl-1)],Classes,data.df[(cl+1):(ncol(data.df))]) 
    }) 
    
    user system elapsed 
    3.349 0.062 3.411 
    
    #using `gsub` 
    system.time({ 
    data.df$Class <- as.character(data.df$Class) 
    data.df$SizeClass <- gsub("(\\w+)\\.(\\d+)\\.(\\w+)", "\\2", data.df$Class, 
        perl = TRUE) 
    data.df$Infected <- gsub("(\\w+)\\.(\\d+)\\.(\\w+)", "\\3", data.df$Class, 
        perl = TRUE) 
    data.df$Class <- gsub("(\\w+)\\.(\\d+)\\.(\\w+)", "\\1", data.df$Class, 
        perl = TRUE) 
    }) 
    
    user system elapsed 
    0.812 0.037 0.848 
    
    +0

    +1非常に良い方法です! –

    2

    はあなたのように見えます要素を持っているので、レベルで作業してから地図に戻します。 にfixed=TRUEを使用し、split="."に調整してください。

    Classes <- do.call(rbind, strsplit(levels(data.df$Class), ".", fixed=TRUE)) 
    colnames(Classes) <- c("Species", "SizeClass", "Infected") 
    df0 <- as.data.frame(Classes[data.df$Class,], row.names=NA) 
    cbind(data.df, df0) 
    
    +1

    実際にはすばらしい答えです。シンプルで、新しい依存関係が追加されていません。しかし、大きなdata.frameでは結合が遅くなる可能性があるので、Ricardoの 'data.frame'構造体は私にとってはより良い解決策です。 –

    +0

    おっと、 'data.table'構造を意味していましたが、この後にコメントを編集できないようです。 –

    +0

    この提案は、私のコードを何倍もスピードアップしました!ありがとう! – CephBirk

    関連する問題