Rで多くのラスターを効率的に読み取って再分類しますか?


8

私はメキシコ湾の波の状態の適合性分析を作成するように命じられました。約8 MBのラスターファイルが2千個ほどあります(2438列、1749行、1 kmのセルサイズ)。ラスターファイルのパラメーターは波の周期です。if 4<= wave period <=9 then make cell = 1、else cell = 0のようにすべてのラスターを再分類します。次に、すべてのラスターを最終ラスターに合計し、ラスターの総数で除算して、適切な観測の合計パーセンテージと一部のESRI互換形式へのエクスポート結果...必要に応じて浮動小数点をサポートできるものかもしれません。私はPythonもRもあまり操作していませんが、オンラインで検索した後、これらの言語の1つでこのプロセスを実行することは理にかなっているようです。私はこれまでにRでいくつかのコードを考え出しましたが、これを機能させる方法について混乱しています。

library(rgdal)
raster_data <- list.files(path=getwd())    #promt user for dir containing raster files
num_files <- length(raster_data)
for (i in raster_data) {                   #read in rasters
   my_data <- readGDAL(raster_data[i])

この時点で、このループ内でデータの再分類と集計を開始する必要があるかどうかについて混乱しています。そうでなければ、コンピュータのメモリが不足する可能性があると思いますが、確信が持てません。また、データを再分類する方法についてもわかりません。

オンラインでの調査ではreclass(my_data, c(-Inf,3,0, 4,9,1, 10,Inf,0))、を使用すると思いますが、それは正しく見えますか?

そして、要約するためにsum(stack(my_data))、私はそれを使用し、どういうわけかそれを出力します。また、これがより効率的に実行されるか、Pythonで記述されている場合は、私もそれを受け入れます。プログラミングに関しては、私は本当に初心者です。


python-gdalを使用してください。あなたの場合、それははるかに効率的です。
SS_Rebelious 2013年

ありがとう、反逆者。この状況でpython-gdalがより効率的である理由を知りたいだけですか?また、これを行うために必要なコードのステップのいくつかを確認することは可能でしょうか?可能な限り少ないメモリとcpuを使用しながらこれを行うための最善の方法を理解しようとしています...データを読み取り、処理し、メモリから取り出すようにコードを記述する方法を理解するのは混乱します次に、次のラスターに移動します。
Nigel

正確な理由はわかりませんが、一般的な原因は、Rが他の目的のために設計されており、サイクルでパフォーマンスが低下することが知られていることです。コード例については、誰も提供しない場合は、対応するスクリプトが格納されているマシンにアクセスできるようになるまで、約10時間で共有します。
SS_Rebelious 2013

回答:


8

これは、Rでこれを行うための簡潔な方法です---ここでは中間ファイルなしで:

library(raster)
raster_data <- list.files(path=getwd())    #promt user for dir containing raster files
s <- stack(raster_data)
f <- function(x) { rowSums(x >= 4 & x <= 9) }
x <- calc(s, f, progress='text', filename='output.tif')

1
+1は、これは小さな問題のために良いですが、のはこの1つのために数学をやらせる:2438列の時間すべてが1749行回8バイト/値の時間を2000のグリッド= 63.6ギガバイト、R作成するために、RAMに保持する必要がありますs。(sおそらく置き換えられないため、2倍のRAMが必要になる可能性がありraster_dataます。)RAMが大量にあることを願っています!2000グリッドを小さなグループに分割し、各グループの計算を実行してから、それらの計算を組み合わせると、ソリューションを実用的にすることができます。
whuber

2
@whuber: 's'は小さなオブジェクトであり、ファイルへのポインタの集まりです。calc関数は、ラスターパッケージの他の関数と同様に、すべての値をメモリにロードしません。それらをチャンクで処理します。つまり、あなたが提案するグループへの分割は、舞台裏で自動的に行われます。チャンクサイズは、rasterOptions()を介して使用可能なRAMの量に合わせて最適化できます。
Robert Hijmans 2013年

1
説明してくれてありがとう!私はチェックせずにそれを想定し、最初にすべてのデータをRAMにロードすることで他のほとんどの機能stackcalc同じようRに動作しました。
whuber

+1 Rの提供されたPythonの例の
簡潔さを気に入ってい

5

コメントで気づいたように、特定の側面でパフォーマンスの問題があるため、一般的にRを非統計的な目的で使用することは避けてください(サイクルの操作は一例です)。これは、単一バンドの単一ファイルの再分類のためのPyhton(この記事のおかげ)でのコード例です。ディレクトリからすべてのファイルを取得する方法を知っている場合は、バッチ処理用に簡単に変更できます。ラスタは配列として表されるので、必要に応じて配列メソッドを使用してパフォーマンスを向上させることができます。Pythonでの配列の操作については、Numpyのドキュメントを参照してください。

UPD:最初に投稿したコードは、ピクセルごとの処理に必要なカスタムフィルターの切り捨てられたバージョンでした。しかし、この質問では、Numpyを使用するとパフォーマンスが向上します(コードを参照)。

from osgeo import gdal
import sys
import numpy

gdalData = gdal.Open("path_to_file")
if gdalData is None:
  sys.exit("ERROR: can't open raster")

#print "Driver short name", gdalData.GetDriver().ShortName
#print "Driver long name", gdalData.GetDriver().LongName
#print "Raster size", gdalData.RasterXSize, "x", gdalData.RasterYSize
#print "Number of bands", gdalData.RasterCount
#print "Projection", gdalData.GetProjection()
#print "Geo transform", gdalData.GetGeoTransform()


raster = gdalData.ReadAsArray()
xsize = gdalData.RasterXSize
ysize = gdalData.RasterYSize

#print xsize, 'x', ysize

## per pixel processing
#for col in range(xsize):
#  for row in range(ysize):
#    # if pixel value is 16 - change it to 7
#    if raster[row, col] == 16:
#      raster[row, col] = 7

#reclassify raster values equal 16 to 7 using Numpy
temp = numpy.equal(raster, 16)
numpy.putmask(raster, temp, 7)

# write results to file (but at first check if we are able to write this format)
format = "GTiff"
driver = gdal.GetDriverByName(format)
metadata = driver.GetMetadata()
if metadata.has_key(gdal.DCAP_CREATE) and metadata[gdal.DCAP_CREATE] == "YES":
  pass
else:
  print "Driver %s does not support Create() method." % format
  sys.exit(1)
if metadata.has_key(gdal.DCAP_CREATECOPY) and metadata[gdal.DCAP_CREATECOPY] == "YES":
  pass
else:
  print "Driver %s does not support CreateCopy() method." % format
  sys.exit(1)

# we already have the raster with exact parameters that we need
# so we use CreateCopy() method instead of Create() to save our time
outData = driver.CreateCopy("path_to_new_file", gdalData, 0)
outData.GetRasterBand(1).WriteArray(raster)

4
「サイクル(ループ)を実行するとRが遅い」は、Rを回避する理由として誤用されることがよくあります。はい、Rのラスターのセルをループした場合は遅くなりますが、ラスターパッケージはラスター全体で一度に機能します、Cコードがたくさんあるので、Cの速度で実行されます。このサイズのラスターの場合、ほとんどの作業はCの速度で行われ、ループのオーバーヘッドはわずかです。
Spacedman、2013

@Spacedman、はい、「ラスター」は便利なパッケージです(そして私はそれが好きです)が、ループが関係していなくても、そのパフォーマンスに満足することはありませんでした。
SS_Rebelious 2013年

2
さて、Rにかかる時間とPythonにかかる時間をよく比較してください。ループではなく全体の派手な配列を操作できませんか?
Spacedman、2013年

@Spacedman、私はちょうど答えを更新しました。
SS_Rebelious 2013年

両方に感謝します。私はあなたが提供したPythonコードといくつかのRの両方をいじってみて、私が何ができるかを見ていきます。結果や問題で更新します。
Nigel

2
  1. readGDALは使用しないでください。これはSpatial *オブジェクトに読み込まれますが、これは良い考えではありません。

  2. rasterパッケージを使用してください。GDALをRasterオブジェクトに読み込むことができます。これらは良いことです。r = raster("/path/to/rasterfile.tif")に読み込まれrます。

  3. あなたの分類は t = r > 4 & r <= 9

  4. 大きな問題は、これらを新しいラスターファイルに出力してから、別のループで要約手順を実行するかどうかです。ストレージがある場合は、ループが失敗した場合(これらの2000ファイルの1つがジャンクであるため)もう一度開始する必要があるため、新しいファイルに書き込みます。したがって、writeRasterしきい値ラスターを作成する場合は、を使用して作成します。

  5. あなたのループはそのようなものです

この。

count = raster(0,size of your raster)
for(i in 1:number of rasters){
  r = raster(path to binary raster file 'i')
  count = count + r
}
return(count)

Rのメモリ管理はここであなたに打撃を与えるかもしれません-あなたがそうするとき、count=count+rRはの新しいコピーを作成するかもしれませんcount。つまり、2000コピーになる可能性があります。ただし、ガベージコレクションが実行されてクリーンアップされるため、Rのループは非常に悪かったのです。

タイミングに関しては、私の4yo PCでは、0〜20の乱数のサイズのラスターでしきい値opが約1.5秒かかります。Times 2000 =あなたは数学をします。いつものように、コードを開発するための小さなテストセットを作成し、実際のデータでそれを起動してコーヒーを飲みます。


私はあなたが「しきい値操作」によって何を意味するのか知りたいです。私のシステム(R2.15.0を実行)では、1.6メガピクセルのグリッド(ネイティブESRI浮動小数点形式)の読み取りに0.19秒かかり、5つのループ操作(2つの比較、結合、加算、およびストア)を実行すると、さらに時間がかかります。 0.09秒、反復ごとに0.28秒。その代わりに、一度に100グリッドを配列にキャッシュrowSumsし、合計を行うためにループを実行すると、RAMの使用量は当然増加します。ただし、当然のことながら、ガベージコレクションはありません。タイミングは、反復ごとに0.26秒まで低下します。
whuber
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.