Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

最近点の探索(Python)

データセットと目標

2つの点群(A,B)がcsvファイルの形式であります。
Aには直交座標(x,y)の値がメッシュの大きさ2で入っており、各点が固有の値を持っています。つまり、x列、y列、固有の値の列からなるデータです。
BにはAの領域内のランダムな点の座標が格納されています。

これらのデータに対して、Bの各点に最も近い点をAの中から見つけ出し、その固有の値をBの点に付与するという作業をしたいと考えています。
一度コードを書いてみたのですが、処理時間がかかりすぎて困っています。
ご助言いただけると幸いです。

コード

自力で書いたコードは以下の通りです。
(初学者のため読みづらいコードかもしれませんがご容赦ください。)

import pandas as pd
import numpy as np

A = pd.read_csv("ファイルのパス",low_memory=False)
B = pd.read_csv("ファイルのパス")

cols = ['x', 'y', 'elevation','distance']
df = pd.DataFrame(index=[],columns=cols)
###出力用のデータフレームです。elevationが前述の固有の値にあたります。distanceはのちの計算結果を代入するためです。
dammy = pd.DataFrame(index=[],columns=cols)
dammy.loc[0]=999999
###ダミーについては後述します。

for index,row in B.iterrows():
    x_B = row['x']
    y_B = row['y']
    k = A[(A['x']>x_B-1.01)&(A['x']<x_B+1.01)&(A['y']>y_B-1.01)&(A['y']<y_B+1.01)]
###B点を中心とした正方形内部の点に対象を絞ることで処理を軽くしようと考えました
    k = k.assign(distance=0)
    if len(k)==1:
        df = df.append(k)
    elif len(k)==0:
        df = df.append(dammy)
###正方形内部にAの点がなかった場合、行数合わせのためにダミーを入れています
    else:
        for index,row in k.iterrows():
            x_A=row['x']
            y_A=row['y']
            dist=np.sqrt((x_B-x_A)**2+(y_B-y_A)**2)
            k.at[index,'distance'] = dist
        l = k['distance'].idxmin()
        m = k.iloc[l:l+1,:]
        df = df.append(m)
###正方形内部に複数点ある場合のみ実際距離を計算して小さい方を選択しています。

これで1時間以上かかってしますのですが、処理を速めるいいアイデアはないでしょうか。
よろしくお願いします。

0

3Answer

AとBは以下のようなデータということで良いでしょうか。

import numpy as np
import pandas as pd

np.random.seed(0)
A = pd.DataFrame({'x': np.arange(0, 11, 2).repeat(6),
                  'y': np.tile(np.arange(0, 11, 2), 6),
                  'elevation': np.random.randint(0, 10, 36)})
#      x   y  elevation
# 0    0   0          5
# 1    0   2          0
# 2    0   4          3
# 3    0   6          3
# 4    0   8          7
# 5    0  10          9
# 6    2   0          3
# 7    2   2          5
# ...
# 34  10   8          3
# 35  10  10          3

B = pd.DataFrame({'x': np.random.rand(20)*10,
                  'y': np.random.rand(20)*10})
#            x         y
# 0   5.488135  9.786183
# 1   7.151894  7.991586
# 2   6.027634  4.614794
# ...
# 17  8.326198  6.169340
# 18  7.781568  9.437481
# 19  8.700121  6.818203

この場合、KD木の構造を利用したscipy.spatial.cKDTreeを用いると以下のように操作できます。

from scipy.spatial.ckdtree import cKDTree

distance, idx = cKDTree(A[['x', 'y']]).query(B)

このidxには、Bの各点に対する最近傍点のインデックス、つまりAの何番目の点が最近傍点かを示す配列が格納されます。distanceはそこまでの距離です。

以下はBの横に、idxにしたがって並び替えたAを結合したデータフレームです。

pd.concat([B, A.loc[idx].reset_index(drop=True)], axis=1)
#            x         y   x   y  elevation
# 0   5.488135  9.786183   6  10          9
# 1   7.151894  7.991586   8   8          5
# 2   6.027634  4.614794   6   4          5
# ...
# 17  8.326198  6.169340   8   6          3
# 18  7.781568  9.437481   8  10          0
# 19  8.700121  6.818203   8   6          3

問題なく最近傍点が求められていることが確認できます。

求めるデータフレームは以下です。

df = B.assign(elevation=A.loc[idx, 'elevation'].to_numpy(),
              distance=distance)
#            x         y  elevation  distance
# 0   5.488135  9.786183          9  0.554728
# 1   7.151894  7.991586          5  0.848148
# 2   6.027634  4.614794          5  0.615414
# 3   5.448832  7.805292          8  0.584549
# ...
# 17  8.326198  6.169340          3  0.367534
# 18  7.781568  9.437481          0  0.603441
# 19  8.700121  6.818203          3  1.076859

Aの座標の値がバラバラでも問題なく動作するはずです。

2Like

Comments

  1. @kosaku884

    Questioner

    うまくいきました!ありがとうございます!

Aの座標が規則的に2刻みで格納されているならAとBを比較せずとも最近点は求まると思うのですが、Aは↓のようになっているという認識で合っていますか?

x y 固有の値
0 0 0.1
0 2 0.3
0 4 0.2
0Like

Comments

  1. @kosaku884

    Questioner

    コメントありがとうございます。
    基本的にはそのようになっていますが、一部領域で座標変換処理を挟んだ影響で小数点が生じているところがあります。
    よろしくお願いします。

Comments

  1. @kosaku884

    Questioner

    コメントありがとうございます。
    Aの方がかなり膨大で、1億点以上あります(分割は可能です)。Bの方は4000点程度です。
    よろしくお願いします。

Your answer might help someone💌