2016-04-20 12 views
3

私はコードをより速く走らせるために少し助けてくれることを望んでいました。密度マップを効率的に作成するPython

基本的には、リスト内に緯度と経度の正方形のグリッドがあります。insideoceanlist次に特定の日の落雷を表すlat, long座標のデータファイルを含むディレクトリがあります。アイデアは毎日のために、正方形のグリッド上の各点の周りにどれくらいの落雷があるか知りたいと思っています。現時点では2つのループしかないので、正方形のグリッド上のすべての点について、その日の各落雷の距離を確認します。それが40km以内なら、その点に1を加えて密度マップを作成します。

開始グリッドは、幅0.11、長さ0.11の四角形で構成される矩形の全体的な形状を持ちます。長方形全体は約50x30です。最後に、オーストラリアの「予測ゾーン」の概要を示すシェイプファイルがあります。グリッド内のポイントがこのゾーンの外にある場合は省略します。したがって、残りのポイント(insideoceanlist)はすべてオーストラリアのものです。

正方形のグリッドには約100000点があり、低速の日には約1000回の落雷があるため、処理に時間がかかります。これをより効率的に行う方法はありますか?私は本当に助言に感謝します。

ちなみにlist2list3に変更しました。リストを反復することは、Pythonの配列よりも速いと聞きました。

for i in range(len(list1)): #list1 is a list of data files containing lat,long coords for lightning strikes for each day 
    dict_density = {} 
    for k in insideoceanlist: #insideoceanlist is a grid of ~100000 lat,long points 
     dict_density[k] = 0 
    list2 = np.loadtxt(list1[i],delimiter = ",") #this open one of the files containing lat,long coords and puts it into an array 
    list3 = map(list,list2) #converts the array into a list 
    # the following part is what I wanted to improve 
    for j in insideoceanlist: 
     for l in list3: 
      if great_circle(l,j).meters < 40000: #great_circle is a function which measures distance between points the two lat,long points 
       dict_density[j] += 1 
    # 
    filename = 'example' +str(i) + '.txt' 
     with open(filename, 'w') as f: 
      for m in range(len(insideoceanlist)): 
       f.write('%s\n' % (dict_density[insideoceanlist[m]])) #writes each point in the same order as the insideoceanlist 
    f.close() 
+1

点の正方形のグリッドは、リストに格納されるのではなく、数学的関数で記述することができます。同様に、どの四角形がどの四角形の中心点に当たるかに相当する四角形は、大規模な比較をせずに直接計算することができます。 –

+0

何の数学関数によって?どの四角形がどの四角形を反復せずにどの四角形に入るかを調べる方法は本当に分かりません。すべての広場は約10kmの広さですので、もし私がこれを取得すれば、私はちょうど近くの広場のそれぞれの点をチェックしますか?歓声 – tpup1

+0

グリッドはどのように定義されていますか?私は正方形のグリッドが完全に正方形であると仮定していました。これは数字のスケーリングと丸めの問題ですが、そうでなければ少し複雑になります。 –

答えて

3

@ DanGetzさんの答えを少し詳しく説明するため、各ストライクポイントのグリッド全体を反復するのではなく、ストライクデータをドライバとして使用するコードを示します。私はあなたが度のサイズが緯度によって異なるにもかかわらず、0.11度の格子の正方形、とオーストラリアの中点を中心にしていると仮定しています!

Wikipediaへのクイックリファレンスを使って、40kmの距離が北から南まで±4グリッド・スクエアの範囲であり、東から北へのグリッド・スクエアの範囲が±5であることがわかります西。

ここでのトリックは、前述のように、ストライク位置(緯度/経度)から直接的に式のようにグリッドに変換することです。グリッドの1つのコーナーの位置を把握し、ストライクからその位置を減算し、グリッドのサイズで除算します(0.11度、切り捨て、行/ colインデックスがあります)。距離が大きくなるまで、周囲のすべての正方形を訪問してください。これは、最大で1 +(2 * 2 * 4 * 5)= 81の正方形で距離を確認します。範囲内の四角形を増やします。

結果は、100,000グリッド・スクエアに1000回のストライクを訪問するのと違って、最大81回の訪問で1000回のストライク(または多くの場合)を実行しているということです。これは大きなパフォーマンス向上です。

着信データ形式については説明していないので、数字をランダムに生成したことに注意してください。あなたはそれを修正したいと思うでしょう。 ;-)

#!python3 

""" 
Per WikiPedia (https://en.wikipedia.org/wiki/Centre_points_of_Australia) 

Median point 
============ 

The median point was calculated as the midpoint between the extremes of 
latitude and longitude of the continent. 

    24 degrees 15 minutes south latitude, 133 degrees 25 minutes east 
    longitude (24°15′S 133°25′E); position on SG53-01 Henbury 1:250 000 
    and 5549 James 1:100 000 scale maps. 

""" 
MEDIAN_LAT = -(24.00 + 15.00/60.00) 
MEDIAN_LON = (133 + 25.00/60.00) 

""" 
From the OP: 

The starting grid has the overall shape of a rectangle, made up of 
squares with width of 0.11 and length 0.11. The entire rectange is about 
50x30. Lastly I have a shapefile which outlines the 'forecast zones' in 
Australia, and if any point in the grid is outside this zone then we 
omit it. So all the leftover points (insideoceanlist) are the ones in 
Australia. 
""" 

DELTA_LAT = 0.11 
DELTA_LON = 0.11 

GRID_WIDTH = 50.0 # degrees 
GRID_HEIGHT = 30.0 # degrees 

GRID_ROWS = int(GRID_HEIGHT/DELTA_LAT) + 1 
GRID_COLS = int(GRID_WIDTH/DELTA_LON) + 1 

LAT_SIGN = 1.0 if MEDIAN_LAT >= 0 else -1.0 
LON_SIGN = 1.0 if MEDIAN_LON >= 0 else -1.0 

GRID_LOW_LAT = MEDIAN_LAT - (LAT_SIGN * GRID_HEIGHT/2.0) 
GRID_HIGH_LAT = MEDIAN_LAT + (LAT_SIGN * GRID_HEIGHT/2.0) 
GRID_MIN_LAT = min(GRID_LOW_LAT, GRID_HIGH_LAT) 
GRID_MAX_LAT = max(GRID_LOW_LAT, GRID_HIGH_LAT) 

GRID_LOW_LON = MEDIAN_LON - (LON_SIGN * GRID_WIDTH/2.0) 
GRID_HIGH_LON = MEDIAN_LON + (LON_SIGN * GRID_WIDTH/2.0) 
GRID_MIN_LON = min(GRID_LOW_LON, GRID_HIGH_LON) 
GRID_MAX_LON = max(GRID_LOW_LON, GRID_HIGH_LON) 

GRID_PROXIMITY_KM = 40.0 

"""https://en.wikipedia.org/wiki/Longitude#Length_of_a_degree_of_longitude""" 
_Degree_sizes_km = (
    (0, 110.574, 111.320), 
    (15, 110.649, 107.551), 
    (30, 110.852, 96.486), 
    (45, 111.132, 78.847), 
    (60, 111.412, 55.800), 
    (75, 111.618, 28.902), 
    (90, 111.694, 0.000), 
) 

# For the Australia situation, +/- 15 degrees means that our worst 
# case scenario is about 40 degrees south. At that point, a single 
# degree of longitude is smallest, with a size about 80 km. That 
# in turn means a 40 km distance window will span half a degree or so. 
# Since grid squares a 0.11 degree across, we have to check +/- 5 
# cols. 

GRID_SEARCH_COLS = 5 

# Latitude degrees are nice and constant-like at about 110km. That means 
# a .11 degree grid square is 12km or so, making our search range +/- 4 
# rows. 

GRID_SEARCH_ROWS = 4 

def make_grid(rows, cols): 
    return [[0 for col in range(cols)] for row in range(rows)] 

Grid = make_grid(GRID_ROWS, GRID_COLS) 

def _col_to_lon(col): 
    return GRID_LOW_LON + (LON_SIGN * DELTA_LON * col) 

Col_to_lon = [_col_to_lon(c) for c in range(GRID_COLS)] 

def _row_to_lat(row): 
    return GRID_LOW_LAT + (LAT_SIGN * DELTA_LAT * row) 

Row_to_lat = [_row_to_lat(r) for r in range(GRID_ROWS)] 

def pos_to_grid(pos): 
    lat, lon = pos 

    if lat < GRID_MIN_LAT or lat >= GRID_MAX_LAT: 
     print("Lat limits:", GRID_MIN_LAT, GRID_MAX_LAT) 
     print("Position {} is outside grid.".format(pos)) 
     return None 

    if lon < GRID_MIN_LON or lon >= GRID_MAX_LON: 
     print("Lon limits:", GRID_MIN_LON, GRID_MAX_LON) 
     print("Position {} is outside grid.".format(pos)) 
     return None 

    row = int((lat - GRID_LOW_LAT)/DELTA_LAT) 
    col = int((lon - GRID_LOW_LON)/DELTA_LON) 

    return (row, col) 


def visit_nearby_grid_points(pos, dist_km): 
    row, col = pos_to_grid(pos) 

    # +0, +0 is not symmetric - don't increment twice 
    Grid[row][col] += 1 

    for dr in range(1, GRID_SEARCH_ROWS): 
     for dc in range(1, GRID_SEARCH_COLS): 
      misses = 0 
      gridpos = Row_to_lat[row+dr], Col_to_lon[col+dc] 
      if great_circle(pos, gridpos).meters <= dist_km: 
       Grid[row+dr][col+dc] += 1 
      else: 
       misses += 1 
      gridpos = Row_to_lat[row+dr], Col_to_lon[col-dc] 
      if great_circle(pos, gridpos).meters <= dist_km: 
       Grid[row+dr][col-dc] += 1 
      else: 
       misses += 1 
      gridpos = Row_to_lat[row-dr], Col_to_lon[col+dc] 
      if great_circle(pos, gridpos).meters <= dist_km: 
       Grid[row-dr][col+dc] += 1 
      else: 
       misses += 1 
      gridpos = Row_to_lat[row-dr], Col_to_lon[col-dc] 
      if great_circle(pos, gridpos).meters <= dist_km: 
       Grid[row-dr][col-dc] += 1 
      else: 
       misses += 1 
      if misses == 4: 
       break 

def get_pos_from_line(line): 
    """ 
    FIXME: Don't know the format of your data, just random numbers 
    """ 
    import random 
    return (random.uniform(GRID_LOW_LAT, GRID_HIGH_LAT), 
      random.uniform(GRID_LOW_LON, GRID_HIGH_LON)) 

with open("strikes.data", "r") as strikes: 
    for line in strikes: 
     pos = get_pos_from_line(line) 
     visit_nearby_grid_points(pos, GRID_PROXIMITY_KM) 
0

インデックス作成にNumpyを使ってみましたか? Numpy配列は基本的にC配列のPythonラッパーであるため、多次元配列を使用することができます。

さらに高速化が必要な場合は、Pythonを最適化したCコンバータであるCythonをご覧ください。多次元インデックス作成に特に適しており、このタイプのコードを約1桁程度高速化できるはずです。これは、コードに1つの追加の依存関係を追加しますが、実装は難しくありません。

Benchmarks)、(Tutorial using Numpy with Cython

また余談迅速として、あなたが明示的範囲(によって生成されたリストが必要な場合)、本質的にあなたが唯一のこれまで(範囲を使用する必要があります

for listI in list1: 
    ... 
    list2 = np.loadtxt(listI, delimiter=',') 
# or if that doesn't work, at least use xrange() rather than range() 

を使用します)関数。あなたのケースでは、それが最も外側のループであるため、多くのことを行うべきではありません。

+0

Justinに感謝、私は間違いなくCythonを見ていきます。私はあなたが索引付けについて何を意味するか分かりません。私は反復のための配列 'list2'を使用してみましたが、それは少し遅いです。範囲についての良いヒント()、歓声。 – tpup1

1

グリッド上にポイントを生成する数式が分かっている場合は、その式を元に戻すことで、特定のポイントに最も近いグリッドポイントをすぐに見つけることができます。

以下は、あなたの目的にはあまり適していない動機づけの例です。地球は球であり、平らではなく円柱ではないためです。あなたは簡単に最も近い格子点を見つけるために、格子点式を元に戻すことはできません場合は、多分あなたは次の操作を行うことができます。

  • は、第2のグリッドを作成簡単な数式のようであること(のはG2それを呼びましょう) 1つのボックス内の任意のポイントに最も近いグリッドポイントが同じボックスにあるか、または8つの隣接するボックスのいずれかにあると確信できるように、十分大きなボックスを使用します。
  • それは
  • に入ることになる元のグリッド(G1)ポイントです
  • はあなたが分類しようとしているpポイントを取り、G2箱を見つけるG2グリッドのどのボックスに格納する比較dictを作成このG2ボックス内のすべてのG1ポイント、およびそのボックス
  • の全てのすぐ隣にpp
に最も近いですこれらの G1ポイントを選択します

あなたが長dの辺と、回転していない平らな表面上に完璧な正方形の格子を有していた場合、その点は、単純な数式で定義することができる完全なフラットグリッドと

の例を動機付け。用

long0 + d * j 

:その緯度値は、すべてのlat0が最も低い番号の緯度であり、そしてそれらの経度値が同じ形式であろういくつかの整数値i

lat0 + d * i 

形態であろうある整数j。与えられた(lat, long)ペアの最も近いグリッドポイントを見つけるには、緯度と経度を別々に見つけることができます。グリッド上の最も近い緯度番号される場所

i = round((lat - lat0)/d) 

及び経度のために同様にj = round((long - long0)/d)

だから、あなたが前に進むことができます一つの方法は、上記の式にしていることをプラグインし、

grid_point = (lat0 + d * round((lat - lat0)/d), 
       long0 + d * round((long - long0)/d) 

を取得し、ちょうどその格子点であなたのdictでカウントをインクリメントすることです。距離のために何千ものグリッドポイントをチェックするのではなく、2つの計算でグリッドポイントを直接見つけることができたので、これまでのコードよりもはるかに高速になります。

あなたはおそらく代わりにdictgrid_pointを使用しての、多次元配列へのインデックスとしてij番号を使用して、この少し速く行うことができます。

+0

経度は、緯度が上がるにつれて近づきます。 –

+0

@AustinHastingsああ、それは覚えています。私はこれがもっと複​​雑な方法を必要とするのではないかと心配しました。しかし同様のことはまだ可能です。 –

+1

私は経度を扱うのに慣れていないので、警告を追加して、誰かがより良い答えを出すまで助けてくれることを期待してこのままにしておきます。なぜ私たちはトーラスに住んでいけませんか? –

関連する問題