web-dev-qa-db-fra.com

Lecture de plusieurs enregistrements JSON dans un Pandas dataframe

Je voudrais savoir s'il existe un moyen efficace de mémoire pour lire un fichier JSON à enregistrements multiples (chaque ligne est un dicton JSON) dans un cadre de données pandas. Ci-dessous, un exemple de 2 lignes avec une solution de travail , J'en ai besoin pour un très grand nombre d'enregistrements. L'exemple d'utilisation serait de traiter la sortie de la fonction Hadoop Pig JSonStorage.

import json
import pandas as pd

test='''{"a":1,"b":2}
{"a":3,"b":4}'''
#df=pd.read_json(test,orient='records') doesn't work, expects []

l=[ json.loads(l) for l in test.splitlines()]
df=pd.DataFrame(l)
41
seanv507

Remarque: json séparé par des lignes est désormais pris en charge dans read_json (depuis 0.19.0):

In [31]: pd.read_json('{"a":1,"b":2}\n{"a":3,"b":4}', lines=True)
Out[31]:
   a  b
0  1  2
1  3  4

ou avec un fichier/chemin d'accès au fichier plutôt qu'une chaîne json:

pd.read_json(json_file, lines=True)

Cela dépendra de la taille de vos DataFrames, ce qui est plus rapide, mais une autre option consiste à utiliser str.join pour briser votre "JSON" multi-lignes (Remarque: ce n'est pas un json valide), en json valide et utilisez read_json:

In [11]: '[%s]' % ','.join(test.splitlines())
Out[11]: '[{"a":1,"b":2},{"a":3,"b":4}]'

Pour ce petit exemple, c'est plus lent, si environ 100 c'est le même, des gains significatifs si c'est plus grand ...

In [21]: %timeit pd.read_json('[%s]' % ','.join(test.splitlines()))
1000 loops, best of 3: 977 µs per loop

In [22]: %timeit l=[ json.loads(l) for l in test.splitlines()]; df = pd.DataFrame(l)
1000 loops, best of 3: 282 µs per loop

In [23]: test_100 = '\n'.join([test] * 100)

In [24]: %timeit pd.read_json('[%s]' % ','.join(test_100.splitlines()))
1000 loops, best of 3: 1.25 ms per loop

In [25]: %timeit l = [json.loads(l) for l in test_100.splitlines()]; df = pd.DataFrame(l)
1000 loops, best of 3: 1.25 ms per loop

In [26]: test_1000 = '\n'.join([test] * 1000)

In [27]: %timeit l = [json.loads(l) for l in test_1000.splitlines()]; df = pd.DataFrame(l)
100 loops, best of 3: 9.78 ms per loop

In [28]: %timeit pd.read_json('[%s]' % ','.join(test_1000.splitlines()))
100 loops, best of 3: 3.36 ms per loop

Remarque: à cette époque, la jointure est étonnamment rapide.

45
Andy Hayden

Si vous essayez d'économiser de la mémoire, la lecture du fichier ligne par ligne sera beaucoup plus efficace en mémoire:

with open('test.json') as f:
    data = pd.DataFrame(json.loads(line) for line in f)

De plus, si vous import simplejson as json, les extensions C compilées incluses avec simplejson sont beaucoup plus rapides que le module pur-Python json.

27
Doctor J

Au Pandas 0,19, read_json a un support natif pour JSON délimité par des lignes :

pd.read_json(jsonfile, lines=True)
14
Doctor J

++++++++ Update +++++++++++++++

À partir de v0.19, Pandas prend en charge cela nativement (voir https://github.com/pandas-dev/pandas/pull/13351 ). Il suffit d'exécuter:

df=pd.read_json('test.json', lines=True)

++++++++ Ancienne réponse ++++++++++

Les réponses existantes sont bonnes, mais pour un peu de variété, voici une autre façon d'atteindre votre objectif qui nécessite une simple étape de prétraitement en dehors de python pour que pd.read_json() puisse consommer les données.

  • Installez jq https://stedolan.github.io/jq/ .
  • Créez un fichier json valide avec cat test.json | jq -c --Slurp . > valid_test.json
  • Créer une trame de données avec df=pd.read_json('valid_test.json')

Dans le bloc-notes ipython, vous pouvez exécuter la commande Shell directement à partir de l'interface de cellule avec

!cat test.json | jq -c --Slurp . > valid_test.json
df=pd.read_json('valid_test.json')
2
Bob Baxley