3

誰かが次のコードと最小限の例を見て、特に非常に大きなデータセットを扱うときのコードの効率に関して改善を提案できるかどうか疑問に思います。

この関数は data.frame を受け取り、それをグループ化変数 (係数) で分割してから、各グループのすべての行の距離行列を計算します。

距離行列を保持する必要はありません-いくつかの統計、つまり平均、ヒストグラムのみ..、それらは破棄できます。

私はメモリ割り当てなどについてあまり知りません。グループごとに 10.000 ~ 100.000 のケースで作業するので、これを行うための最良の方法は何か疑問に思っています。どんな考えでも大歓迎です!

また、重大なメモリの問題が発生した場合のように、ビッグメモリまたはその他の大規模なデータ処理パッケージを関数に含める最も簡単な方法は何ですか?

FactorDistances <- function(df) {
  # df is the data frame where the first column is the grouping variable. 
  # find names and number of groups in df (in the example there are three:(2,3,4)
  factor.names <- unique(df[1])
  n.factors <-length(unique(df$factor))
  # split df by factor into list - each subset dataframe is one list element
  df.l<-list()
  for (f in 1:n.factors) {df.l[[f]]<-df[which(df$factor==factor.names[f,]),]}
  # use lapply to go through list and calculate distance matrix for each group
  # this results in a new list where each element is a distance matrix
  distances <- lapply (df.l, function(x) dist(x[,2:length(x)], method="minkowski", p=2))  
  # again use lapply to get the mean distance for each group
  means <- lapply (distances,  mean)  
  rm(distances)
  gc()
  return(means)
}

df <- data.frame(cbind(factor=rep(2:4,2:4), rnorm(9), rnorm(9)))
FactorDistances(df)
# The result are three average euclidean distances between all pairs in each group
# If a group has only one member, the value is NaN

編集:回答として投稿したチャンクの問題を反映するようにタイトルを編集しました..

4

2 に答える 2

5

dist() が処理できない非常に大きな行列のチャンキング ソリューションを思いついたので、他の誰かが役立つと思う場合に備えてここに投稿します (または、障害を見つけてください!)。dist() よりもかなり遅いですが、dist() がエラーをスローした場合にのみ使用する必要があるため、これは無関係です。通常は次のいずれかです。

"Error in double(N * (N - 1)/2) : vector size specified is too large" 
"Error: cannot allocate vector of size 6.0 Gb"
"Error: negative length vectors are not allowed"

関数は行列の平均距離を計算しますが、それを他のものに変更することもできますが、実際に行列を保存したい場合は、ファイルにバックアップされたビッグメモリ行列のようなものが適切であると思います..アイデアとアリにリンクする称賛彼の助けのために!

FunDistanceMatrixChunking <- function (df, blockSize=100){
  n <- nrow(df)
  blocks <- n %/% blockSize
  if((n %% blockSize) > 0)blocks <- blocks + 1
  chunk.means <- matrix(NA, nrow=blocks*(blocks+1)/2, ncol= 2)
  dex <- 1:blockSize
  chunk <- 0
  for(i in 1:blocks){    
    p <- dex + (i-1)*blockSize
    lex <- (blockSize+1):(2*blockSize)
    lex <- lex[p<= n]
    p <- p[p<= n]
    for(j in 1:blocks){
      q <- dex +(j-1)*blockSize
      q <- q[q<=n]     
      if (i == j) {       
        chunk <- chunk+1
        x <- dist(df[p,])
        chunk.means[chunk,] <- c(length(x), mean(x))}
      if ( i > j) {
        chunk <- chunk+1
        x <- as.matrix(dist(df[c(q,p),]))[lex,dex] 
        chunk.means[chunk,] <- c(length(x), mean(x))}
    }
  }
  mean <- weighted.mean(chunk.means[,2], chunk.means[,1])
  return(mean)
}
df <- cbind(var1=rnorm(1000), var2=rnorm(1000))
mean(dist(df))
FunDistanceMatrixChunking(df, blockSize=100)

これを回答ではなく編集として投稿する必要があるかどうかはわかりません..実際にはこのように指定しませんでしたが、問題は解決します..

于 2012-11-22T00:43:10.660 に答える
2

いくつかの考え:

  • unique(df[1])おそらく機能しますが (リストの data.frame プロパティを無視することで)、緊張して読みにくくなります。unique(df[,1])のほうがいい。
  • for (f in 1:n.factors) {df.l[[f]]<-df[which(df$factor==factor.names[f,]),]}で行うことができますsplit
  • メモリが心配な場合は、すべてのレベルの距離行列全体を絶対に保存しないでください。次に、すべての因子レベルの要約統計量を計算してください! ラップリーを次のように変更しますlapply (df.l, function(x) mean(dist(x[,2:length(x)], method="minkowski", p=2)))

複数の要約統計量が必要な場合は、両方を計算してリストを返します。

lapply (df.l, function(x) {
   dmat <- dist(x[,2:length(x)], method="minkowski", p=2)
   list( mean=mean(dmat), median=median(dmat) )
})

それがあなたをどこにでも連れて行くかどうか見てください。そうでない場合は、より専門化する必要があるかもしれません (を避けてlapply、代わりに data.frames を行列として保存するなど)。

于 2012-11-19T21:13:58.890 に答える