2014-07-20 7 views
5

Provo a leggere un'Openstreetmaps API stringa JSON, che è valida.'Expected String o Unicode' durante la lettura di JSON con Pandas

Sto usando seguente codice:

--------------------------------------------------------------------------- 
TypeError         Traceback (most recent call last) 
<ipython-input-66-304b7fbfb645> in <module>() 
----> 1 osmdataframe = pd.read_json(osmdata) 

/Users/paul/anaconda/lib/python2.7/site-packages/pandas/io/json.pyc in read_json(path_or_buf, orient, typ, dtype, convert_axes, convert_dates, keep_default_dates, numpy, precise_float, date_unit) 
    196   obj = FrameParser(json, orient, dtype, convert_axes, convert_dates, 
    197       keep_default_dates, numpy, precise_float, 
--> 198       date_unit).parse() 
    199 
    200  if typ == 'series' or obj is None: 

/Users/paul/anaconda/lib/python2.7/site-packages/pandas/io/json.pyc in parse(self) 
    264 
    265   else: 
--> 266    self._parse_no_numpy() 
    267 
    268   if self.obj is None: 

/Users/paul/anaconda/lib/python2.7/site-packages/pandas/io/json.pyc in _parse_no_numpy(self) 
    481   if orient == "columns": 
    482    self.obj = DataFrame(
--> 483     loads(json, precise_float=self.precise_float), dtype=None) 
    484   elif orient == "split": 
    485    decoded = dict((str(k), v) 

TypeError: Expected String or Unicode 

come modificare la richiesta o Panda read_json, per evitare un errore:

import pandas as pd 
import requests 

# Links unten 
minLat = 50.9549 
minLon = 13.55232 

# Rechts oben 
maxLat = 51.1390 
maxLon = 13.89873 

osmrequest = {'data': '[out:json][timeout:25];(node["highway"="bus_stop"](%s,%s,%s,%s););out body;>;out skel qt;' % (minLat, minLon, maxLat, maxLon)} 
osmurl = 'http://overpass-api.de/api/interpreter' 
osm = requests.get(osmurl, params=osmrequest) 

osmdata = osm.json() 

osmdataframe = pd.read_json(osmdata) 

che getta l'errore seguente? A proposito, qual è il problema?

risposta

11

Se si stampa la stringa JSON in un file,

content = osm.read() 
with open('/tmp/out', 'w') as f: 
    f.write(content) 

vedrete qualcosa di simile:

{ 
    "version": 0.6, 
    "generator": "Overpass API", 
    "osm3s": { 
    "timestamp_osm_base": "2014-07-20T07:52:02Z", 
    "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 
    }, 
    "elements": [ 

{ 
    "type": "node", 
    "id": 536694, 
    "lat": 50.9849256, 
    "lon": 13.6821776, 
    "tags": { 
    "highway": "bus_stop", 
    "name": "Niederhäslich Bergmannsweg" 
    } 
}, 
...]} 

Se la stringa JSON dovesse essere convertito in un oggetto Python, sarebbe un dict la cui chiave elements è una lista di dict. La stragrande maggioranza dei dati è all'interno di questo elenco di dicts.

Questa stringa JSON non è direttamente convertibile in un oggetto Panda. Quale sarebbe l'indice e quali sarebbero le colonne? Sicuramente non vuoi che [u'elements', u'version', u'osm3s', u'generator'] siano le colonne, dal momento che quasi tutte le informazioni sono nella lista elements.

Ma se si desidera che il dataframe consistere dei dati solo nelle elements lista-di-dicts, allora dovresti specificare che, dal momento che Panda non può fare quel presupposto per voi.

Ulteriori complicazioni sono che ogni dict in elements è un dettato nidificato. Consideriamo la prima dict in elements:

{ 
    "type": "node", 
    "id": 536694, 
    "lat": 50.9849256, 
    "lon": 13.6821776, 
    "tags": { 
    "highway": "bus_stop", 
    "name": "Niederhäslich Bergmannsweg" 
    } 
} 

Qualora ['lat', 'lon', 'type', 'id', 'tags'] essere le colonne? Sembra plausibile, tranne per il fatto che la colonna tags finirà per essere una colonna di dict. Di solito non è molto utile. Sarebbe meglio se le chiavi all'interno del dict tags fossero trasformate in colonne. Possiamo farlo, ma ancora una volta dobbiamo codificarcelo da quando Pandas non ha modo di sapere che è quello che vogliamo.


import pandas as pd 
import requests 
# Links unten 
minLat = 50.9549 
minLon = 13.55232 

# Rechts oben 
maxLat = 51.1390 
maxLon = 13.89873 

osmrequest = {'data': '[out:json][timeout:25];(node["highway"="bus_stop"](%s,%s,%s,%s););out body;>;out skel qt;' % (minLat, minLon, maxLat, maxLon)} 
osmurl = 'http://overpass-api.de/api/interpreter' 
osm = requests.get(osmurl, params=osmrequest) 

osmdata = osm.json() 
osmdata = osmdata['elements'] 
for dct in osmdata: 
    for key, val in dct['tags'].iteritems(): 
     dct[key] = val 
    del dct['tags'] 

osmdataframe = pd.DataFrame(osmdata) 
print(osmdataframe[['lat', 'lon', 'name']].head()) 

rendimenti

  lat  lon      name 
0 50.984926 13.682178 Niederhäslich Bergmannsweg 
1 51.123623 13.782789    Sagarder Weg 
2 51.065752 13.895734  Weißig, Einkaufszentrum 
3 51.007140 13.698498   Stuttgarter Straße 
4 51.010199 13.701411   Heilbronner Straße 
+1

grande spiegazione !! Lo capisco perfettamente, ma non ho potuto risolvere l'enigma per conto mio. Grazie! Nella riga 'osmdata = json.loads (osm.read())' potrebbe essere qualcosa di non corretto, perché ottengo: 'AttributeError: L'oggetto 'Response' non ha attributo 'read'' – Balzer82

+0

Deve essere' osmdata = json.loads (osm.content) ' – Balzer82

+0

@ Balzer82: la mia versione delle richieste era troppo vecchia; l'API è cambiata da allora. Ho aggiornato con 'requests' e modificherò il codice sopra. – unutbu