GeoPandas: अन्य डेटाफ़्रेम में निकटतम बिंदु ढूंढें


20

मुझे 2 जियोडेटाफ़्रेम मिला है:

import geopandas as gpd
from shapely.geometry import Point
gpd1 = gpd.GeoDataFrame([['John',1,Point(1,1)],['Smith',1,Point(2,2)],['Soap',1,Point(0,2)]],columns=['Name','ID','geometry'])
gpd2 = gpd.GeoDataFrame([['Work',Point(0,1.1)],['Shops',Point(2.5,2)],['Home',Point(1,1.1)]],columns=['Place','geometry'])

और मैं gpd1 में प्रत्येक पंक्ति के लिए gpd2 में निकटतम बिंदु का नाम खोजना चाहता हूं:

desired_output = 

    Name  ID     geometry  Nearest
0   John   1  POINT (1 1)     Home
1  Smith   1  POINT (2 2)    Shops
2   Soap   1  POINT (0 2)     Work

मैं एक लंबो फ़ंक्शन का उपयोग करके इसे प्राप्त करने की कोशिश कर रहा हूं:

gpd1['Nearest'] = gpd1.apply(lambda row: min_dist(row.geometry,gpd2)['Place'] , axis=1)

साथ में

def min_dist(point, gpd2):

    geoseries = some_function()
    return geoseries

इस विधि ने मेरे लिए काम किया: stackoverflow.com/questions/37402046/… इस लिंक को देखें
जॉनी चीज़स्कॉलर

जवाबों:


16

आप सीधे Shapely फ़ंक्शन का उपयोग कर सकते हैं निकटतम अंक (GeoSeries की ज्यामिति Shapely ज्यामितीय हैं):

from shapely.ops import nearest_points
# unary union of the gpd2 geomtries 
pts3 = gpd2.geometry.unary_union
def near(point, pts=pts3):
     # find the nearest point and return the corresponding Place value
     nearest = gpd2.geometry == nearest_points(point, pts)[1]
     return gpd2[nearest].Place.get_values()[0]
gpd1['Nearest'] = gpd1.apply(lambda row: near(row.geometry), axis=1)
gpd1
    Name  ID     geometry  Nearest
0   John   1  POINT (1 1)     Home
1  Smith   1  POINT (2 2)    Shops
2   Soap   1  POINT (0 2)     Work

व्याख्या

for i, row in gpd1.iterrows():
    print nearest_points(row.geometry, pts3)[0], nearest_points(row.geometry, pts3)[1]
 POINT (1 1) POINT (1 1.1)
 POINT (2 2) POINT (2.5 2)
 POINT (0 2) POINT (0 1.1)

मेरे लिए कुछ काम नहीं कर रहा है और मैं इसका पता नहीं लगा सकता। ज्यामिति के ठोस होने के बावजूद फ़ंक्शन रिक्त GeoSeries देता है। उदाहरण के लिए: sample_point = gpd2.geometry.unary_union[400] / sample_point in gpd2.geometry यह सही है। gpd2.geometry == sample_point यह सब झूठा निकलता है।
लूट

ऊपर जोड़: gpd2.geometry.geom_equals(sample_point)काम करता है।
लूट

13

यदि आपके पास बड़े डेटाफ़्रेम हैं, तो मुझे वह मिल गया है scipy cKDTree स्थानिक सूचकांक .queryविधि निकटतम पड़ोसी खोजों के लिए बहुत तेज़ परिणाम देती है। चूंकि यह एक स्थानिक सूचकांक का उपयोग करता है, इसलिए यह डेटाफ्रेम और फिर सभी दूरी को कम से कम पाते हुए लूपिंग की तुलना में तेज़ी से बढ़ने का आदेश देता है। यह आकार में nearest_pointsRTree (जियोपैन्डस के माध्यम से उपलब्ध स्थानिक सूचकांक विधि) के साथ उपयोग करने से भी तेज है क्योंकि cKDTree आपको अपनी खोज को वेक्टर करने की अनुमति देता है जबकि दूसरी विधि नहीं है।

यहां एक सहायक फ़ंक्शन है जो gpd2प्रत्येक बिंदु से निकटतम पड़ोसी की दूरी और 'नाम' लौटाएगा gpd1। यह मानता है कि दोनों gdfs में एक geometryकॉलम (अंकों का) है।

import geopandas as gpd
import numpy as np
import pandas as pd

from scipy.spatial import cKDTree
from shapely.geometry import Point

gpd1 = gpd.GeoDataFrame([['John', 1, Point(1, 1)], ['Smith', 1, Point(2, 2)],
                         ['Soap', 1, Point(0, 2)]],
                        columns=['Name', 'ID', 'geometry'])
gpd2 = gpd.GeoDataFrame([['Work', Point(0, 1.1)], ['Shops', Point(2.5, 2)],
                         ['Home', Point(1, 1.1)]],
                        columns=['Place', 'geometry'])

def ckdnearest(gdA, gdB):
    nA = np.array(list(zip(gdA.geometry.x, gdA.geometry.y)) )
    nB = np.array(list(zip(gdB.geometry.x, gdB.geometry.y)) )
    btree = cKDTree(nB)
    dist, idx = btree.query(nA, k=1)
    gdf = pd.concat(
        [gdA, gdB.loc[idx, gdB.columns != 'geometry'].reset_index(),
         pd.Series(dist, name='dist')], axis=1)
    return gdf

ckdnearest(gpd1, gpd2)

और यदि आप एक LineString के निकटतम बिंदु को ढूंढना चाहते हैं, तो यहां एक पूर्ण कार्य उदाहरण है:

import itertools
from operator import itemgetter

import geopandas as gpd
import numpy as np
import pandas as pd

from scipy.spatial import cKDTree
from shapely.geometry import Point, LineString

gpd1 = gpd.GeoDataFrame([['John', 1, Point(1, 1)],
                         ['Smith', 1, Point(2, 2)],
                         ['Soap', 1, Point(0, 2)]],
                        columns=['Name', 'ID', 'geometry'])
gpd2 = gpd.GeoDataFrame([['Work', LineString([Point(100, 0), Point(100, 1)])],
                         ['Shops', LineString([Point(101, 0), Point(101, 1), Point(102, 3)])],
                         ['Home',  LineString([Point(101, 0), Point(102, 1)])]],
                        columns=['Place', 'geometry'])


def ckdnearest(gdfA, gdfB, gdfB_cols=['Place']):
    A = np.concatenate(
        [np.array(geom.coords) for geom in gdfA.geometry.to_list()])
    B = [np.array(geom.coords) for geom in gdfB.geometry.to_list()]
    B_ix = tuple(itertools.chain.from_iterable(
        [itertools.repeat(i, x) for i, x in enumerate(list(map(len, B)))]))
    B = np.concatenate(B)
    ckd_tree = cKDTree(B)
    dist, idx = ckd_tree.query(A, k=1)
    idx = itemgetter(*idx)(B_ix)
    gdf = pd.concat(
        [gdfA, gdfB.loc[idx, gdfB_cols].reset_index(drop=True),
         pd.Series(dist, name='dist')], axis=1)
    return gdf

c = ckdnearest(gpd1, gpd2)

क्या इस विधि का उपयोग करके रेखा पर निकटतम बिंदु देना संभव है? उदाहरण के लिए निकटतम सड़क पर एक जीपीएस स्थान को स्नैप करने के लिए।
हाइपरनॉट

यह उत्तर आश्चर्यजनक है! हालाँकि, निकटतम बिंदुओं के लिए कोड मेरे लिए एक बग पैदा करता है। ऐसा लगता है कि निकटतम बिंदु से सही दूरी प्रत्येक बिंदु के लिए वापस आ गई है, लेकिन जो लाइन आईडी वापस आ गई है वह गलत है। मुझे लगता है कि इसकी आइडियल गणना है, लेकिन मैं पायथन के लिए बहुत नया हूं, इसलिए मैं इसके चारों ओर अपना सिर लपेटने का प्रबंधन नहीं कर सकता।
शकेदक

1

पता लगा लिया:

def min_dist(point, gpd2):
    gpd2['Dist'] = gpd2.apply(lambda row:  point.distance(row.geometry),axis=1)
    geoseries = gpd2.iloc[gpd2['Dist'].argmin()]
    return geoseries

बेशक कुछ आलोचना का स्वागत है। मैं gpd2 की हर पंक्ति के लिए gpd2 ['Dist'] को रिकॉल करने का प्रशंसक नहीं हूं ...


1

जीन द्वारा जवाब मेरे लिए काम नहीं किया। अंत में मुझे पता चला कि gpd2.geometry.unary_union ने एक ज्यामिति के परिणामस्वरूप केवल मेरे लगभग 150.000 अंकों के कुल 30.000 को समाहित किया। किसी और को उसी समस्या में चलाने के लिए, यहाँ मैंने इसे कैसे हल किया:

    from shapely.ops import nearest_points
    from shapely.geometry import MultiPoint

    gpd2_pts_list = gpd2.geometry.tolist()
    gpd2_pts = MultiPoint(gpd2_pts_list)
    def nearest(point, gpd2_pts, gpd2=gpd2, geom_col='geometry', src_col='Place'):
         # find the nearest point
         nearest_point = nearest_points(point, gpd2_pts)[1]
         # return the corresponding value of the src_col of the nearest point
         value = gpd2[gpd2[geom_col] == nearest_point][src_col].get_values()[0]
         return value

    gpd1['Nearest'] = gpd1.apply(lambda x: nearest(x.geometry, gpd2_pts), axis=1)

0

@ JHuw के उत्कृष्ट उत्तर का उपयोग करते समय अपने स्वयं के डेटा के साथ त्रुटियों को अनुक्रमित करने वाले किसी के लिए , मेरी समस्या यह थी कि मेरे अनुक्रमित संरेखित नहीं थे। GdfA और gdfB के सूचकांक को रीसेट करने से मेरे मुद्दे हल हो गए हैं, शायद इससे आपको @ Shakedk भी मदद मिल सकती है

import itertools
from operator import itemgetter

import geopandas as gpd
import numpy as np
import pandas as pd

from scipy.spatial import cKDTree
from shapely.geometry import Point, LineString

gpd1 = gpd.GeoDataFrame([['John', 1, Point(1, 1)],
                         ['Smith', 1, Point(2, 2)],
                         ['Soap', 1, Point(0, 2)]],
                        columns=['Name', 'ID', 'geometry'])
gpd2 = gpd.GeoDataFrame([['Work', LineString([Point(100, 0), Point(100, 1)])],
                         ['Shops', LineString([Point(101, 0), Point(101, 1), Point(102, 3)])],
                         ['Home',  LineString([Point(101, 0), Point(102, 1)])]],
                        columns=['Place', 'geometry'])


def ckdnearest(gdfA, gdfB, gdfB_cols=['Place']):
    # resetting the index of gdfA and gdfB here.
    gdfA = gdfA.reset_index(drop=True)
    gdfB = gdfB.reset_index(drop=True)
    A = np.concatenate(
        [np.array(geom.coords) for geom in gdfA.geometry.to_list()])
    B = [np.array(geom.coords) for geom in gdfB.geometry.to_list()]
    B_ix = tuple(itertools.chain.from_iterable(
        [itertools.repeat(i, x) for i, x in enumerate(list(map(len, B)))]))
    B = np.concatenate(B)
    ckd_tree = cKDTree(B)
    dist, idx = ckd_tree.query(A, k=1)
    idx = itemgetter(*idx)(B_ix)
    gdf = pd.concat(
        [gdfA, gdfB.loc[idx, gdfB_cols].reset_index(drop=True),
         pd.Series(dist, name='dist')], axis=1)
    return gdf

c = ckdnearest(gpd1, gpd2)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.