Bagaimana cara mengekstrak n baris pertama dan menghitung fungsi per grup menggunakan subset itu, lalu menghitung mean dari grup yang berbeda?

Ini adalah tindak lanjut dari pertanyaan saya sebelumnya: Bagaimana cara mengekstrak n baris pertama per grup dan menghitung fungsi menggunakan subset itu?

Posting lain yang relevan juga: Cara mengekstrak n baris pertama per kelompok?

Saya memiliki data berikut:

set.seed(1)
dt1 <- data.table(ticker="aa",letters=sample(LETTERS,10^6,T),x=rnorm(2000,100,10),y=rnorm(2000,80,20))
dt2 <- data.table(ticker="aapl",letters=sample(LETTERS,10^6,T),x=rnorm(2000,100,10),y=rnorm(2000,80,20))
dt3 <- data.table(ticker="abc",letters=sample(LETTERS,10^6,T),x=rnorm(2000,100,10),y=rnorm(2000,80,20))
myList <- list(dt1,dt2,dt3)

Saya ingin menerapkan fungsi ke data ini pada indeks tertentu berdasarkan grup di mana output fungsi bergantung pada kerangka data yang disubset. Saya kemudian ingin mengelompokkan data.tabel yang dihasilkan dengan variabel pengelompokan yang berbeda dan mengambil cara sederhana.

Apakah saya ingin menghitung fungsi saya berdasarkan grup1 pada baris subset terlebih dahulu, rbindlist hasilnya, lalu menghitung mean berdasarkan grup2?

Atau apakah saya ingin rbindlist seluruh data saya terlebih dahulu, pilih baris subset terlebih dahulu, lalu hitung fungsi saya berdasarkan grup1 lalu hitung rata-rata berdasarkan grup2?

# data.table version of function
dt_calc_perf <- function(dt){
  buy <- ifelse(dt$x > mean(dt$y),1,0)
  dt$perf <- buy*(dt$x/dt$y-1)
  return(dt)
}
# vector return version of function
calc_perf <- function(dt){
  buy <- ifelse(dt$x > mean(dt$y),1,0)
  perf <- buy*(dt$x/dt$y-1)
  return(perf)
}

# which is faster?

# method 1
method1 <- function(){
  res1 <- rbindlist(lapply(1:length(myList), 
                           function(m) dt_calc_perf(myList[[m]][1:1000])))
  res1 <- res1[,list('perf'=mean(perf),'tickers'=paste(ticker,collapse=',')),
               by=letters]
}

# method 2
dt <- rbindlist(myList)
x <- dt[dt[,.I[1:1000],by=ticker]$V1]

method2 <- function(){
  res2 <- x[,list('letters'=letters,'perf'= calc_perf(.SD)),by=ticker]
  res2 <- res2[,list('perf'=mean(perf),'tickers'=paste(ticker,collapse=',')),
               by=letters]

}

all.equal(method1(),method2())
[1] TRUE

dengan panjang(Daftar Saya) = 3:

 microbenchmark(method1(),method2())
Unit: milliseconds
      expr      min       lq     mean   median       uq       max neval
 method1() 2.874678 2.976673 3.181134 3.031414 3.103259 10.266646   100
 method2() 3.008534 3.150086 3.352862 3.215517 3.292495  9.901859   100

dengan panjang(Daftar Saya) = 12:

> myList <- list(dt1,dt2,dt3,dt1,dt2,dt3,dt1,dt2,dt3,dt1,dt2,dt3)
> microbenchmark(method1(),method2())
Unit: milliseconds
      expr      min       lq      mean   median        uq       max neval
 method1() 9.284757 9.655745 10.346527 9.786392 10.016470 17.044078   100
 method2() 3.020508 3.176173  3.330252 3.239680  3.322644  9.895444   100

Sunting:::

Satu hal yang perlu diperhatikan adalah fungsi method saya pada akhirnya akan dimasukkan ke dalam algoritma optimasi genetik di mana method akan dipanggil berkali-kali. Tujuan saya adalah untuk dapat menghitung calc_perf (yang pada kenyataannya jauh lebih kompleks: input dt output vektor perf) berdasarkan subset dan ticker. Lalu kelompokkan hasil dt dengan letters dan hitung mean(perf).


person road_to_quantdom    schedule 22.10.2018    source sumber
comment
Maaf, saya tidak jelas apa yang Anda tanyakan...   -  person MichaelChirico    schedule 23.10.2018
comment
Apa cara paling efisien untuk melakukan ini? Apakah ada implementasi yang lebih cepat yang saya lewatkan?   -  person road_to_quantdom    schedule 23.10.2018
comment
data Anda sudah dibagi berdasarkan tiket? atau Anda membaginya untuk metode 1   -  person MichaelChirico    schedule 23.10.2018
comment
data saya disimpan dalam daftar. setiap item dalam daftar adalah data.tabel dari ticker tertentu   -  person road_to_quantdom    schedule 23.10.2018


Jawaban (1)


Pertama, menurut saya jumlah subsetting harus ditingkatkan untuk tolok ukur, sehingga kita dapat melihat hambatan dengan lebih baik, jadi:

sn <- 100000

Kedua, saat melakukan benchmarking, menurut saya rbindlist harus dimasukkan ke dalam method2, jadi:

method2 <- function() {
  dt <- rbindlist(myList)
  x <- dt[dt[, .I[1:sn], by = ticker]$V1]
  res2 <- x[, list('letters' = letters, 'perf' = calc_perf(.SD[1:sn])),
            by = ticker]
  res2[, list('perf' = mean(perf),
              'tickers' = paste(ticker, collapse = ',')),
       by = letters]
}

Metode saya, mirip dengan method1, tetapi implementasi penghitungan kinerja berbeda:

method3 <- function() {
  require(hutils)
  dl <- lapply(myList, function(x) {
    x[1:sn][, perf := if_else(x > mean(y), x/y - 1, 0)]
  })
  x <- rbindlist(dl)
  x[, list('perf' = mean(perf),
           'tickers' = paste(ticker, collapse = ',')),
    by = letters]
}

Tolak ukur:

# for data creation:
creatData <- function(x) {
  data.table(ticker = as.character(x), letters = sample(LETTERS, 10 ^ 6, T),
             x = rnorm(2000, 100, 10), y = rnorm(2000, 80, 20))
}
# create larger list:
set.seed(12)
myList <- lapply(1:40, creatData)

system.time(r1 <- method1()) # 1.84 - 2.55
system.time(r2 <- method2()) # 3.76 - 5.59
system.time(r3 <- method3()) # 1.46 - 1.62

all.equal(r1, r2) # T
all.equal(r1, r3) # T
person minem    schedule 25.10.2018
comment
Saya setuju bahwa peningkatan subsetting adalah hal yang baik untuk berpotensi diubah guna melihat kemacetan dengan lebih baik. Namun, saya tidak ingin memasukkan rbindlist ke dalam method2. Fungsi method saya pada akhirnya akan dimasukkan ke dalam optimasi algoritma genetika. Saya mencoba meminimalkan jumlah yang benar-benar diperlukan di method karena pengoptimalan menghasilkan jumlah pemanggilan fungsi yang tidak masuk akal. Saya akan mengedit pertanyaannya sesuai dengan itu - person road_to_quantdom; 25.10.2018
comment
@road_to_quantdom seberapa besar data Anda yang sebenarnya? bisakah kamu rbindlist semuanya? Seberapa rumitkah calc_perf yang sebenarnya? mungkin fungsi itu hanya perlu optimasi? - person minem; 25.10.2018
comment
data rbindlisted adalah 74 juta baris. Ini masih merupakan bagian dari data yang lebih besar (walaupun cukup), saya tidak yakin apakah saya dapat rbindlist seluruh data. calc_perf memerlukan pembuatan 3 vektor kolom, dan informasi dari 4 kolom lainnya di dt. - person road_to_quantdom; 25.10.2018
comment
Sepertinya mengubah fungsi saya untuk menggunakan := adalah pendekatan terbaik untuk meningkatkan kecepatan. Rasanya aneh mengubah fungsi yang memerlukan input dt menjadi fungsi yang memerlukan daftar begitu banyak nama vektor kolom - person road_to_quantdom; 25.10.2018
comment
@road_to_quantdom mungkin Anda dapat membuat pertanyaan terpisah untuk calc_perf pengoptimalan fungsi, dengan memberikan kode asli? akan bermanfaat juga jika Anda menyusun contoh Anda sedekat mungkin dengan kenyataan dan menyediakan operasi nyata yang Anda lakukan - person minem; 25.10.2018