6

次のデータセットがありますが、これをワイド フォーマットからロング フォーマットに変更したいと考えています。

Name     Code  CURRENCY   01/01/1980   02/01/1980   03/01/1980   04/01/1980
Abengoa  4256  USD        1.53         1.54         1.51         1.52      
Adidas   6783  USD        0.23         0.54         0.61         0.62      

データは、1980 年から 2013 年までの各日のさまざまな企業の株価で構成されています。したがって、ワイド データには 8,612 列 (および約 3,000 行) があります。現在、次のコマンドを使用して、データを長い形式に再形成しています。

library(reshape)
data <- read.csv("data.csv")
data1 <- melt(data,id=c("Name","Code", "CURRENCY"),variable_name="Date")

ただし、約 50MB の .csv ファイルの場合、既に約 2 時間かかります。16 GB の RAM を搭載した 2.7 GHz Intel Core i7 でこれを実行しているため、計算時間は弱いハードウェアによって駆動されるべきではありません。これを行うための他のより効率的な方法はありますか?

どうもありがとう!

4

3 に答える 3

5

質問から:

data <- read.csv("data.csv")

... サイズが約 50MB の .csv ファイルの場合、既に約 2 時間かかります ...

したがって、スタック/メルト/リシェイプが機能しますが、ここでの最大の要因はread.csv. それをタイミングに含めていると仮定しますmelt(明確ではありません)。

へのデフォルトの引数read.csvは遅いことがよく知られています。stringsAsFactorsいくつかの簡単な検索で、次のようなヒントやヒント (例colClasses: ) が表示されます。

しかし、私はお勧めしますfreaddata.table1.8.7以降)。fread生のテキスト形式のマニュアル ページ の感触をつかむには、https ://www.rdocumentation.org/packages/data.table/versions/1.12.2/topics/fread をご覧ください。

そこの例のセクションには、たまたま、最大60秒ではなく3秒で読み取られることが示されている50MBの例があります。また、ベンチマークは他の回答に表示され始めています。

私が正しく推測した場合、スタック/リシェイプ/メルトの答えは次の順序です。

于 2013-03-03T22:36:48.187 に答える
5

ベンチマークの概要:

Stack(@AnandaMahto で提案されているように) を使用することは、間違いなく小さな
データ セット (N < 3,000) の場合の方法です。
データセットが大きくなるにつれて、data.tableパフォーマンスが向上し始めますstack


これはdata.tableを使用したオプションです

dtt <- data.table(data)

# non value columns, ie, the columns to keep post reshape
nvc <- c("Name","Code", "CURRENCY")

# name of columns being transformed 
dateCols <- setdiff(names(data), nvc)

# use rbind list to combine subsets
dtt2 <- rbindlist(lapply(dateCols, function(d) {
    dtt[, Date := d]
    cols <- c(nvc, "Date", d)
    setnames(dtt[, cols, with=FALSE], cols, c(nvc, "Date", "value"))
}))

## Results: 

dtt2
#       Name Code CURRENCY         Date value
# 1: Abengoa 4256      USD X_01_01_1980  1.53
# 2:  Adidas 6783      USD X_01_01_1980  0.23
# 3: Abengoa 4256      USD X_02_01_1980  1.54
# 4:  Adidas 6783      USD X_02_01_1980  0.54
# 5: ... <cropped>

より大きなサンプルデータでベンチマークを更新

@AnandaMahto からの提案によると、以下は大規模な (より大きな) サンプル データを使用したベンチマークです。 以下で使用されているメソッドを自由に改善したり、新しいメソッドを追加したりしてください。

ベンチマーク

 Resh <- quote(reshape::melt(data,id=c("Name","Code", "CURRENCY"),variable_name="Date"))
 Resh2 <- quote(reshape2::melt(data,id=c("Name","Code", "CURRENCY"),variable_name="Date"))
 DT <- quote({    nvc <- c("Name","Code", "CURRENCY"); dateCols <- setdiff(names(data), nvc); rbindlist(lapply(dateCols, function(d) { dtt[, Date := d]; cols <- c(nvc, "Date", d); setnames(dtt[, cols, with=FALSE], cols, c(nvc, "Date", "value"))}))})
 Stack <- quote(data.frame(data[1:3], stack(data[-c(1, 2, 3)])))


 # SAMPLE SIZE: ROWS = 900; COLS = 380 + 3; 
 dtt <- data.table(data);  
 benchmark(Resh=eval(Resh),Resh2=eval(Resh2),DT=eval(DT), Stack=eval(Stack), replications=5, columns=c("relative", "test", "elapsed", "user.self", "sys.self", "replications"), order="relative")
 # relative  test elapsed user.self sys.self replications
 #    1.000 Stack   0.813     0.623    0.192            5
 #    2.530    DT   2.057     2.035    0.026            5
 #   40.470  Resh  32.902    18.410   14.602            5
 #   40.578 Resh2  32.990    18.419   14.728            5

 # SAMPLE SIZE: ROWS = 3,500; COLS = 380 + 3; 
 dtt <- data.table(data);  
 benchmark(DT=eval(DT), Stack=eval(Stack), replications=5, columns=c("relative", "test", "elapsed", "user.self", "sys.self", "replications"), order="relative")
 #  relative  test elapsed user.self sys.self replications
 #      1.00    DT   2.407     2.336    0.076            5
 #      1.08 Stack   2.600     1.626    0.983            5

 # SAMPLE SIZE: ROWS = 27,000; COLS = 380 + 3; 
 dtt <- data.table(data);  
 benchmark(DT=eval(DT), Stack=eval(Stack), replications=5, columns=c("relative", "test", "elapsed", "user.self", "sys.self", "replications"), order="relative")
 # relative  test elapsed user.self sys.self replications
 #    1.000    DT  10.450     7.418    3.058            5
 #    2.232 Stack  23.329    14.180    9.266            5

サンプルデータ作成

  # rm(list=ls(all=TRUE))
  set.seed(1)
  LLLL <- apply(expand.grid(LETTERS, LETTERS[10:15], LETTERS[1:20], LETTERS[1:5], stringsAsFactors=FALSE), 1, paste0, collapse="")

  size <- 900
  dateSamples <- 380
  startDate <- as.Date("1980-01-01")

  Name <- apply(matrix(LLLL[1:(2*size)], ncol=2), 1, paste0, collapse="")
  Code <- sample(1e3:max(1e4-1, size+1e3), length(Name))
  CURRENCY <- sample(c("USD", "EUR", "YEN"), length(Name), TRUE)

  Dates <- seq(startDate, length.out=dateSamples, by="mon")
  Values <- sample(c(1:1e2, 1:5e2), size=size*dateSamples, TRUE) / 1e2

  # Calling the sample dataframe `data` to keep consistency, but I dont like this practice
  data <- data.frame(Name, Code, CURRENCY,       
                     matrix(Values, ncol=length(Dates), dimnames=list(c(), as.character(Dates)))
                    ) 

  data[1:6, 1:8]
  #        Name Code CURRENCY X1980.01.01 X1980.02.01 X1980.03.01 X1980.04.01 X1980.05.01
  # 1  AJAAQNFA 3389      YEN        0.37        0.33        3.58        4.33        1.06
  # 2  BJAARNFA 4348      YEN        1.14        2.69        2.57        0.27        3.02
  # 3  CJAASNFA 6154      USD        2.47        3.72        3.32        0.36        4.85
  # 4  DJAATNFA 9171      USD        2.22        2.48        0.71        0.79        2.85
  # 5  EJAAUNFA 2814      USD        2.63        2.17        1.66        0.55        3.12
  # 6  FJAAVNFA 9081      USD        1.92        1.47        3.51        3.23        3.68
于 2013-03-03T16:34:44.847 に答える
3

テストが進行している間、私はあなたが考慮すべき答えとして私のコメントを投稿します. 次のように使用stackしてみてください:

data1 <- data.frame(data[1:3], stack(data[-c(1, 2, 3)]))

多くの場合、stackはこれらのタイプの操作で非常に効率的に機能し、R でベクトルが再利用される方法により、最初の数列に足し戻すことも迅速に機能します。

さらに言えば、これも検討する価値があるかもしれません:

data.frame(data[1:3],
           vals = as.vector(as.matrix(data[-c(1, 2, 3)])),
           date = rep(names(data)[-c(1, 2, 3)], each = nrow(data)))

ただし、結果が実際のデータセットでのベンチマークに匹敵するとは思えないため、このような小さなデータのサンプルでベンチマークを行うことには慎重です。


更新: いくつかのベンチマークの結果

@RicardoSaporta のベンチマーク手順を使用して、私が「手動」作成data.tableと呼んでいるものに対してベンチマークしました。data.frameここでは、1000 行から 3000 行の範囲のデータセットで、500 行ずつ増加し、すべて 8003 列 (8000 データ列と最初の 3 列) のベンチマークの結果を確認できます。

結果はここで見ることができます: http://rpubs.com/mrdwab/reduce-computing-time

Ricardo の正解です。約 3000 行で、ベース R のアプローチと大きな違いがあるようです (それが何であるかについて誰かが説明を持っていると興味深いでしょう)。しかし、この「手動」アプローチはstack、パフォーマンスが本当に主要な関心事である場合、実際には よりも高速です。

過去 3 回の実行の結果は次のとおりです。

data <- makeSomeData(2000, 8000)
dtt <- data.table(data)
suppressWarnings(benchmark(DT = eval(DT), Manual = eval(Manual), replications = 1, 
    columns = c("relative", "test", "elapsed", "user.self", "sys.self", "replications"), 
    order = "relative"))
##   relative   test elapsed user.self sys.self replications
## 2    1.000 Manual   0.908     0.696    0.108            1
## 1    3.963     DT   3.598     3.564    0.012            1

rm(data, dateCols, nvc, dtt)

data <- makeSomeData(2500, 8000)
dtt <- data.table(data)
suppressWarnings(benchmark(DT = eval(DT), Manual = eval(Manual), replications = 1, 
    columns = c("relative", "test", "elapsed", "user.self", "sys.self", "replications"), 
    order = "relative"))
##   relative   test elapsed user.self sys.self replications
## 2    1.000 Manual   2.841     1.044    0.296            1
## 1    1.694     DT   4.813     4.661    0.080            1

rm(data, dateCols, nvc, dtt)

data <- makeSomeData(3000, 8000)
dtt <- data.table(data)
suppressWarnings(benchmark(DT = eval(DT), Manual = eval(Manual), replications = 1, 
    columns = c("relative", "test", "elapsed", "user.self", "sys.self", "replications"), 
    order = "relative"))
##   relative   test elapsed user.self sys.self replications
## 1     1.00     DT   7.223     5.769    0.112            1
## 2    29.27 Manual 211.416     1.560    0.952            1

痛い!data.table最後の実行で本当にテーブルが変わります!

于 2013-03-03T17:16:42.377 に答える