JSON para pandas DataFrame

143

O que estou tentando fazer é extrair dados de elevação de uma API do google maps ao longo de um caminho especificado pelas coordenadas de latitude e longitude da seguinte maneira:

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()

Isso me dá um dado parecido com este:

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"',
 '}']

ao colocar como DataFrame, aqui está o que recebo:

insira a descrição da imagem aqui

pd.read_json(elevations)

e aqui está o que eu quero:

insira a descrição da imagem aqui

Não tenho certeza se isso é possível, mas principalmente o que estou procurando é uma maneira de reunir os dados de elevação, latitude e longitude em um dataframe de pandas (não precisa ter cabeçalhos de mutilina sofisticados).

Se alguém puder ajudar ou dar alguns conselhos sobre como trabalhar com esses dados, isso seria ótimo! Se você não pode dizer que eu não trabalhei muito com dados json antes ...

EDITAR:

Este método não é tão atraente, mas 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 o dataframe com colunas latitude, longitude, elevação

insira a descrição da imagem aqui

pregar
fonte
Olá amigo, você sabe como conseguir um pedaço de json? alguma subparte?
M. Mariscal

Respostas:

184

Encontrei uma solução rápida e fácil para o que eu queria usar json_normalize()incluído 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'])

Isso fornece um bom quadro de dados nivelado com os dados json que obtive da API do Google Maps.

pregar
fonte
12
Isto já não parece funcionar - Eu tive que usar pd.DataFrame.from_records()como descrito aqui stackoverflow.com/a/33020669/1137803
avv
4
from_records também não funciona às vezes se o json é suficientemente complexo, você deve aplicar json.io.json.json_normalize para obter um mapa plano Verifique stackoverflow.com/questions/39899005/…
devssh
27

Confira 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 que ajude :)

Rishu
fonte
1
Erro. Você deve passar o conteúdo do arquivo (ou seja, uma string) para json.loads (), e não o próprio objeto de arquivo - json.load (train_file.read ())
Vasin Yuriy
13

Você pode primeiro importar seus dados json em um dicionário Python:

data = json.loads(elevations)

Em seguida, modifique os dados rapidamente:

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']

Reconstrua a sequência json:

elevations = json.dumps(data)

Finalmente :

pd.read_json(elevations)

Você também pode evitar despejar dados de volta para uma string. Suponho que o Panda possa criar diretamente um DataFrame a partir de um dicionário (não o uso há muito tempo: p)

Raphaël Braud
fonte
Eu ainda acabo com o mesmo resultado usando os dados json e o dicionário que foi criado. Parece que cada elemento no quadro de dados tem seu próprio ditado. Tentei usar sua abordagem de uma maneira menos atraente, criando uma lista separada para lat, lng e elevação enquanto iterava através de 'dados'.
pbreach
@ user2593236: Olá, eu fiz um erro ao copiar / colar meu código no SO: a del faltava (resposta editado)
Raphaël Braud
Hmm .. Ainda é a mesma coisa em que tem 'resultados' e 'status' como cabeçalhos, enquanto o restante dos dados json aparece como dict em cada célula. Eu acho que a solução para esse problema seria alterar o formato dos dados para que não sejam subdivididos em 'resultados' e 'status'; em seguida, o quadro de dados usará 'lat', 'lng', 'elevação', ' resolução 'como cabeçalhos separados. Ou isso, ou precisarei encontrar uma maneira de carregar os dados do json em um dataframe que terá um índice de cabeçalho multinível, como mencionei na pergunta.
pbreach
Qual mesa final você espera? O que você recebeu após a sua edição?
Raphaël Braud
O que eu recebi após a edição final faz o trabalho, basicamente tudo que eu precisava era obter os dados em um formato tabular com o qual eu posso exportar e trabalhar
#
9

Apenas uma nova versão da resposta aceita, pois python3.xnão suportaurllib2

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
fonte
4

O problema é que você tem várias colunas no quadro de dados que contêm dictos com dictos menores dentro deles. Json útil geralmente é muito aninhado. Escrevi pequenas funções que puxam as informações que desejo para uma nova coluna. Dessa forma, eu tenho no formato que eu quero 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
fonte
4

Otimização da resposta aceita:

A resposta aceita tem alguns problemas de funcionamento, por isso quero compartilhar meu código que não depende do 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)

Resultado:

        _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 

PS: API é para preços de eletricidade dinamarqueses

DisabledWhale
fonte
3

Aqui está uma pequena classe de utilitário que converte JSON em DataFrame e vice-versa: Espero que você ache isso ú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
fonte
1

A solução do billmanH me ajudou, mas não funcionou até que eu mudei de:

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

para:

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

aqui está o resto, converter para um dicionário é útil para trabalhar com dados 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'])
niltoide
fonte
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
fonte
1

Depois de obter o nivelamento DataFrameobtido pela resposta aceita, você pode transformar as colunas em um MultiIndex("cabeçalho multilinha sofisticado") como este:

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