JSON a pandas DataFrame

144

Lo que intento hacer es extraer datos de elevación de una API de Google Maps a lo largo de una ruta especificada por las coordenadas de latitud y longitud de la siguiente manera:

from urllib2 import Request, urlopen
import json

path1 = '42.974049,-81.205203|42.974298,-81.195755'
request=Request('http://maps.googleapis.com/maps/api/elevation/json?locations='+path1+'&sensor=false')
response = urlopen(request)
elevations = response.read()

Esto me da una información que se ve así:

elevations.splitlines()

['{',
 '   "results" : [',
 '      {',
 '         "elevation" : 243.3462677001953,',
 '         "location" : {',
 '            "lat" : 42.974049,',
 '            "lng" : -81.205203',
 '         },',
 '         "resolution" : 19.08790397644043',
 '      },',
 '      {',
 '         "elevation" : 244.1318664550781,',
 '         "location" : {',
 '            "lat" : 42.974298,',
 '            "lng" : -81.19575500000001',
 '         },',
 '         "resolution" : 19.08790397644043',
 '      }',
 '   ],',
 '   "status" : "OK"',
 '}']

cuando lo pongo como DataFrame aquí es lo que obtengo:

ingrese la descripción de la imagen aquí

pd.read_json(elevations)

y esto es lo que quiero:

ingrese la descripción de la imagen aquí

No estoy seguro de si esto es posible, pero principalmente lo que estoy buscando es una forma de poder juntar los datos de elevación, latitud y longitud en un marco de datos de pandas (no tiene que tener encabezados de mutilina sofisticados).

Si alguien puede ayudar o dar algún consejo sobre cómo trabajar con estos datos, ¡sería genial! Si no puede decir que no he trabajado mucho con datos json antes ...

EDITAR:

Este método no es tan atractivo pero parece funcionar:

data = json.loads(elevations)
lat,lng,el = [],[],[]
for result in data['results']:
    lat.append(result[u'location'][u'lat'])
    lng.append(result[u'location'][u'lng'])
    el.append(result[u'elevation'])
df = pd.DataFrame([lat,lng,el]).T

termina el marco de datos que tiene columnas de latitud, longitud, elevación

ingrese la descripción de la imagen aquí

pbreach
fuente
Hola amigo, ¿sabes cómo conseguir un pedazo de json? alguna subparte?
M. Mariscal

Respuestas:

185

Encontré una solución rápida y fácil para lo que quería usar json_normalize()incluida pandas 1.01.

from urllib2 import Request, urlopen
import json

import pandas as pd    

path1 = '42.974049,-81.205203|42.974298,-81.195755'
request=Request('http://maps.googleapis.com/maps/api/elevation/json?locations='+path1+'&sensor=false')
response = urlopen(request)
elevations = response.read()
data = json.loads(elevations)
df = pd.json_normalize(data['results'])

Esto proporciona un marco de datos plano y agradable con los datos json que obtuve de la API de Google Maps.

pbreach
fuente
13
Esto ya no parece funcionar: tuve que usar pd.DataFrame.from_records()como se describe aquí stackoverflow.com/a/33020669/1137803
avv
44
from_records tampoco funciona a veces si json es suficientemente complejo, debe aplicar json.io.json.json_normalize para obtener un mapa plano. Consulte stackoverflow.com/questions/39899005/…
devssh
27

Mira este recorte.

# reading the JSON data using json.load()
file = 'data.json'
with open(file) as train_file:
    dict_train = json.load(train_file)

# converting json dataset from dictionary to dataframe
train = pd.DataFrame.from_dict(dict_train, orient='index')
train.reset_index(level=0, inplace=True)

Espero eso ayude :)

Rishu
fuente
1
Error. Debe pasar el contenido del archivo (es decir, una cadena) a json.loads (), no el objeto del archivo en sí - json.load (train_file.read ())
Vasin Yuriy
13

Primero puede importar sus datos json en un diccionario Python:

data = json.loads(elevations)

Luego modifique los datos sobre la marcha:

for result in data['results']:
    result[u'lat']=result[u'location'][u'lat']
    result[u'lng']=result[u'location'][u'lng']
    del result[u'location']

Reconstruir cadena json:

elevations = json.dumps(data)

Finalmente :

pd.read_json(elevations)

Probablemente, también puede evitar volcar datos en una cadena, supongo que Panda puede crear directamente un DataFrame desde un diccionario (no lo he usado desde hace mucho tiempo: p)

Raphaël Braud
fuente
Todavía termino con el mismo resultado usando los datos json y el diccionario que se creó. Parece que cada elemento en el marco de datos tiene su propio dict. Intenté usar su enfoque de una manera menos atractiva construyendo una lista separada para lat, lng y elevación mientras iteraba a través de 'datos'.
pbreach
@ user2593236: Hola, cometí un error al copiar / pegar mi código en SO: faltaba un del (respuesta editada)
Raphaël Braud
Hmm ... Sigue siendo lo mismo donde tiene 'resultados' y 'estado' como encabezados mientras que el resto de los datos json aparecen como dictados en cada celda. Creo que la solución a este problema sería cambiar el formato de los datos para que no se subdivida en 'resultados' y 'estado', entonces el marco de datos usará 'lat', 'lng', 'elevación', ' resolución 'como los encabezados separados. O eso, o tendré que encontrar una manera de cargar los datos json en un marco de datos que tendrá un índice de encabezado multinivel como mencioné en la pregunta.
pbreach
¿Qué mesa final esperas? ¿El que obtuviste después de tu edición?
Raphaël Braud
La que me dieron después de mi edición final hace el trabajo, básicamente todo lo que necesitaba era obtener los datos en forma de tabla que puedo exportar y trabajar con
pbreach
9

Solo una nueva versión de la respuesta aceptada, ya python3.xque no admiteurllib2

from requests import request
import json
from pandas.io.json import json_normalize

path1 = '42.974049,-81.205203|42.974298,-81.195755'
response=request(url='http://maps.googleapis.com/maps/api/elevation/json?locations='+path1+'&sensor=false', method='get')
elevations = response.json()
elevations
data = json.loads(elevations)
json_normalize(data['results'])
AB Abhi
fuente
4

El problema es que tiene varias columnas en el marco de datos que contienen dictos con dictos más pequeños dentro de ellos. Json útil a menudo está muy anidado. He estado escribiendo pequeñas funciones que extraen la información que quiero en una nueva columna. De esa manera lo tengo en el formato que quiero usar.

for row in range(len(data)):
    #First I load the dict (one at a time)
    n = data.loc[row,'dict_column']
    #Now I make a new column that pulls out the data that I want.
    data.loc[row,'new_column'] = n.get('key')
billmanH
fuente
4

Optimización de la respuesta aceptada:

La respuesta aceptada tiene algunos problemas de funcionamiento, por lo que quiero compartir mi código que no se basa en urllib2:

import requests
from pandas.io.json import json_normalize
url = 'https://www.energidataservice.dk/proxy/api/datastore_search?resource_id=nordpoolmarket&limit=5'

r = requests.get(url)
dictr = r.json()
recs = dictr['result']['records']
df = json_normalize(recs)
print(df)

Salida:

        _id                    HourUTC               HourDK  ... ElbasAveragePriceEUR  ElbasMaxPriceEUR  ElbasMinPriceEUR
0    264028  2019-01-01T00:00:00+00:00  2019-01-01T01:00:00  ...                  NaN               NaN               NaN
1    138428  2017-09-03T15:00:00+00:00  2017-09-03T17:00:00  ...                33.28              33.4              32.0
2    138429  2017-09-03T16:00:00+00:00  2017-09-03T18:00:00  ...                35.20              35.7              34.9
3    138430  2017-09-03T17:00:00+00:00  2017-09-03T19:00:00  ...                37.50              37.8              37.3
4    138431  2017-09-03T18:00:00+00:00  2017-09-03T20:00:00  ...                39.65              42.9              35.3
..      ...                        ...                  ...  ...                  ...               ...               ...
995  139290  2017-10-09T13:00:00+00:00  2017-10-09T15:00:00  ...                38.40              38.4              38.4
996  139291  2017-10-09T14:00:00+00:00  2017-10-09T16:00:00  ...                41.90              44.3              33.9
997  139292  2017-10-09T15:00:00+00:00  2017-10-09T17:00:00  ...                46.26              49.5              41.4
998  139293  2017-10-09T16:00:00+00:00  2017-10-09T18:00:00  ...                56.22              58.5              49.1
999  139294  2017-10-09T17:00:00+00:00  2017-10-09T19:00:00  ...                56.71              65.4              42.2 

PD: API es para precios de electricidad daneses

DisabledWhale
fuente
3

Aquí hay una pequeña clase de utilidad que convierte JSON a DataFrame y viceversa: Espero que esto sea útil.

# -*- coding: utf-8 -*-
from pandas.io.json import json_normalize

class DFConverter:

    #Converts the input JSON to a DataFrame
    def convertToDF(self,dfJSON):
        return(json_normalize(dfJSON))

    #Converts the input DataFrame to JSON 
    def convertToJSON(self, df):
        resultJSON = df.to_json(orient='records')
        return(resultJSON)
Siva
fuente
1

La solución de billmanH me ayudó pero no funcionó hasta que cambié de:

n = data.loc[row,'json_column']

a:

n = data.iloc[[row]]['json_column']

aquí está el resto, la conversión a un diccionario es útil para trabajar con datos json.

import json

for row in range(len(data)):
    n = data.iloc[[row]]['json_column'].item()
    jsonDict = json.loads(n)
    if ('mykey' in jsonDict):
        display(jsonDict['mykey'])
niltoideo
fuente
1
#Use the small trick to make the data json interpret-able
#Since your data is not directly interpreted by json.loads()

>>> import json
>>> f=open("sampledata.txt","r+")
>>> data = f.read()
>>> for x in data.split("\n"):
...     strlist = "["+x+"]"
...     datalist=json.loads(strlist)
...     for y in datalist:
...             print(type(y))
...             print(y)
...
...
<type 'dict'>
{u'0': [[10.8, 36.0], {u'10': 0, u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}
<type 'dict'>
{u'1': [[10.8, 36.1], {u'10': 0, u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}
<type 'dict'>
{u'2': [[10.8, 36.2], {u'10': 0, u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}
<type 'dict'>
{u'3': [[10.8, 36.300000000000004], {u'10': 0, u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}
<type 'dict'>
{u'4': [[10.8, 36.4], {u'10': 0, u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}
<type 'dict'>
{u'5': [[10.8, 36.5], {u'10': 0, u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}
<type 'dict'>
{u'6': [[10.8, 36.6], {u'10': 0, u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}
<type 'dict'>
{u'7': [[10.8, 36.7], {u'10': 0, u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}
<type 'dict'>
{u'8': [[10.8, 36.800000000000004], {u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}
<type 'dict'>
{u'9': [[10.8, 36.9], {u'1': 0, u'0': 0, u'3': 0, u'2': 0, u'5': 0, u'4': 0, u'7': 0, u'6': 0, u'9': 0, u'8': 0}]}

MIKHIL NAGARALE
fuente
1

Una vez que haya DataFrameobtenido el aplanado obtenido por la respuesta aceptada, puede hacer que las columnas a MultiIndex("encabezado multilínea elegante") así:

df.columns = pd.MultiIndex.from_tuples([tuple(c.split('.')) for c in df.columns])
loganbvh
fuente