2

data.table と rep を使用してグループごとにデータを複製しようとすると、メモリ割り当ての問題が発生します。

サンプルデータは次のとおりです。

ob1 <- as.data.frame(cbind(c(1999),c("THE","BLACK","DOG","JUMPED","OVER","RED","FENCE"),c(4)),stringsAsFactors=FALSE)
ob2 <- as.data.frame(cbind(c(2000),c("I","WALKED","THE","BLACK","DOG"),c(3)),stringsAsFactors=FALSE)
ob3 <- as.data.frame(cbind(c(2001),c("SHE","PAINTED","THE","RED","FENCE"),c(1)),stringsAsFactors=FALSE)
ob4 <- as.data.frame(cbind(c(2002),c("THE","YELLOW","HOUSE","HAS","BLACK","DOG","AND","RED","FENCE"),c(2)),stringsAsFactors=FALSE)
sample_data <- rbind(ob1,ob2,ob3,ob4)
colnames(sample_data) <- c("yr","token","multiple")

私がやろうとしているのは、毎年の倍数でトークンを (現在の順序で) 複製することです。

次のコードが機能し、必要な答えが得られます。

good_solution1 <- ddply(sample_data, "yr", function(x) data.frame(rep(x[,2],x[1,3])))

good_solution2 <- data.table(sample_data)[, rep(token,unique(multiple)),by = "yr"]

問題は、これを 40mm 以上の行に拡大すると、考えられる両方のソリューションでメモリの問題が発生することです。

私の理解が正しければ、これらのソリューションは基本的に、毎回割り当てる rbind を実行しています。

誰かがより良い解決策を持っていますか?

data.table の set() を調べましたが、レプリケーションごとにトークンを同じ順序に保ちたかったため、問題が発生していました。

4

2 に答える 2

3

1 つの方法は次のとおりです。

require(data.table)
dt <- data.table(sample_data)
# multiple seems to be a character, convert to numeric
dt[, multiple := as.numeric(multiple)]
setkey(dt, "multiple")
dt[J(rep(unique(multiple), unique(multiple))), allow.cartesian=TRUE]

最後の行を除くすべてが単純である必要があります。最後の行では、 を使用してキー列を使用してサブセットを使用していますJ(.)。対応する値の各値J(.)は「キー列」と照合され、一致したサブセットが返されます。

つまり、dt[J(1)]そうすると、サブセットが得られますmultiple = 1。注意深く注意すると、dt[J(rep(1,2)]実行すると同じサブセットが 2 回得られます。dt[J(1,1)]passとには違いがあることに注意してくださいdt[J(rep(1,2)]。前者は (1,1) の値を data.table の最初の 2 つのキー列とそれぞれ照合しますが、後者は (1 と 2) をデータの最初のキー列と照合してサブセット化します。テーブル。

したがって、列の同じ値を で 2 回渡すと、2 回J(.)複製されます。このトリックを使用して、1 回 1 回、2 回 2 回などを渡します。それがこのrep(.)部分の機能です。rep(.)1,2,2,3,3,3,4,4,4,4 を返します。

max(nrow(dt), nrow(i))そして、結合の結果が(i は 内にある rep ベクトル)よりも多くの行になる場合、この結合を実行J(.)するために明示的に使用する必要がありますallow.cartesian = TRUE(これは data.table 1.8.8 の新機能だと思います)。


編集:これは、「比較的」ビッグデータで行ったベンチマークです。どちらの方法でも、メモリ割り当てにスパイクは見られません。しかし、R の関数内でピーク時のメモリ使用量を監視する方法をまだ見つけていません。SO でそのような投稿を見たことがあると確信していますが、現時点では滑っています。また書きます。現時点では、誰かが興味を持っている/自分で実行したい場合に備えて、テスト データといくつかの予備的な結果を示します。

# dummy data
set.seed(45)
yr <- 1900:2013
sz <- sample(10:50, length(yr), replace = TRUE)
token <- unlist(sapply(sz, function(x) do.call(paste0, data.frame(matrix(sample(letters, x*4, replace=T), ncol=4)))))
multiple <- rep(sample(500:5000, length(yr), replace=TRUE), sz)

DF <- data.frame(yr = rep(yr, sz), 
                 token = token, 
                 multiple = multiple, stringsAsFactors=FALSE)

# Arun's solution
ARUN.DT <- function(dt) {
    setkey(dt, "multiple")
    idx <- unique(dt$multiple)
    dt[J(rep(idx,idx)), allow.cartesian=TRUE]
}

# Ricardo's solution
RICARDO.DT <- function(dt) {
    setkey(dt, key="yr")
    newDT <- setkey(dt[, rep(NA, list(rows=length(token) * unique(multiple))), by=yr][, list(yr)], 'yr')
    newDT[, tokenReps := as.character(NA)]

    # Add the rep'd tokens into newDT, using recycling
    newDT[, tokenReps := dt[.(y)][, token], by=list(y=yr)]
    newDT
}

# create data.table
require(data.table)
DT <- data.table(DF)

# benchmark both versions
require(rbenchmark)
benchmark(res1 <- ARUN.DT(DT), res2 <- RICARDO.DT(DT), replications=10, order="elapsed")

#                     test replications elapsed relative user.self sys.self
# 1    res1 <- ARUN.DT(DT)           10   9.542    1.000     7.218    1.394
# 2 res2 <- RICARDO.DT(DT)           10  17.484    1.832    14.270    2.888

しかし、Ricardo が言うように、メモリが不足しても問題にならない場合があります。したがって、その場合、速度とメモリの間でトレードオフが必要になります。私が確認したいのは、ここで両方の方法で使用されるピークメモリであり、使用するJoin方が良いかどうかを明確に言います.

于 2013-03-24T01:18:26.327 に答える
1

最初にすべての行にメモリを割り当ててから、それらに繰り返しデータを入力してみてください。
例えば:

  # make sure `sample_data$multiple` is an integer
  sample_data$multiple <- as.integer(sample_data$multiple)

  # create data.table
  S <- data.table(sample_data, key='yr')

  # optionally, drop original data.frame if not needed
  rm(sample_data)

  ## Allocate the memory first
  newDT <- data.table(yr = rep(sample_data$yr, sample_data$multiple), key="yr")
  newDT[, tokenReps := as.character(NA)]

  # Add the rep'd tokens into newDT, using recycling
  newDT[, tokenReps := S[.(y)][, token], by=list(y=yr)]

2つのメモ:

(1) は現在キャラクターであるため、 (元の例では)sample_data$multiple渡されると強制されます。repその場合は、実際のデータを再確認する価値があるかもしれません。

(2)以下を使用して、年間に必要な行数を決定しました

S[, list(rows=length(token) * unique(multiple)), by=yr] 
于 2013-03-24T01:47:25.250 に答える