पंडों में दो जियोडेटाफ़्रेम के साथ निकटतम दूरी प्राप्त करें


14

यहाँ मेरा पहला जियोडेटफ़्रेम है:

!pip install geopandas
import pandas as pd
import geopandas

city1 = [{'City':"Buenos Aires","Country":"Argentina","Latitude":-34.58,"Longitude":-58.66},
           {'City':"Brasilia","Country":"Brazil","Latitude":-15.78 ,"Longitude":-70.66},
         {'City':"Santiago","Country":"Chile ","Latitude":-33.45 ,"Longitude":-70.66 }]
city2 =  [{'City':"Bogota","Country":"Colombia ","Latitude":4.60 ,"Longitude":-74.08},
           {'City':"Caracas","Country":"Venezuela","Latitude":10.48  ,"Longitude":-66.86}]
city1df = pd.DataFrame(city1)
city2df = pd.DataFrame(city2)
gcity1df = geopandas.GeoDataFrame(
    city1df, geometry=geopandas.points_from_xy(city1df.Longitude, city1df.Latitude))
gcity2df = geopandas.GeoDataFrame(
    city2df, geometry=geopandas.points_from_xy(city2df.Longitude, city2df.Latitude))

City1

           City    Country  Latitude  Longitude                     geometry
0  Buenos Aires  Argentina    -34.58     -58.66  POINT (-58.66000 -34.58000)
1      Brasilia     Brazil    -15.78     -47.91  POINT (-47.91000 -15.78000)
2      Santiago      Chile    -33.45     -70.66  POINT (-70.66000 -33.45000)

और मेरा दूसरा जियोडेटाफ़्रेम: सिटी 2:

         City    Country  Latitude  Longitude                     geometry
1        Bogota   Colombia      4.60     -74.08    POINT (-74.08000 4.60000)
2       Caracas  Venezuela     10.48     -66.86   POINT (-66.86000 10.48000)

मैं शहर से निकटतम शहर के साथ तीसरे डेटाफ्रेम को शहर 1 से शहर 2 तक की दूरी के साथ पसंद करूंगा:

           City    Country  Latitude  Longitude                     geometry    Nearest    Distance
0  Buenos Aires  Argentina    -34.58     -58.66  POINT (-58.66000 -34.58000)    Bogota    111 Km

यहाँ मेरा वास्तविक समाधान है geodjango और तानाशाह का उपयोग (लेकिन यह बहुत लंबा है):

from django.contrib.gis.geos import GEOSGeometry
result = []
dict_result = {}
for city01 in city1 :
  dist = 99999999
  pnt = GEOSGeometry('SRID=4326;POINT( '+str(city01["Latitude"])+' '+str(city01['Longitude'])+')')
  for city02 in city2:
    pnt2 = GEOSGeometry('SRID=4326;POINT('+str(city02['Latitude'])+' '+str(city02['Longitude'])+')')
    distance_test = pnt.distance(pnt2) * 100
    if distance_test < dist :
      dist = distance_test
  result.append(dist)
  dict_result[city01['City']] = city02['City']

यहाँ मेरी कोशिश कर रहे हैं:

from shapely.ops import nearest_points
# unary union of the gpd2 geomtries 
pts3 = gcity2df.geometry.unary_union
def Euclidean_Dist(df1, df2, cols=['x_coord','y_coord']):
    return np.linalg.norm(df1[cols].values - df2[cols].values,
                   axis=1)
def near(point, pts=pts3):
     # find the nearest point and return the corresponding Place value
     nearest = gcity2df.geometry == nearest_points(point, pts)[1]

     return gcity2df[nearest].City
gcity1df['Nearest'] = gcity1df.apply(lambda row: near(row.geometry), axis=1)
gcity1df

यहाँ :

    City    Country     Latitude    Longitude   geometry    Nearest
0   Buenos Aires    Argentina   -34.58  -58.66  POINT (-58.66000 -34.58000)     Bogota
1   Brasilia    Brazil  -15.78  -70.66  POINT (-70.66000 -15.78000)     Bogota
2   Santiago    Chile   -33.45  -70.66  POINT (-70.66000 -33.45000)     Bogota

सादर


नमस्कार और StackOverflow में आपका स्वागत है! आपको लगता है कि StackOverflow एक साइट है जहाँ आप एक समस्या पोस्ट करते हैं और बदले में कुछ कोड प्राप्त करते हैं। यह वास्तव में मामला नहीं है। शीघ्र ही आपका प्रश्न बंद या हटा दिया जाएगा। भविष्य में ऐसा होने से रोकने के लिए, कृपया भ्रमण करें और सहायता केंद्र पर एक नज़र डालें । विशेष रूप से, अपने आप को पारिवारिक बनाने के रूप में यहाँ पर विषय के रूप में माना जाता है
azro

इसके अलावा, जब आप DF के बारे में पोस्ट करते हैं, तो कृपया उन सभी लोगों के लिए DF सामग्री के साथ pyhton कोड पोस्ट करें, जो आपकी मदद करना चाहते हैं, अपने दम पर न लिखें
azro

@azro मैंने संपादित किया है और समस्या और मेरे प्रारंभिक डेटा के लिए मेरा समाधान जोड़ा है।
user462794

क्या आपके शहर केवल दक्षिण अमेरिका में हैं? यदि नहीं, तो वे एक दूसरे से कितनी दूर हो सकते हैं? शहर 1 में कितने शहर हो सकते हैं, और शहर 2 में कितने होंगे? क्या सबसे तेज़ समाधान खोजना महत्वपूर्ण है, या एक सरल समाधान है जो एक उचित समय में चलता है ठीक है? यदि बाद का मामला है, तो एक उचित समय क्या होगा?
वाल्टर ट्रॉस

@WalterTross मेरा शहर दुनिया भर में है, और मैं उपवास समाधान खोज रहा हूं। धन्यवाद
user462794

जवाबों:


11

सबसे पहले, मैं दो डेटा फ़्रेम को क्रॉस जॉइन करके मर्ज करता हूं। और फिर, मैंने mapअजगर में दो बिंदुओं के बीच की दूरी पाई । मैं उपयोग करता हूं map, क्योंकि अधिकांश समय की तुलना में यह बहुत तेज है apply,itertuples , iterrowsआदि (संदर्भ: https://stackoverflow.com/a/52674448/8205554 )

अंत में, मैं डेटा फ्रेम द्वारा समूह और दूरी के न्यूनतम मूल्यों को लाने के लिए।

यहाँ पुस्तकालय हैं,

import pandas as pd
import geopandas
import geopy.distance
from math import radians, cos, sin, asin, sqrt

यहाँ उपयोग किए गए कार्य हैं,

def dist1(p1, p2):
    lon1, lat1, lon2, lat2 = map(radians, [p1.x, p1.y, p2.x, p2.y])

    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 

    return c * 6373

def dist2(p1, p2):
    lon1, lat1, lon2, lat2 = map(radians, [p1[0], p1[1], p2[0], p2[1]])

    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 

    return c * 6373

def dist3(p1, p2):
    x = p1.y, p1.x
    y = p2.y, p2.x

    return geopy.distance.geodesic(x, y).km

def dist4(p1, p2):
    x = p1[1], p1[0]
    y = p2[1], p2[0]

    return geopy.distance.geodesic(x, y).km

और डेटा,

city1 = [
  {
    'City': 'Buenos Aires',
    'Country': 'Argentina',
    'Latitude': -34.58,
    'Longitude': -58.66
  },
  {
    'City': 'Brasilia',
    'Country': 'Brazil',
    'Latitude': -15.78,
    'Longitude': -70.66
  },
  {
    'City': 'Santiago',
    'Country': 'Chile ',
    'Latitude': -33.45,
    'Longitude': -70.66
  }
]

city2 = [
  {
    'City': 'Bogota',
    'Country': 'Colombia ',
    'Latitude': 4.6,
    'Longitude': -74.08
  },
  {
    'City': 'Caracas',
    'Country': 'Venezuela',
    'Latitude': 10.48,
    'Longitude': -66.86
  }
]


city1df = pd.DataFrame(city1)
city2df = pd.DataFrame(city2)

क्रॉस के साथ जुड़ें geopandas डेटा फ्रेम के ,

gcity1df = geopandas.GeoDataFrame(
    city1df, 
    geometry=geopandas.points_from_xy(city1df.Longitude, city1df.Latitude)
)
gcity2df = geopandas.GeoDataFrame(
    city2df, 
    geometry=geopandas.points_from_xy(city2df.Longitude, city2df.Latitude)
)

# cross join geopandas
gcity1df['key'] = 1
gcity2df['key'] = 1
merged = gcity1df.merge(gcity2df, on='key')

math कार्य और geopandas ,

# 6.64 ms ± 588 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit

# find distance
merged['dist'] = list(map(dist1, merged['geometry_x'], merged['geometry_y']))

mapping = {
    'City_x': 'City',
    'Country_x': 'Country',
    'Latitude_x': 'Latitude',
    'Longitude_x': 'Longitude',
    'geometry_x': 'geometry',
    'City_y': 'Nearest',
    'dist': 'Distance'
}

nearest = merged.loc[merged.groupby(['City_x', 'Country_x'])['dist'].idxmin()]
nearest.rename(columns=mapping)[list(mapping.values())]

           City    Country  Latitude  Longitude                     geometry  \
2      Brasilia     Brazil    -15.78     -70.66  POINT (-70.66000 -15.78000)   
0  Buenos Aires  Argentina    -34.58     -58.66  POINT (-58.66000 -34.58000)   
4      Santiago     Chile     -33.45     -70.66  POINT (-70.66000 -33.45000)   

  Nearest     Distance  
2  Bogota  2297.922808  
0  Bogota  4648.004515  
4  Bogota  4247.586882 

geopy तथा geopandas ,

# 9.99 ms ± 764 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit

# find distance
merged['dist'] = list(map(dist3, merged['geometry_x'], merged['geometry_y']))

mapping = {
    'City_x': 'City',
    'Country_x': 'Country',
    'Latitude_x': 'Latitude',
    'Longitude_x': 'Longitude',
    'geometry_x': 'geometry',
    'City_y': 'Nearest',
    'dist': 'Distance'
}

nearest = merged.loc[merged.groupby(['City_x', 'Country_x'])['dist'].idxmin()]
nearest.rename(columns=mapping)[list(mapping.values())]

           City    Country  Latitude  Longitude                     geometry  \
2      Brasilia     Brazil    -15.78     -70.66  POINT (-70.66000 -15.78000)   
0  Buenos Aires  Argentina    -34.58     -58.66  POINT (-58.66000 -34.58000)   
4      Santiago     Chile     -33.45     -70.66  POINT (-70.66000 -33.45000)   

  Nearest     Distance  
2  Bogota  2285.239605  
0  Bogota  4628.641817  
4  Bogota  4226.710978 

यदि आप pandasइसके बजाय उपयोग करना चाहते हैंgeopandas ,

# cross join pandas
city1df['key'] = 1
city2df['key'] = 1
merged = city1df.merge(city2df, on='key')

साथ में mathकार्यों के ,

# 8.65 ms ± 2.21 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit

# find distance
merged['dist'] = list(
    map(
        dist2, 
        merged[['Longitude_x', 'Latitude_x']].values, 
        merged[['Longitude_y', 'Latitude_y']].values
    )
)

mapping = {
    'City_x': 'City',
    'Country_x': 'Country',
    'Latitude_x': 'Latitude',
    'Longitude_x': 'Longitude',
    'City_y': 'Nearest',
    'dist': 'Distance'
}

nearest = merged.loc[merged.groupby(['City_x', 'Country_x'])['dist'].idxmin()]
nearest.rename(columns=mapping)[list(mapping.values())]

           City    Country  Latitude  Longitude Nearest     Distance
2      Brasilia     Brazil    -15.78     -70.66  Bogota  2297.922808
0  Buenos Aires  Argentina    -34.58     -58.66  Bogota  4648.004515
4      Santiago     Chile     -33.45     -70.66  Bogota  4247.586882

के साथ geopy,

# 9.8 ms ± 807 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit

# find distance
merged['dist'] = list(
    map(
        dist4, 
        merged[['Longitude_x', 'Latitude_x']].values, 
        merged[['Longitude_y', 'Latitude_y']].values
    )
)

mapping = {
    'City_x': 'City',
    'Country_x': 'Country',
    'Latitude_x': 'Latitude',
    'Longitude_x': 'Longitude',
    'City_y': 'Nearest',
    'dist': 'Distance'
}

nearest = merged.loc[merged.groupby(['City_x', 'Country_x'])['dist'].idxmin()]
nearest.rename(columns=mapping)[list(mapping.values())]

           City    Country  Latitude  Longitude Nearest     Distance
2      Brasilia     Brazil    -15.78     -70.66  Bogota  2285.239605
0  Buenos Aires  Argentina    -34.58     -58.66  Bogota  4628.641817
4      Santiago     Chile     -33.45     -70.66  Bogota  4226.710978

उन दूरियों की गणना एक अनुमानित सूत्र के साथ की जाती है जो पृथ्वी के समतल होने का हिसाब नहीं देती। geopy.distance.distance()समान 3 दूरी का उपयोग करके (गोलाकार) 2285, 4629और 4227किमी।
वाल्टर ट्रॉस

मैं इन मानों को लिंक से जाँचता हूँ: दूरी .-//33.45,-70.66/ 4.6,-74.08 क्या गलत है?
ई। ज़ेतिजिन

इस तथ्य के अलावा कि मैं अधिक भरोसा करता हूं geopy, एक वेबसाइट के रूप में मैं अधिक edwilliams.org/gccalc.htm पर भरोसा करता हूं , जो इससे सहमत है geopy। एनओएए की वेबसाइट, nhc.noaa.gov/gccalc.shtml , का कहना है कि यह पूर्व पर आधारित है, लेकिन फिर अलग परिणाम देता है। यह शायद पूर्व के एक पुराने संस्करण पर आधारित है।
वाल्टर ट्रॉस

5

मुझे लगता है कि O (m · n) से बेहतर समय जटिलता के साथ समाधान खोजना काफी मुश्किल है , जहां एम और एन हैं के आकार city1औरcity2 । दूरी की तुलना (केवल ओ (एम · एन) ऑपरेशन) को सरल रखते हुए, और खस्ता और पांडा द्वारा प्रदान किए गए वेक्टर संचालन का लाभ उठाते हुए, गति किसी भी उचित इनपुट आकार के लिए समस्या नहीं होनी चाहिए।

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

from geopy.distance import distance as dist
import numpy as np
import pandas as pd

def find_closest(lat1, lng1, lat2, lng2):
    def x_y_z_of_lat_lng_on_unit_sphere(lat, lng):
        rad_lat, rad_lng = np.radians(lat), np.radians(lng)
        sin_lat, sin_lng = np.sin(rad_lat), np.sin(rad_lng)
        cos_lat, cos_lng = np.cos(rad_lat), np.cos(rad_lng)
        return cos_lat * cos_lng, cos_lat * sin_lng, sin_lat
    x1, y1, z1 = x_y_z_of_lat_lng_on_unit_sphere(lat1, lng1)
    x2, y2, z2 = x_y_z_of_lat_lng_on_unit_sphere(lat2, lng2)
    return pd.Series(map(lambda x, y, z:
                         ((x2-x)**2 + (y2-y)**2 + (z2-z)**2).idxmin(),
                         x1, y1, z1))

city1 = [{"City":"Tokyo",    "Ctry":"JP", "Latitude": 35.68972, "Longitude": 139.69222},
         {"City":"Pretoria", "Ctry":"ZA", "Latitude":-25.71667, "Longitude": 28.28333},
         {"City":"London",   "Ctry":"GB", "Latitude": 51.50722, "Longitude": -0.12574}]
city2 = [{"City":"Seattle",  "Ctry":"US", "Latitude": 47.60972, "Longitude":-122.33306},
         {"City":"Auckland", "Ctry":"NZ", "Latitude":-36.84446, "Longitude": 174.76364}]
city1df = pd.DataFrame(city1)
city2df = pd.DataFrame(city2)

closest = find_closest(city1df.Latitude, city1df.Longitude, city2df.Latitude, city2df.Longitude)

resultdf = city1df.join(city2df, on=closest, rsuffix='2')
km = pd.Series(map(lambda latlng1, latlng2: round(dist(latlng1, latlng2).km),
                   resultdf[['Latitude',  'Longitude' ]].to_numpy(),
                   resultdf[['Latitude2', 'Longitude2']].to_numpy()))
resultdf['Distance'] = km
print(resultdf.to_string())
#        City Ctry  Latitude  Longitude     City2 Ctry2  Latitude2  Longitude2  Distance
# 0     Tokyo   JP  35.68972  139.69222   Seattle    US   47.60972  -122.33306      7715
# 1  Pretoria   ZA -25.71667   28.28333  Auckland    NZ  -36.84446   174.76364     12245
# 2    London   GB  51.50722   -0.12574   Seattle    US   47.60972  -122.33306      7723

ध्यान दें कि कोई भी समाधान जो अक्षांश और देशांतर का उपयोग करता है जैसे कि वे कार्टेशियन निर्देशांक गलत हैं, क्योंकि ध्रुवों की ओर बढ़ने से मध्याह्न (समान देशांतर की रेखाएं) एक दूसरे के करीब हो जाती हैं।


3

यह समाधान शायद आपकी समस्या को हल करने का सबसे तेज़ तरीका नहीं है, लेकिन मुझे विश्वास है कि यह चाल चलेगा।

#New dataframe is basicly a copy of first but with more columns
gcity3df = gcity1df.copy()
gcity3df["Nearest"] = None
gcity3df["Distance"] = None

#For each city (row in gcity3df) we will calculate the nearest city from gcity2df and 
fill the Nones with results

for index, row in gcity3df.iterrows():
    #Setting neareast and distance to None, 
    #we will be filling those variables with results

    nearest = None
    distance = None
    for df2index, df2row in gcity2df.iterrows():
        d = row.geometry.distance(df2row.geometry)
        #If df2index city is closer than previous ones, replace nearest with it
        if distance is None or d < distance:
            distance = d
            nearest = df2row.City 
    #In the end we appends the closest city to gdf
    gcity3df.at[index, "Nearest"] = nearest
    gcity3df.at[index, "Distance"] = distance

यदि आपको मीटर पर काम करने की आवश्यकता है और डिग्री नहीं, तो आप हमेशा अपनी परत को हटा सकते हैं (यह उस गलती को भी मिटा देगा जो वाल्टर का मतलब है)। आप इसे कर सकते हैं gcity3df = gcity3df.to_crs({'init': 'epsg:XXXX'})जहां XXXX आपके विश्व क्षेत्र में उपयोग किए जा रहे सीआरएस के लिए ईपीएसजी कोड है।

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