JSON à pandas DataFrame


144

Ce que j'essaie de faire, c'est d'extraire les données d'altitude d'une API google maps le long d'un chemin spécifié par les coordonnées de latitude et de longitude comme suit:

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

Cela me donne une donnée qui ressemble à ceci:

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

lors de la mise en tant que DataFrame, voici ce que j'obtiens:

entrez la description de l'image ici

pd.read_json(elevations)

et voici ce que je veux:

entrez la description de l'image ici

Je ne sais pas si cela est possible, mais ce que je recherche principalement, c'est un moyen de pouvoir rassembler les données d'élévation, de latitude et de longitude dans un cadre de données pandas (il n'est pas nécessaire d'avoir des en-têtes mutilines fantaisie).

Si quelqu'un peut aider ou donner des conseils sur l'utilisation de ces données, ce serait formidable! Si vous ne pouvez pas dire que je n'ai pas beaucoup travaillé avec les données json auparavant ...

ÉDITER:

Cette méthode n'est pas très attrayante mais semble fonctionner:

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

finit par dataframe avec des colonnes latitude, longitude, altitude

entrez la description de l'image ici


Bonjour ami, savez-vous comment obtenir un morceau de json? une sous-partie?
M. Mariscal le

Réponses:


185

J'ai trouvé une solution rapide et facile à ce que je voulais en utilisant json_normalize()inclus 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'])

Cela donne une belle trame de données aplatie avec les données json que j'ai obtenues de l'API Google Maps.


13
Cela ne semble plus fonctionner - j'ai dû utiliser pd.DataFrame.from_records()comme décrit ici stackoverflow.com/a/33020669/1137803
avv

4
from_records ne fonctionne pas non plus parfois si le json est suffisamment complexe, vous devez appliquer json.io.json.json_normalize pour obtenir un flatmap Consultez stackoverflow.com/questions/39899005/…
devssh

27

Regardez ce snip.

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

J'espère que ça aide :)


1
Erreur. Vous devez transmettre le contenu du fichier (c'est-à-dire une chaîne) à json.loads (), pas à l'objet fichier lui-même - json.load (train_file.read ())
Vasin Yuriy

13

Vous pouvez d'abord importer vos données json dans un dictionnaire Python:

data = json.loads(elevations)

Puis modifiez les données à la volée:

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

Reconstruire la chaîne json:

elevations = json.dumps(data)

Finalement :

pd.read_json(elevations)

Vous pouvez, aussi, probablement éviter de vider les données en une chaîne, je suppose que Panda peut créer directement une trame de données à partir d'un dictionnaire (je ne l'ai pas utilisé depuis longtemps: p)


Je finis toujours avec le même résultat en utilisant les données json et le dictionnaire qui a été créé. Il semble que chaque élément dans la trame de données a sa propre dict. J'ai essayé d'utiliser votre approche d'une manière moins attrayante en créant une liste séparée pour la latitude, la longueur et l'élévation tout en itérant dans les `` données ''.
pbreach

@ user2593236: Bonjour, j'ai fait une erreur en copiant / collant mon code dans SO: il manquait un del (réponse modifiée)
Raphaël Braud

Hmm .. Toujours la même chose où il a «résultats» et «statut» comme en-têtes tandis que le reste des données json apparaît comme dicts dans chaque cellule. Je pense que la solution à ce problème serait de changer le format des données afin qu'elles ne soient pas subdivisées en `` résultats '' et `` statut '', puis la trame de données utilisera les `` lat '', `` lng '', `` élévation '', `` résolution 'comme en-têtes séparés. Soit ça, ou je vais devoir trouver un moyen de charger les données JSON dans une trame de données qui ont un indice d' en- tête à plusieurs niveaux comme je l' ai mentionné dans la question.
pbreach le

À quelle table finale vous attendez-vous? Celui que vous avez obtenu après votre montage?
Raphaël Braud le

Celui que j'ai obtenu après ma dernière modification fait le travail, en gros, tout ce dont j'avais besoin était d'obtenir les données dans un format tabulaire que je peux exporter et travailler avec
pbreach

9

Juste une nouvelle version de la réponse acceptée, comme python3.xne prend pas en chargeurllib2

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

4

Le problème est que vous avez plusieurs colonnes dans le bloc de données qui contiennent des dicts avec des dicts plus petits à l'intérieur. Utile Json est souvent fortement imbriqué. J'ai écrit de petites fonctions qui tirent les informations que je veux dans une nouvelle colonne. De cette façon, je l'ai dans le format que je veux utiliser.

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

4

Optimisation de la réponse acceptée:

La réponse acceptée a quelques problèmes de fonctionnement, je souhaite donc partager mon code qui ne repose pas sur 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)

Production:

        _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: l'API est pour les prix de l'électricité au Danemark


3

Voici une petite classe utilitaire qui convertit JSON en DataFrame et inversement: j'espère que vous trouverez cela utile.

# -*- 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)

1

La solution de billmanH m'a aidé, mais ne fonctionnait pas jusqu'à ce que je suis passé de:

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

à:

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

voici le reste, la conversion en dictionnaire est utile pour travailler avec des données 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'])

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


1

Une fois que vous avez l'aplatissement DataFrameobtenu par la réponse acceptée, vous pouvez faire des colonnes un MultiIndex("en-tête multiligne fantaisie") comme ceci:

df.columns = pd.MultiIndex.from_tuples([tuple(c.split('.')) for c in df.columns])
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.