Tôi có một DataFrame lớn với cột địa chỉ:
địa chỉ dữ liệu
0 0.617964 IN,Krisnagiri,635115
1 0.635428 IN, Chennai,600005
2 0.630125 IN,Karnal,132001
3 0.981282 IN,Jaipur,302021
4 0.715813 IN, Chennai,600005
...
Và tôi đã viết hàm sau để thay thế địa chỉ bằng tọa độ kinh độ và vĩ độ của địa chỉ:
từ geopy.geocologists nhập Nominatim
geo_locator = Đề cử(user_agent="MY_APP_ID")
def get_coordines(addr):
vị trí = geo_locator.geocode(addr)
nếu vị trí không phải là Không có:
return pd.Series({'lat': location.latitude, 'lon': location.longitude})
vị trí = geo_locator.geocode(addr.split(',')[0])
nếu vị trí không phải là Không có:
return pd.Series({'lat': location.latitude, 'lon': location.longitude})
return pd.Series({'lat': -1, 'lon': -1})
Sau đó gọi phương thức áp dụng gấu trúc trên cột địa chỉ và nối kết quả vào cuối DF thay vì cột địa chỉ:
df = pd.concat([df, df.addr.apply(get_coordines)], axis=1).drop(['addr'], axis=1)
Tuy nhiên, vì get_coordine gọi API của bên thứ 3 nên không thành công:geopy.exc.GeocodingTimedOut:Đã hết thời gian chờ dịch vụ
Làm cách nào tôi có thể giới hạn yêu cầu để đảm bảo tôi nhận được phản hồi trước khi chuyển sang giá trị tiếp theo?
gia hạn:
Để cải thiện hơn nữa, tôi chỉ muốn gọi API cho các giá trị duy nhất, tức là: nếu địa chỉ IN,Krishnagiri,635115
Có 20 lần xuất hiện trong DataFrame của tôi và tôi chỉ muốn gọi nó một lần và áp dụng kết quả của tất cả 20 lần xuất hiện.
更新 2:
Nhật ký + dấu vết ngăn xếp, mã @Andrew Lavers:
...
Fetched Gandipet, Khanapur, Quận Rangareddy, Telangana, 500075, Ấn Độ
Tổng công ty thành phố Fetched Jaipur, Jaipur, Rajasthan, 302015, Ấn Độ
Fetched Chennai, quận Chennai, Tamil Nadu, Ấn Độ
Ngoại lệ từ công cụ định vị địa lý: Ngoại lệ giả để thử nghiệm
Lùi lại trong 1 giây.
Ngoại lệ từ công cụ định vị địa lý: Ngoại lệ giả để thử nghiệm
Lùi lại trong 3 giây.
Đã tìm nạp Không có
Traceback (most recent call last):
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/geopy/geochers/base.py", dòng 344, trong _call_geocoding
trang = người yêu cầu(req, timeout=timeout, **kwargs)
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/urllib/request.py", dòng 526, đang mở
phản hồi = self._open(req, data)
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/urllib/request.py", dòng 544, trong _open
'_open', yêu cầu)
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/urllib/request.py", dòng 504, trong _call_chain
kết quả = func(*args)
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/urllib/request.py", dòng 1361, trong https_open
bối cảnh=self._context, check_hostname=self._check_hostname)
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/urllib/request.py", dòng 1321, trong do_open
r = h.getresponse()
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/http/client.py", dòng 1331, trong getresponse
phản hồi.begin()
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/http/client.py", dòng 297, bắt đầu
phiên bản, trạng thái, lý do = self._read_status()
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/http/client.py", dòng 258, trong _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py", dòng 586, ở dạng readinto
return self._sock.recv_into(b)
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ssl.py", dòng 1002, trong recv_into
trả về self.read(nbyte, bộ đệm)
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ssl.py", dòng 865, ở dạng đọc
trả về self._sslobj.read(len, bộ đệm)
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ssl.py", dòng 625, ở dạng đọc
v = self._sslobj.read(len, bộ đệm)
socket.timeout: Đã hết thời gian đọc
Trong quá trình xử lý ngoại lệ trên, một ngoại lệ khác đã xảy ra:
Traceback (most recent call last):
Tệp "/Users/...//tmp.py", dòng 89, trong
df.addr.apply(get_coordines)
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pandas/core/series.py", dòng 3194, được áp dụng
ánh xạ = lib.map_infer(giá trị, f, chuyển đổi=convert_dtype)
Tệp "pandas/_libs/src/inference.pyx", dòng 1472, trong pandas._libs.lib.map_infer
Tệp "/Users/...//tmp.py", dòng 76, trong get_coordine
vị trí = geo_locator.geocode(addr.split(',')[0])
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/geopy/geochers/osm.py", dòng 307, ở dạng mã địa lý
self._call_geocoding(url, timeout=timeout), chính xác_one
Tệp "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/geopy/geocodings/base.py", dòng 371, trong _call_geocoding
raise GeocodingTimedOut('Đã hết thời gian chờ dịch vụ')
geopy.exc.GeocodingTimedOut: Đã hết thời gian chờ dịch vụ
Quá trình kết thúc với mã thoát 1
đây là một sốđã thử nghiệmMã có thể giúp đỡ. 1) Giới hạn tốc độ đơn giản được chỉ định trên API (Nominatum có vẻ là 1 mỗi giây, nhưng tôi đã cố gắng đạt được mức thấp nhất là 0,1 giây). 2) Bộ nhớ đệm kết quả đơn giản trong từ điển, có thể điều khiển thông qua các tham số kiểm tra 3) Thử lại vòng lặp với tính năng làm chậm theo cấp số nhân và tăng tốc tuyến tính. (Giảm tốc nhanh, tăng tốc chậm) 4) Ngoại lệ kiểm tra sai giả
Tôi không thể tái hiện vấn đề bạn đang gặp phải - có thể là do đường dẫn API của bạn.
Một chiến lược mạnh mẽ hơn có thể là xây dựng bộ nhớ đệm liên tục cục bộ và tiếp tục thử lại cho đến khi toàn bộ lô được tạo. Bộ đệm có thể là khung dữ liệu gấu trúc được ghi vào tệp dưới dạng csv. tổng thểmã giảThế thôi.
lặp lại cho đến khi tất cả địa chỉ đều nằm trong bộ đệm
bộ đệm = pd.read_csv("cache.csv)
addresss_to_get = địa chỉ trong df không có trong bộ đệm
cho lô n địa chỉ trong địa chỉ_to_get:
cache.add(get_location(addr))
cache.write_csv("cache.csv")
Đây là mã kiểm tra
nhập ngày giờ
import time
nhập gấu trúc dưới dạng pd
từ geopy.geocologists nhập Nominatim
geo_locator = Nominatim(user_agent="notarealemail@gmail.com")
# Xác định hàm giới hạn tốc độ và biến toàn cục liên quan
Last_time = datetime.datetime.now()
thời gian lùi lại = 0
tỷ lệ def_limit(min_interval_seconds = .1):
toàn cầu lần cuối cùng
ngủ = min_interval_seconds - (datetime.datetime.now() - Last_time).total_seconds()
nếu ngủ> 0:
print(f'Ngủ trong {ngủ} giây')
time.sleep(ngủ)
Last_time = datetime.datetime.now()
# tạo một từ điển bộ đệm được khóa theo địa chỉ
geo_cache = {}
backoff_seconds = 0
def get_coordines_with_retry(addr):
# Trả về các dây từ bộ đệm chung nếu nó tồn tại
backoff_seconds toàn cầu
# đặt các hệ số và giá trị backoff ban đầu
max_backoff_seconds = 60
backoff_exponential=2
backoff_tuyến tính = 2
# lệnh gọi API có giới hạn tốc độ
rate_limit()
# Thử lại cho đến khi đạt max_back_giây
while backoff_seconds < max_backoff_seconds: # lùi lại cho đến thời điểm này
nếu backoff_seconds > 0:
print(f"Đang lùi trong {backoff_seconds} giây.")
time.sleep(backoff_seconds)
thử:
vị trí = geo_locator.geocode(addr)
# LOẠI BỎ ĐIỀU NÀY: giả mạo lỗi để kiểm tra
#nhập ngẫu nhiên
#if ngẫu nhiên.random() < .3:
# raise(Ngoại lệ("Ngoại lệ giả để kiểm tra"))
# Thành công - do đó giảm thời gian chờ một cách tuyến tính
print (f"Đã tìm nạp {location} cho địa chỉ {addr}")
backoff_seconds = backoff_seconds - backoff_Tuyến tính nếu backoff_seconds > backoff_Tuyến tính khác 0
break
ngoại trừ Ngoại lệ là e:
print(f"Ngoại lệ từ công cụ định vị địa lý: {e}")
#Backoff theo cấp số nhân
backoff_seconds = 1 + backoff_seconds * backoff_exponential
nếu backoff_seconds > max_backoff_seconds:
tăng ngoại lệ("Đạt mức chờ tối đa\n")
trở lại (địa điểm)
def get_coordines(addr, useCache = True):
# Trở về từ bộ đệm nếu được tải trước đó
bộ đệm địa lý toàn cầu
nếu addr trong Geo_cache:
trả về geo_cache[addr]
# Cố gắng sử dụng địa chỉ đầy đủ
vị trí = get_coordines_with_retry(addr)
# Chỉ thử sử dụng phần đầu tiên nếu Không tìm thấy
nếu vị trí không phải là Không có:
result = pd.Series({'lat': location.latitude, 'lon': location.longitude})
khác :
print (f"Đang thử tách địa chỉ cho địa chỉ {addr}")
location = get_coordines_with_retry(addr.split(',')[0])
nếu vị trí không phải là Không có:
result = pd.Series({'lat': location.latitude, 'lon': location.longitude})
khác:
result = pd.Series({'lat': -1, 'lon': -1})
# gán vào bộ đệm
nếu sử dụngCache:
geo_cache[addr] = kết quả
trở lại (kết quả)
# Sử dụng dữ liệu thử nghiệm
df = pd.DataFrame({'addr' : [
'IN,Krishnagiri,635115',
'IN, Chennai, 600005',
'IN,Karnal,132001',
'IN, Jaipur, 302021',
'IN,Chennai,600005']})
# lặp lại dữ liệu thử nghiệm để tạo tập hợp lớn hơn
df = pd.concat([df, df, df, df, df, df, df, df, df, df])
df.addr.apply(get_coordines)
print(f"Bộ đệm địa chỉ chứa {len(geo_cache)} vị trí địa chỉ.")
Tôi là một lập trình viên xuất sắc, rất giỏi!