Nueva columna con coordenadas usando Geopy Pandas.

Tengo un 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 

Quiero obtener las coordenadas de latitud y longitud para cualquiera de las columnas en el dataframe a continuación. La documentación ( http://geopy.readthedocs.org/en/latest/#data ) es bastante sencilla cuando se trabaja con la documentación para ubicaciones individuales.

 >>> 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', ...} 

Sin embargo, quiero aplicar la función a cada fila en el df y hacer una nueva columna. He intentado lo siguiente

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

pero creo que me falta algo en mi código porque obtengo lo siguiente:

  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 

Me gustaría algo como esto con suerte usando la función 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 

Aprecio cualquier ayuda. Después de obtener las coordenadas me gustaría mapearlas. Cualquier recurso recomendado para mapear coordenadas también es muy apreciado. Gracias

Puede llamar a apply y pasar la función que desea ejecutar en cada fila como la siguiente:

 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... 

A continuación, puede acceder a los atributos de latitud y longitud:

 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) 

O hazlo en una sola línea llamando apply dos veces:

 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) 

También su bash geolocator.geocode(lambda row: 'state_name' (row)) no hizo nada, por lo tanto, tiene una columna llena de valores None

EDITAR

@leb hace un punto interesante aquí, si tiene muchos valores duplicados, será más eficiente geocodificar para cada valor único y luego agregar esto:

 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) 

Así que lo anterior obtiene todos los valores únicos mediante el uso unique , construye un dict a partir de ellos y luego llama al map para realizar la búsqueda y agregar los acordes, esto será más eficiente que tratar de geocodificar filas.

Aumente y acepte la respuesta de @EchChum, solo quería agregar a esto. Sus métodos funcionan a la perfección, pero a partir de la experiencia personal me gustaría compartir algunas cosas:

Cuando se trata de geoencoding, si tiene varias combinaciones de ciudad / estado que se repiten, es mucho más rápido enviar solo 1 para geocodificarse y luego replicar el rest en otras filas a continuación:

Esto es muy útil para la gran cantidad de datos que se puede hacer de dos maneras:

  1. Solo en base a sus datos, ya que las filas parecen duplicados, y solo si lo desea, elimine las adicionales y ejecute la geoencoding en una de ellas. Esto se puede hacer usando drop_duplicate
  2. Si desea mantener todas sus filas, group_by la combinación ciudad / estado, aplique geoencoding a la primera llamando al head(1) , luego duplíquelo en las filas restantes.

La razón es que cada vez que llama a Nominatim hay un pequeño problema de latencia, incluso si estaba haciendo cola en la misma ciudad / estado en una fila. Esta pequeña latencia empeora cuando sus datos aumentan, lo que provoca un gran retraso en la respuesta y un posible tiempo de espera.

Una vez más, todo esto se debe a un trato personal con él. Solo tenlo en cuenta para uso futuro si no te beneficia ahora.