2017-10-04 5 views
0

J'utilise un api FCC pour convertir les coordonnées lat/long dans les codes de groupe de blocs:Pandas et multitraitement

import pandas as pd 
import numpy as np 
import urllib 
import time 
import json 

# getup, getup1, and getup2 make up the url to the api 
getup = 'http://data.fcc.gov/api/block/find?format=json&latitude=' 

getup1 = '&longitude=' 

getup2 = '&showall=false' 

lat = ['40.7127837','34.0522342','41.8781136','29.7604267','39.9525839', 
'33.4483771','29.4241219','32.715738','32.7766642','37.3382082','30.267153', 
'39.768403','30.3321838','37.7749295','39.9611755','35.2270869', 
'32.7554883','42.331427','31.7775757','35.1495343'] 

long = ['-74.0059413','-118.2436849','-87.6297982','-95.3698028','-75.1652215', 
'-112.0740373','-98.4936282','-117.1610838','-96.7969879','-121.8863286', 
'-97.7430608','-86.158068','-81.655651','-122.4194155','-82.9987942', 
'-80.8431267','-97.3307658','-83.0457538','-106.4424559','-90.0489801'] 

#make lat and long in to a Pandas DataFrame 
latlong = pd.DataFrame([lat,long]).transpose() 
latlong.columns = ['lat','long'] 

new_list = [] 

def block(x): 
    for index,row in x.iterrows(): 
     #request url and read the output 
     a = urllib.request.urlopen(getup + row['lat'] + getup1 + row['long'] + getup2).read() 
     #load json output in to a form python can understand 
     a1 = json.loads(a) 
     #append output to an empty list. 
     new_list.append(a1['Block']['FIPS']) 

#call the function with latlong as the argument.   
block(latlong) 

#print the list, note: it is important that function appends to the list 
print(new_list) 

donne cette sortie:

['360610031001021', '060372074001033', '170318391001104', '482011000003087', 
'421010005001010', '040131141001032', '480291101002041', '060730053003011', 
'481130204003064', '060855010004004', '484530011001092', '180973910003057', 
'120310010001023', '060750201001001', '390490040001005', '371190001005000', 
'484391233002071', '261635172001069', '481410029001001', '471570042001018'] 

Le problème avec ce script est que je peux appelle seulement l'api une fois par rangée. Il faut environ 5 minutes par millier pour exécuter le script, ce qui n'est pas acceptable avec plus de 1 000 000 d'entrées que j'ai l'intention d'utiliser avec ce script.

Je souhaite utiliser le multitraitement pour mettre en parallèle cette fonction afin de réduire le temps d'exécution de la fonction. J'ai essayé de regarder dans le manuel de multitraitement, mais n'ai pas été en mesure de comprendre comment exécuter la fonction et ajouter la sortie dans une liste vide en parallèle.

Juste pour référence: J'utilise python 3.6

Toute orientation serait génial!

+0

Hé, vous voudrez peut-être regarder au [python GIL] (https://wiki.python.org/moin/GlobalInterpreterLock). Utiliser le parrallélisme en python augmente la plupart du temps le temps de calcul au lieu de le diminuer. – Tbaki

+0

Étant donné que vous êtes lié à IO, les threads ont un sens ici, vous devrez restructurer votre problème pour éviter d'être ajouté à la liste globale. Docs ici un bon endroit pour commencer - https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor-example – chrisb

+0

@Tbaki 'multiprocessing' n'est pas affecté par le GIL, en effet, il a été créé pour fournir un api 'threading'-like pour créer plusieurs processus pour * contourner * les limites de la GIL. Comme le souligne @chrisb, cependant, étant donné que ce code est lié à IO, «threading» ne sera pas non plus limité par le GIL. –

Répondre

1

Vous n'avez pas besoin d'implémenter le parallélisme vous-même, il existe des bibliothèques mieux que urllib, par ex. demandes [0] et quelques spin-offs [1] qui utilisent des threads ou des futures. Je suppose que vous devez vérifier vous-même lequel est le plus rapide.

En raison de la petite quantité de dépendances que j'aime le mieux les demandes-futures, voici mon implémentation de votre code en utilisant dix threads. La bibliothèque soutiendrait même processus si vous croyez ou comprendre qu'il est en quelque sorte mieux dans votre cas:

import pandas as pd 
import numpy as np 
import urllib 
import time 
import json 
from concurrent.futures import ThreadPoolExecutor 

from requests_futures.sessions import FuturesSession 

#getup, getup1, and getup2 make up the url to the api 
getup = 'http://data.fcc.gov/api/block/find?format=json&latitude=' 

getup1 = '&longitude=' 

getup2 = '&showall=false' 

lat = ['40.7127837','34.0522342','41.8781136','29.7604267','39.9525839', 
'33.4483771','29.4241219','32.715738','32.7766642','37.3382082','30.267153', 
'39.768403','30.3321838','37.7749295','39.9611755','35.2270869', 
'32.7554883','42.331427','31.7775757','35.1495343'] 

long = ['-74.0059413','-118.2436849','-87.6297982','-95.3698028','-75.1652215', 
'-112.0740373','-98.4936282','-117.1610838','-96.7969879','-121.8863286', 
'-97.7430608','-86.158068','-81.655651','-122.4194155','-82.9987942', 
'-80.8431267','-97.3307658','-83.0457538','-106.4424559','-90.0489801'] 

#make lat and long in to a Pandas DataFrame 
latlong = pd.DataFrame([lat,long]).transpose() 
latlong.columns = ['lat','long'] 

def block(x): 
    requests = [] 
    session = FuturesSession(executor=ThreadPoolExecutor(max_workers=10)) 
    for index, row in x.iterrows(): 
     #request url and read the output 
     url = getup+row['lat']+getup1+row['long']+getup2   
     requests.append(session.get(url)) 
    new_list = [] 
    for request in requests: 
     #load json output in to a form python can understand 
     a1 = json.loads(request.result().content) 
     #append output to an empty list. 
     new_list.append(a1['Block']['FIPS']) 
    return new_list 

#call the function with latlong as the argument.   
new_list = block(latlong) 

#print the list, note: it is important that function appends to the list 
print(new_list) 

[0] http://docs.python-requests.org/en/master/

[1] https://github.com/kennethreitz/grequests

+0

Cela a fonctionné très bien! Je suis passé de 5 minutes par mille à 1 minute par mille. –

+0

Alors je serais heureux si vous acceptez ma réponse :) – mkastner