JSON zu Pandas DataFrame


143

Ich versuche, Höhendaten aus einer Google Maps-API entlang eines Pfads zu extrahieren, der durch Breiten- und Längengradkoordinaten wie folgt angegeben wird:

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

Dies gibt mir Daten, die so aussehen:

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

Wenn ich als DataFrame einsetze, bekomme ich Folgendes:

Geben Sie hier die Bildbeschreibung ein

pd.read_json(elevations)

und hier ist was ich will:

Geben Sie hier die Bildbeschreibung ein

Ich bin mir nicht sicher, ob dies möglich ist, aber ich suche hauptsächlich nach einer Möglichkeit, die Höhen-, Breiten- und Längengrade in einem Pandas-Datenrahmen zusammenzufassen (muss keine ausgefallenen mutilinen Header haben).

Wenn jemand helfen oder Ratschläge zum Umgang mit diesen Daten geben kann, wäre das großartig! Wenn Sie nicht sagen können, dass ich vorher nicht viel mit JSON-Daten gearbeitet habe ...

BEARBEITEN:

Diese Methode ist nicht besonders attraktiv, scheint aber zu funktionieren:

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

endet Datenrahmen mit Spalten Breite, Länge, Höhe

Geben Sie hier die Bildbeschreibung ein


Hallo Freund, weißt du, wie man ein Stück Json bekommt? irgendein Unterabschnitt?
M. Mariscal

Antworten:


184

Ich fand eine schnelle und einfache Lösung zu dem, was ich wollte Verwendung json_normalize()in inbegriffen 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'])

Dies ergibt einen schönen abgeflachten Datenrahmen mit den JSON-Daten, die ich von der Google Maps-API erhalten habe.


12
Dies scheint nicht mehr zu funktionieren - ich musste pd.DataFrame.from_records()wie hier beschrieben stackoverflow.com/a/33020669/1137803
avv

4
from_records funktioniert auch manchmal nicht, wenn der json ausreichend komplex ist. Sie müssen json.io.json.json_normalize anwenden, um eine Flatmap zu erhalten. Check out stackoverflow.com/questions/39899005/…
devssh

27

Überprüfen Sie diesen Ausschnitt.

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

Ich hoffe es hilft :)


1
Error. Sie sollten den Dateiinhalt (dh eine Zeichenfolge) an json.loads () übergeben, nicht an das Dateiobjekt selbst - json.load (train_file.read ())
Vasin Yuriy

13

Sie können Ihre JSON-Daten zunächst in ein Python-Wörterbuch importieren:

data = json.loads(elevations)

Ändern Sie dann die Daten im laufenden Betrieb:

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

Json-String neu erstellen:

elevations = json.dumps(data)

Endlich :

pd.read_json(elevations)

Sie können wahrscheinlich auch vermeiden, Daten in einen String zurückzugeben. Ich gehe davon aus, dass Panda einen DataFrame direkt aus einem Wörterbuch erstellen kann (ich habe ihn seit langer Zeit nicht mehr verwendet: p).


Ich erhalte immer noch das gleiche Ergebnis mit den JSON-Daten und dem erstellten Wörterbuch. Es scheint, als hätte jedes Element im Datenrahmen ein eigenes Diktat. Ich habe versucht, Ihren Ansatz auf weniger attraktive Weise zu verwenden und eine separate Liste für Lat, Lng und Elevation zu erstellen, während ich durch 'Daten' iterierte.
pbreach

@ user2593236: Hallo, ich habe einen Fehler beim Kopieren / Einfügen meines Codes in SO gemacht: Ein Del fehlte (Antwort bearbeitet)
Raphaël Braud

Hmm .. Immer noch dasselbe, wo es "Ergebnisse" und "Status" als Überschriften hat, während der Rest der JSON-Daten als Diktate in jeder Zelle angezeigt wird. Ich denke, die Lösung für dieses Problem wäre, das Format der Daten so zu ändern, dass sie nicht in "Ergebnisse" und "Status" unterteilt werden. Dann verwendet der Datenrahmen "lat", "lng", "height", " Auflösung 'als separate Überschriften. Entweder das, oder ich muss einen Weg finden, um die JSON-Daten in einen Datenrahmen zu laden, der einen mehrstufigen Header-Index hat, wie ich in der Frage erwähnt habe.
pbreach

Welchen Final Table erwarten Sie? Die, die du nach deiner Bearbeitung bekommen hast?
Raphaël Braud


9

Nur eine neue Version der akzeptierten Antwort, da python3.xnicht unterstützturllib2

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

Das Problem ist, dass der Datenrahmen mehrere Spalten enthält, die Diktate mit kleineren Diktaten enthalten. Nützlicher Json ist oft stark verschachtelt. Ich habe kleine Funktionen geschrieben, die die gewünschten Informationen in eine neue Spalte ziehen. Auf diese Weise habe ich es in dem Format, das ich verwenden möchte.

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

Optimierung der akzeptierten Antwort:

Die akzeptierte Antwort weist einige Funktionsprobleme auf. Daher möchte ich meinen Code freigeben, der nicht auf urllib2 basiert:

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)

Ausgabe:

        _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 ist für dänische Strompreise


3

Hier ist eine kleine Dienstprogrammklasse, die JSON in DataFrame und zurück konvertiert: Ich hoffe, Sie finden dies hilfreich.

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

Die Lösung von billmanH hat mir geholfen, hat aber erst funktioniert, als ich von:

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

zu:

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

Hier ist der Rest: Die Konvertierung in ein Wörterbuch ist hilfreich für die Arbeit mit JSON-Daten.

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

Sobald Sie die DataFramedurch die akzeptierte Antwort erhaltene Abflachung erhalten haben, können Sie die Spalten MultiIndexwie folgt zu einem ("ausgefallenen mehrzeiligen Header") machen:

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

0

Eine mögliche Alternative zu pandas.json_normalize besteht darin, einen eigenen Datenrahmen zu erstellen, indem nur die ausgewählten Schlüssel und Werte aus dem verschachtelten Wörterbuch extrahiert werden. Der Hauptgrund dafür ist, dass json_normalize für sehr große json-Dateien langsam wird (und möglicherweise nicht immer die gewünschte Ausgabe erzeugt).

Hier ist eine alternative Möglichkeit, das verschachtelte Wörterbuch in Pandas mit glom zu reduzieren. Ziel ist es, ausgewählte Schlüssel und Werte aus dem verschachtelten Wörterbuch zu extrahieren und in einer separaten Spalte des Pandas-Datenrahmens zu speichern (:

Hier ist eine Schritt-für-Schritt-Anleitung: https://medium.com/@enrico.alemani/flatten-nested-dictionaries-in-pandas-using-glom-7948345c88f5

import pandas as pd
from glom import glom
from ast import literal_eval


target = {
    "page": 10,
    "total": 100,
    "data":
        {
            "total_time": 100,
            "href": "https://mylink.com",
            "ip_address": "000.00.00.00",
            "id": "012345",
            "response_status": "completed",
        }
}

# Import data
df = pd.DataFrame([str(target)], columns=['target'])

# Extract id keys and save value into a separate pandas column
df['id'] = df['target'].apply(lambda row: glom(literal_eval(row), 'data.id'))
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.