Увеличете производителността/скоростта

Трябва да взема данни от 1303 растера (всеки растер има данни за 1 месец) и да направя времеви серии за всяка клетка от мрежата в растерите. Накрая ще обединя всички времеви редове в един масивен (зоопарк) файл.

Имам кода, който може да го направи (опитах малка част от набора от данни и работи), но изглежда, че отнема цяла вечност само за подреждане на растера (повече от 2 часа вече и все още се броят) и това не е по-бавна част, която ще прави времевите серии. И така, ето моят код, ако някой знае по-бърз начин за подреждане на растери и/или за създаване на времеви серии (може би без двойния цикъл?), моля, помогнете...

Не знам друг език за програмиране, но това ще бъде ли твърде много, за да искам от R?

files <- list.files(pattern=".asc") 
pat <- "^.*pet_([0-9]{1,})_([0-9]{1,}).asc$"
ord_files <- as.Date(gsub(pat, sprintf("%s-%s-01", "\\1", "\\2"), files))
files<-files[order(ord_files)]


#using "raster" package to import data 
s<- raster(files[1])
pet<-vector()
for (i in 2:length(files))
{
r<- raster(files[i])
s <- stack(s, r)
}

#creating a data vector
beginning = as.Date("1901-01-01")
full <- seq(beginning, by='1 month', length=length(files))
dat<-as.yearmon(full)

#building the time series
for (lat in 1:360)
for (long in 1:720)
{
pet<-as.vector(s[lat,long])
x <- xts(pet, dat)
write.zoo(x,file=paste("P:/WRSRL/Users1/ncgk/IBERIA/cru_pet/zoo/","lat",lat,"long",long,".csv", sep="")  , sep=",")
}

person sbg    schedule 29.03.2012    source източник
comment
Въпросът е коя част от кода отнема колко време. Последният двоен цикъл ще бъде изпълнен 360*720 пъти, това е много. Ако имате повече от един процесор, можете да работите паралелно (разгледайте foreach).   -  person smu    schedule 29.03.2012
comment
Все още се боря с импортирането на всички файлове, реших, че растерният пакет ще бъде най-добрият вариант, след като прочетох няколко публикации тук, но не съм сигурен, че работи за 1303 файла. Но read.table е още по-лошо!   -  person sbg    schedule 29.03.2012
comment
Тогава проблемът може да е следният: За всяка итерация R трябва да разпредели нов обект S с нарастващ размер. Това разпределение може да струва много време. Може да е по-бързо да разпределите s преди цикъла. Давам ви тривиален пример: вашият начин: s = c(); for(i in 1:10){ s <- c(s, rnorm(100)) } по-бързо: s = rep(NA, 1000); for(i in seq(1,10*100,100)){ s[i:(i+99)] <- rnorm(100) } (съжалявам, това изглежда грозно като коментар)   -  person smu    schedule 29.03.2012


Отговори (4)


Първият бит може просто да бъде:

s <- stack(files) 

Причината, поради която създаването на стек е донякъде бавно, е, че всеки файл трябва да бъде отворен и проверен, за да се види дали има същия nrow, ncol и т.н. като другите файлове. Ако сте напълно сигурни, че случаят е такъв, можете да използвате пряк път като този (обикновено НЕ се препоръчва)

quickStack <- function(f) {
r <- raster(f[1])
ln <- extension(basename(f), '')
s <- stack(r)
s@layers <- sapply(1:length(f), function(x){ r@file@name = f[x]; r@layernames=ln[x]; r@data@haveminmax=FALSE ; r })
s@layernames <- ln
s
}

quickStack(files)

Вероятно можете също да ускорите втората част, както в примерите по-долу, в зависимост от това колко RAM имате.

Прочетете ред по ред:

for (lat in 1:360) {
pet <- getValues(s, lat, 1)
for (long in 1:720) {
    x <- xts(pet[long,], dat)
    write.zoo(x,file=paste("P:/WRSRL/Users1/ncgk/IBERIA/cru_pet/zoo/","lat",lat,"long",long,".csv", sep="")  , sep=",")
}
}

по-крайно, прочетете всички стойности наведнъж:

 pet <- getValues(s)
 for (lat in 1:360) {
for (long in 1:720) {
    cell <- (lat-1) * 720 + long
    x <- xts(pet[cell,], dat)
    write.zoo(x,file=paste("P:/WRSRL/Users1/ncgk/IBERIA/cru_pet/zoo/","lat",lat,"long",long,".csv", sep="")  , sep=",")
}
}
person Robert Hijmans    schedule 30.03.2012

Ще публикувам повторно коментара си тук и ще дам по-добър пример:

Общата идея: разпределете пространството за s, преди да се изпълни „растерният“ цикъл. Ако свържете s и r към нов обект s вътре в цикъла, R трябва да разпредели нова памет за s за всяка итерация. Това е наистина бавно, особено ако s е голямо.

s <- c()
system.time(for(i in 1:1000){ s <- c(s, rnorm(100))})
# user  system elapsed 
# 0.584   0.244   0.885 

s <- rep(NA, 1000*100)
system.time(for(i in seq(1,1000*100,100)){ s[i:(i+99)] <- rnorm(100) })
# user  system elapsed 
# 0.052   0.000   0.050

както можете да видите, предварителното разпределение е около 10 пъти по-бързо.

За съжаление не съм запознат с raster и stack, така че не мога да ви кажа как да приложите това към вашия код.

person smu    schedule 29.03.2012
comment
благодаря, опитах се да направя това, като разпределих пространството преди цикъла: files1<-files[1:20] arr<-array(data = NA, dim = c(360,720,length(files1))) for (i in 1:length(files1)) {dat<-read.table(files1[i], skip=6)}, но отне 8 секунди за 20 файла, докато използвах растерния пакет отне 3 секунди. Никога преди не съм използвал растер и стек, така че сега не знам как да разпределя предварително с тях... - person sbg; 29.03.2012
comment
Какъв е размерът на вашите файлове? Ако са по-големи, 8 секунди за 20 файла не е лошо. Бихте могли да ускорите read.table, ако посочите типовете данни с помощта на аргумента colClasses. - person smu; 29.03.2012
comment
прав си, не знам защо растерният цикъл работи вече повече от 3 часа... ще го убия и ще пробвам добрата стара read.table... - person sbg; 29.03.2012

Нещо подобно трябва да работи (ако имате достатъчно памет):

#using "raster" package to import data 
rlist <- lapply(files, raster)
s <- do.call(stack, rlist)
rlist <- NULL # to allow freeing of memory

Той зарежда всички raster обекти в голям списък и след това извиква stack веднъж.

Ето пример за увеличения на скоростта: 1,25 секунди срещу 8 секунди за 60 файла - но старият ви код е квадратичен във времето, така че печалбите са много по-големи за повече файлове...

library(raster)
f <- system.file("external/test.grd", package="raster")
files <- rep(f, 60)

system.time({
 rlist <- lapply(files, raster)
 s <- do.call(stack, rlist)
 rlist <- NULL # to allow freeing of memory
}) # 1.25 secs

system.time({
 s<- raster(files[1])
 for (i in 2:length(files)) {
  r<- raster(files[i])
  s <- stack(s, r)
 }
}) # 8 secs
person Tommy    schedule 29.03.2012

Опитах друг начин да се справя с множество файлове. Първо комбинирах растера на времевия ред в един файл във формат NetCDF, използвайки write.Raster(x,format="CDF",..) и след това просто прочетох един файл за всяка година, този път използвах brick(netcdffile,varname ='') четенето спестява много. Трябва обаче да запазя стойността на всяка клетка за всички години според някакъв предварително дефиниран формат, в който използвам write.fwf(x=v,...,append=TRUE), но отнема много време за почти 500 000 точки. Някой има ли същия опит и помощ за това как да се ускори този процес? Ето моя код за извличане на цялата стойност за всяка точка:

weather4Point <- function(startyear,endyear)  
{

  for (year in startyear:endyear)
  {
    #get the combined netCDF file

    tminfile <- paste("tmin","_",year,".nc",sep='')

    b_tmin <- brick(tminfile,varname='tmin')

    pptfile <- paste("ppt","_",year,".nc",sep='')

    b_ppt <- brick(pptfile,varname='ppt')

    tmaxfile <- paste("tmax","_",year,".nc",sep='')

    b_tmax <- brick(tmaxfile,varname='tmax')

    #Get the first year here!!!

    print(paste("processing year :",year,sep=''))

    for(l in 1:length(pl))
    {
      v <- NULL

      #generate file with the name convention with t_n(latitude)w(longitude).txt, 5 digits after point should be work

      filename <- paste("c:/PRISM/MD/N",round(coordinates(pl[l,])[2],5),"W",abs(round(coordinates(pl[l,])[1],5)),".wth",sep='')  

      print(paste("processing file :",filename,sep=''))            

      tmin <- as.numeric(round(extract(b_tmin,coordinates(pl[l,])),digits=1))

      tmax <- as.numeric(round(extract(b_tmax,coordinates(pl[l,])),digits=1))

      ppt <- as.numeric(round(extract(b_ppt,coordinates(pl[l,])),digits=2))

      v <- cbind(tmax,tmin,ppt)

      tablename <- c("tmin","tmax","ppt")

      v <- data.frame(v)   

      colnames(v) <- tablename

      v["default"] <- 0

      v["year"] <- year

      date <- seq(as.Date(paste(year,"/1/1",sep='')),as.Date(paste(year,"/12/31",sep='')),"days")

      month <- as.numeric(substr(date,6,7))

      day   <- as.numeric(substr(date,9,10))

      v["month"] <- month 

      v["day"]  <-  day

      v <- v[c("year","month","day","default","tmin","tmax","ppt")]

      #write into a file with format
      write.fwf(x=v,filename,append=TRUE,na="NA",rownames=FALSE,colnames=FALSE,width=c(6,3,3,5,5,5,6))
    }
  }
}
person Peter    schedule 31.10.2013