web-dev-qa-db-ja.com

geopyを使用した座標を持つ新しい列pandas

私はdfを持っています:

import pandas as pd
import numpy as np
import datetime as DT
import hmac
from geopy.geocoders import Nominatim
from geopy.distance import vincenty

df


     city_name  state_name  county_name
0    WASHINGTON  DC  DIST OF COLUMBIA
1    WASHINGTON  DC  DIST OF COLUMBIA
2    WASHINGTON  DC  DIST OF COLUMBIA
3    WASHINGTON  DC  DIST OF COLUMBIA
4    WASHINGTON  DC  DIST OF COLUMBIA
5    WASHINGTON  DC  DIST OF COLUMBIA
6    WASHINGTON  DC  DIST OF COLUMBIA
7    WASHINGTON  DC  DIST OF COLUMBIA
8    WASHINGTON  DC  DIST OF COLUMBIA
9    WASHINGTON  DC  DIST OF COLUMBIA

以下のデータフレームのいずれかの列の緯度と経度の座標を取得したいと思います。ドキュメント( http://geopy.readthedocs.org/en/latest/#data )は、個々の場所のドキュメントを操作するときに非常に簡単です。

>>> from geopy.geocoders import Nominatim
>>> geolocator = Nominatim()
>>> location = geolocator.geocode("175 5th Avenue NYC")
>>> print(location.address)
Flatiron Building, 175, 5th Avenue, Flatiron, New York, NYC, New York,     ...
>>> print((location.latitude, location.longitude))
(40.7410861, -73.9896297241625)
>>> print(location.raw)
{'place_id': '9167009604', 'type': 'attraction', ...}

ただし、この関数をdfの各行に適用して、新しい列を作成したいと思います。私は以下を試しました

df['city_coord'] = geolocator.geocode(lambda row: 'state_name' (row))

しかし、次のようになるため、コードに何かが欠けていると思います。

    city_name   state_name  county_name coordinates
0    WASHINGTON  DC  DIST OF COLUMBIA    None
1    WASHINGTON  DC  DIST OF COLUMBIA    None
2    WASHINGTON  DC  DIST OF COLUMBIA    None
3    WASHINGTON  DC  DIST OF COLUMBIA    None
4    WASHINGTON  DC  DIST OF COLUMBIA    None
5    WASHINGTON  DC  DIST OF COLUMBIA    None
6    WASHINGTON  DC  DIST OF COLUMBIA    None
7    WASHINGTON  DC  DIST OF COLUMBIA    None
8    WASHINGTON  DC  DIST OF COLUMBIA    None
9    WASHINGTON  DC  DIST OF COLUMBIA    None

うまくいけばLambda関数を使用してこのようなものが欲しいです:

     city_name  state_name  county_name  city_coord
0    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456 
1    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456 
2    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456 
3    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456 
4    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456 
5    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456 
6    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456 
7    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456 
8    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456 
9    WASHINGTON  DC  DIST OF COLUMBIA    38.8949549, -77.0366456
10   GLYNCO      GA  GLYNN               31.2224512, -81.5101023

私はどんな助けにも感謝します。座標を取得したら、それらをマッピングしたいと思います。座標をマッピングするための推奨リソースも大歓迎です。ありがとう

16
Dave

applyを呼び出して、次のようにすべての行で実行する関数を渡すことができます。

_In [9]:

geolocator = Nominatim()
df['city_coord'] = df['state_name'].apply(geolocator.geocode)
df
Out[9]:
    city_name state_name       county_name  \
0  WASHINGTON         DC  DIST OF COLUMBIA   
1  WASHINGTON         DC  DIST OF COLUMBIA   

                                          city_coord  
0  (District of Columbia, United States of Americ...  
1  (District of Columbia, United States of Americ...  
_

次に、緯度と経度の属性にアクセスできます。

_In [16]:

df['city_coord'] = df['city_coord'].apply(lambda x: (x.latitude, x.longitude))
df
Out[16]:
    city_name state_name       county_name                       city_coord
0  WASHINGTON         DC  DIST OF COLUMBIA  (38.8937154, -76.9877934586326)
1  WASHINGTON         DC  DIST OF COLUMBIA  (38.8937154, -76.9877934586326)
_

または、applyを2回呼び出して、ワンライナーでそれを行います。

_In [17]:
df['city_coord'] = df['state_name'].apply(geolocator.geocode).apply(lambda x: (x.latitude, x.longitude))
df

Out[17]:
    city_name state_name       county_name                       city_coord
0  WASHINGTON         DC  DIST OF COLUMBIA  (38.8937154, -76.9877934586326)
1  WASHINGTON         DC  DIST OF COLUMBIA  (38.8937154, -76.9877934586326)
_

また、あなたの試みgeolocator.geocode(lambda row: 'state_name' (row))は何もしなかったので、なぜあなたはNone値でいっぱいの列を持っているのですか?

[〜#〜]編集[〜#〜]

@lebはここで興味深い点を示しています。重複する値が多数ある場合は、一意の値ごとにジオコーディングしてから、これを追加する方がパフォーマンスが高くなります。

_In [38]:
states = df['state_name'].unique()
d = dict(Zip(states, pd.Series(states).apply(geolocator.geocode).apply(lambda x: (x.latitude, x.longitude))))
d

Out[38]:
{'DC': (38.8937154, -76.9877934586326)}

In [40]:    
df['city_coord'] = df['state_name'].map(d)
df

Out[40]:
    city_name state_name       county_name                       city_coord
0  WASHINGTON         DC  DIST OF COLUMBIA  (38.8937154, -76.9877934586326)
1  WASHINGTON         DC  DIST OF COLUMBIA  (38.8937154, -76.9877934586326)
_

したがって、上記はuniqueを使用してすべての一意の値を取得し、それらからdictを作成してから、mapを呼び出してルックアップを実行し、座標を追加します。これは、行をジオコーディングするよりも効率的です。賢い

15
EdChum

@EdChumの答えに賛成して受け入れてください、私はこれに追加したかっただけです。彼の方法は完璧に機能しますが、個人的な経験から、いくつかのことを共有したいと思います。

ジオコーディングを扱う場合、複数の都市と州の組み合わせが繰り返されている場合、ジオコーディングを取得するために1つだけを送信し、残りを以下の他の行に複製する方がはるかに高速です。

これは非常に大きなデータに役立ちます。2つの方法で実行できます。

  1. 行が完全に重複しているように見えるため、データに基づいて、必要な場合にのみ、余分な行を削除して、そのうちの1つにジオコーディングを実行します。これは、_drop_duplicate_を使用して実行できます
  2. すべての行、_group_by_を都市と州の組み合わせで保持する場合は、最初の行にhead(1)を呼び出してジオコーディングを適用し、残りの行に複製します。

理由は、Nominatimを呼び出すたびに、同じ都市/州を続けてキューに入れていたとしても、小さな遅延の問題が発生するためです。このsmallデータが大きくなるとレイテンシーが悪化し、応答が大幅に遅れてタイムアウトになる可能性があります。

繰り返しますが、これはすべて個人的に対処することによるものです。それが今あなたに利益をもたらさないならば、ただ将来の使用のために心に留めておいてください。

4
Leb