JSON
Índice de contenido
JSON (JavaScript Object Notation) es un formato de datos muy utilizado para representar datos estructurados. En Python, el formato JSON se almacena como un string, por ejemplo:
subject_json = '{"Asignatura": "Historia", "Aulas": ["A3", "B2"]}'
subject_json
'{"Asignatura": "Historia", "Aulas": ["A3", "B2"]}'
Librería pandas
Pandas incluye la función pd.read_json() que permite convertir una cadena en formato JSON a un objeto de pandas (una Serie o un DataFrame). Sus principales parámetros son los siguientes:
- path_or_buf es una cadena de JSON válida, un objeto de tipo archivo (que disponga de método .read()) o la ruta de un archivo.
- orient debe ser una cadena de texto indicando cómo es el formato JSON esperado. Se pueden generar cadenas de texto en formato JSON válido mediante la función to_json() de pandas con una orientación adecuada. Las posibles orientaciones son las siguientes:
- ‘split’: tipo diccionario {index -> [index], columns -> [columns], data -> [values]}
- ‘records’: tipo lista [{column -> value}, … , {column -> value}]
- ‘index’: tipo diccionario {index -> {column -> value}}
- ‘columns’: tipo diccionario {column -> {index -> value}}
- ‘values’: simplemente los valores de un vector
Los valores permitidos y predeterminados dependen del valor del parámetro typ:
- Cuando typ == ‘series’, las orientaciones permitidas son {‘split’,‘records’,‘index’}, y por defecto es ‘index’.
- Cuando typ == ‘frame’,las orientaciones permitidas son {‘split’,‘records’,‘index’, ‘columns’,‘values’, ’table’}, y por defecto es ‘columns’.
- typ es el tipo de objeto que se desea generar y puede ser uno de los dos siguientes: {‘frame’, ‘series’}. Por defecto es ‘frame’.
- convert_dates puede ser False o True (este último es el valor por defecto), o bien una lista con nombres de columnas. Si es False, no se tratará de leer ninguna columna como fecha. Si se trata de una lista de nombres de columnas, esas columnas se convertirán y, además, las columnas que aparentemente puedan hacer referencia a fechas también podrán hacerlo (según el parámetro keep_default_dates, explicado a continuación).
- keep_default_dates también es un booleano y por defecto es True. Si el parámetro convert_dates no está puesto en False, entonces trata de convertir las columnas que aparentemente puedan hacer referencia a fechas. Detecta que una columna podría ser de tipo fecha fijándose en el nombre que tiene dicha columna, en concreto fijándose en los siguientes detalles:
- El nombre termina en ‘_at’
- El nombre termina en ‘_time’
- El nombre empieza con ’timestamp’
- El nombre incluye la palabra ‘modified’
- EL nombre incluye la palabra ‘date’
- encoding es el tipo de codificación de los datos y por defecto es ‘utf-8’.
- lines es un booleano (por defecto False) y permite indicar si se quiere leer el archivo como un objeto json línea a línea.
- compression puede ser {‘infer’, ‘gzip’, ‘bz2’, ‘zip’, ‘xz’, None}, y por defecto es ‘infer’. Se utiliza para descomprimir datos sobre la marcha. Si es “infer”, utiliza gzip, bz2, zip o xz si path_or_buf es una cadena que termina en “.gz”, “.bz2”, “.zip” o “xz”, respectivamente. Si se quiere usar “zip”, el archivo ZIP debe contener solo un archivo de datos para ser leído.
- nrows es un entero que permite poner un límite de líneas a leer. Solo se puede pasar si lines = True.
import pandas as pd
Lectura de archivos en formato JSON
pd.read_json(path_or_buf = "C:/Users/user/Downloads/201906_Usage_Bicimad.zip",
encoding = "iso-8859-2",
lines = True)
| _id | user_day_code | idplug_base | user_type | idunplug_base | travel_time | idunplug_station | ageRange | idplug_station | unplug_hourTime | zip_code | track | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | {'$oid': '5cf83b752f3843a016be4e2f'} | e4d55deb9ac172a8d8f5f0a32599815bd51b7c8760d67e... | 21 | 1 | 8 | 219 | 90 | 0 | 66 | {'$date': '2019-06-01T00:00:00.000+0200'} | NaN | |
| 1 | {'$oid': '5cf83b762f3843a016be4e48'} | 8a0c4123e924a50a958f51985eb71aea750fb072438035... | 19 | 1 | 19 | 359 | 71 | 4 | 136 | {'$date': '2019-06-01T00:00:00.000+0200'} | 28039 | NaN |
| 2 | {'$oid': '5cf83b762f3843a016be4e4f'} | a6a9c1f74a68496000542210abc4fc2eba79e2756ad535... | 17 | 1 | 7 | 375 | 39 | 4 | 38 | {'$date': '2019-06-01T00:00:00.000+0200'} | 28013 | NaN |
| 3 | {'$oid': '5cf83b762f3843a016be4e53'} | 5706c0bd494acc02279d532821c9666b0e506d4f81c838... | 4 | 1 | 21 | 264 | 66 | 5 | 90 | {'$date': '2019-06-01T00:00:00.000+0200'} | 28009 | NaN |
| 4 | {'$oid': '5cf83b762f3843a016be4e54'} | eb1b6d32bd4add5d5ff91af72a38786d61075c090383a5... | 3 | 1 | 13 | 367 | 152 | 4 | 166 | {'$date': '2019-06-01T00:00:00.000+0200'} | 28006 | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 450806 | {'$oid': '5d19317fa9cd6441ac49177b'} | 2ea80cbe37fc790baa40bf26547aee6bef31b77fa3afcc... | 9 | 3 | 12 | 3293 | 83 | 4 | 90 | {'$date': '2019-06-26T23:00:00.000+0200'} | NaN | |
| 450807 | {'$oid': '5d19317fa9cd6441ac49177c'} | 97032d3747cb14430b7899cfdfdf17f4f8bb92da7ccb60... | 12 | 1 | 23 | 317 | 51 | 5 | 135 | {'$date': '2019-06-26T23:00:00.000+0200'} | 28045 | NaN |
| 450808 | {'$oid': '5d193180a9cd6441ac49177f'} | 4b8a5eabbe71df3addcff5ccdec5b7a6b49252f23edb7c... | 3 | 1 | 14 | 1558 | 172 | 0 | 174 | {'$date': '2019-06-26T23:00:00.000+0200'} | NaN | |
| 450809 | {'$oid': '5d193180a9cd6441ac491780'} | a155966b8f5dfaeaac34612d902eab89b8dc590df6b9e9... | 6 | 1 | 6 | 2199 | 35 | 0 | 2 | {'$date': '2019-06-26T23:00:00.000+0200'} | NaN | |
| 450810 | {'$oid': '5d193180a9cd6441ac491781'} | a2859c578a940a5b6f9697d37bbb60887b59668b74f4ab... | 3 | 1 | 19 | 301 | 30 | 5 | 130 | {'$date': '2019-06-26T23:00:00.000+0200'} | 28010 | NaN |
450811 rows × 12 columns
Conversión de un dataframe a JSON
df = pd.DataFrame([["a", "b"], ["c", "d"]],
index=["row 1", "row 2"],
columns=["col 1", "col 2"])
df
| col 1 | col 2 | |
|---|---|---|
| row 1 | a | b |
| row 2 | c | d |
# 'split': tipo diccionario {index -> [index], columns -> [columns], data -> [values]}
json_string1 = df.to_json(orient = "split")
json_string1
'{"columns":["col 1","col 2"],"index":["row 1","row 2"],"data":[["a","b"],["c","d"]]}'
# 'records': tipo lista [{column -> value}, ... , {column -> value}]
json_string2 = df.to_json(orient = "records")
json_string2
'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'
Carga de una cadena JSON en un dataframe
pd.read_json(json_string1, orient= "records")
| columns | index | data | |
|---|---|---|---|
| 0 | col 1 | row 1 | [a, b] |
| 1 | col 2 | row 2 | [c, d] |
pd.read_json(json_string1, orient= "split")
| col 1 | col 2 | |
|---|---|---|
| row 1 | a | b |
| row 2 | c | d |
# 'columns': tipo diccionario {column -> {index -> value}}
pd.read_json(json_string1, orient= "columns")
| columns | index | data | |
|---|---|---|---|
| 0 | col 1 | row 1 | [a, b] |
| 1 | col 2 | row 2 | [c, d] |
Módulo json de la librería estándar de Python
import json
Lectura de archivos en formato JSON
Es posible cargar un archivo en formato JSON a un diccionario de Python utilizando el método json.load():
data_path = "C:/Users/user/Desktop/201906_Usage_Bicimad.json"
data_path
'C:/Users/user/Desktop/201906_Usage_Bicimad.json'
json.load(data_path)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-d3754150bc61> in <module>
----> 1 json.load(data_path)
~\anaconda3\envs\python-385\lib\json\__init__.py in load(fp, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
291 kwarg; otherwise ``JSONDecoder`` is used.
292 """
--> 293 return loads(fp.read(),
294 cls=cls, object_hook=object_hook,
295 parse_float=parse_float, parse_int=parse_int,
AttributeError: 'str' object has no attribute 'read'
with open(data_path, "r") as f:
json.load(f)
---------------------------------------------------------------------------
JSONDecodeError Traceback (most recent call last)
<ipython-input-4-571467e78874> in <module>
1 with open(data_path, "r") as f:
----> 2 json.load(f)
~\anaconda3\envs\python-385\lib\json\__init__.py in load(fp, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
291 kwarg; otherwise ``JSONDecoder`` is used.
292 """
--> 293 return loads(fp.read(),
294 cls=cls, object_hook=object_hook,
295 parse_float=parse_float, parse_int=parse_int,
~\anaconda3\envs\python-385\lib\json\__init__.py in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
355 parse_int is None and parse_float is None and
356 parse_constant is None and object_pairs_hook is None and not kw):
--> 357 return _default_decoder.decode(s)
358 if cls is None:
359 cls = JSONDecoder
~\anaconda3\envs\python-385\lib\json\decoder.py in decode(self, s, _w)
338 end = _w(s, end).end()
339 if end != len(s):
--> 340 raise JSONDecodeError("Extra data", s, end)
341 return obj
342
JSONDecodeError: Extra data: line 2 column 1 (char 364)
rows_list = []
for line in open(data_path, "r"):
rows_list.append(json.loads(line))
rows_list[:3]
[{'_id': {'$oid': '5cf83b752f3843a016be4e2f'},
'user_day_code': 'e4d55deb9ac172a8d8f5f0a32599815bd51b7c8760d67e42b11adf7c0829341b',
'idplug_base': 21,
'user_type': 1,
'idunplug_base': 8,
'travel_time': 219,
'idunplug_station': 90,
'ageRange': 0,
'idplug_station': 66,
'unplug_hourTime': {'$date': '2019-06-01T00:00:00.000+0200'},
'zip_code': ''},
{'_id': {'$oid': '5cf83b762f3843a016be4e48'},
'user_day_code': '8a0c4123e924a50a958f51985eb71aea750fb072438035f149283490cc6bfaf4',
'idplug_base': 19,
'user_type': 1,
'idunplug_base': 19,
'travel_time': 359,
'idunplug_station': 71,
'ageRange': 4,
'idplug_station': 136,
'unplug_hourTime': {'$date': '2019-06-01T00:00:00.000+0200'},
'zip_code': '28039'},
{'_id': {'$oid': '5cf83b762f3843a016be4e4f'},
'user_day_code': 'a6a9c1f74a68496000542210abc4fc2eba79e2756ad5355a626632f7783dd418',
'idplug_base': 17,
'user_type': 1,
'idunplug_base': 7,
'travel_time': 375,
'idunplug_station': 39,
'ageRange': 4,
'idplug_station': 38,
'unplug_hourTime': {'$date': '2019-06-01T00:00:00.000+0200'},
'zip_code': '28013'}]
Conversión de un diccionario a JSON
Es posible cargar un diccionario de Python en una cadena en formato JSON utilizando el método json.dumps():
subject_dict = {"Asignatura": "Historia", "Aulas": ["A3", "B2"]}
subject_dict
{'Asignatura': 'Historia', 'Aulas': ['A3', 'B2']}
json.dumps(subject_dict)
'{"Asignatura": "Historia", "Aulas": ["A3", "B2"]}'
¿Qué pasa si introducimos, por ejemplo, una tilde?
subject_dict2 = {"Asignatura": "Matemáticas", "Aulas": ["A3", "B2"]}
subject_dict2
{'Asignatura': 'Matemáticas', 'Aulas': ['A3', 'B2']}
json.dumps(subject_dict2)
'{"Asignatura": "Matem\\u00e1ticas", "Aulas": ["A3", "B2"]}'
Por defecto, json.dumps() hace que todo el texto del diccionario de Python se interprete como codificado en ASCII. Si hay caracteres que no son ASCII, se escapan automáticamente. Para mantener los caracteres Unicode sin alterar, se puede introducir el parámetro ensure_ascii = False:
json.dumps(subject_dict2, ensure_ascii = False)
'{"Asignatura": "Matemáticas", "Aulas": ["A3", "B2"]}'
Carga de una cadena JSON en un diccionario
Es posible cargar una cadena en formato JSON a un diccionario de Python utilizando el método json.loads():
string1 = '{"Asignatura": "Historia", "Aulas": ["A3", "B2"]}'
string1
'{"Asignatura": "Historia", "Aulas": ["A3", "B2"]}'
dict1 = json.loads(string1)
dict1
{'Asignatura': 'Historia', 'Aulas': ['A3', 'B2']}
Impresión con formato
Para imprimir datos en formato JSON de forma más apropiada, se pueden pasar parámetros especiales al método json.dumps():
subject_dict = json.loads(string1)
subject_dict
{'Asignatura': 'Historia', 'Aulas': ['A3', 'B2']}
string2 = json.dumps(subject_dict, indent = 4, sort_keys = True)
print(string2)
{
"Asignatura": "Historia",
"Aulas": [
"A3",
"B2"
]
}
