JSON - XML#
Transmettre l’information d’une machine à une autre, d’un logiciel à un autre, d’une base de données à une autre est un problème récurrent. Le format le plus simple pour des données est le format csv. Ca marche bien pour les tables mais cela ne permet de transmettre aisément des données non structurées.
Enoncé#
[1]:
from sklearn.datasets import load_iris as load_data
from pandas import DataFrame
data = load_data()
df = DataFrame(data.data, columns=data.feature_names)
df["fleur"] = [data.target_names[t] for t in data.target]
df.tail()
[1]:
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | fleur | |
---|---|---|---|---|---|
145 | 6.7 | 3.0 | 5.2 | 2.3 | virginica |
146 | 6.3 | 2.5 | 5.0 | 1.9 | virginica |
147 | 6.5 | 3.0 | 5.2 | 2.0 | virginica |
148 | 6.2 | 3.4 | 5.4 | 2.3 | virginica |
149 | 5.9 | 3.0 | 5.1 | 1.8 | virginica |
Q1 : écriture des données au format CSV#
Le plus simple…
[2]:
from io import StringIO
buffer = StringIO()
df.to_csv(buffer, index=False)
text = buffer.getvalue()
text[:300]
[2]:
'sepal length (cm),sepal width (cm),petal length (cm),petal width (cm),fleur\n5.1,3.5,1.4,0.2,setosa\n4.9,3.0,1.4,0.2,setosa\n4.7,3.2,1.3,0.2,setosa\n4.6,3.1,1.5,0.2,setosa\n5.0,3.6,1.4,0.2,setosa\n5.4,3.9,1.7,0.4,setosa\n4.6,3.4,1.4,0.3,setosa\n5.0,3.4,1.5,0.2,setosa\n4.4,2.9,1.4,0.2,setosa\n4.9,3.1,1.5,0.1,s'
[ ]:
Q2 : écriture des données au format JSON#
[3]:
r = df.to_json(orient="records")
r[:400]
[3]:
'[{"sepal length (cm)":5.1,"sepal width (cm)":3.5,"petal length (cm)":1.4,"petal width (cm)":0.2,"fleur":"setosa"},{"sepal length (cm)":4.9,"sepal width (cm)":3.0,"petal length (cm)":1.4,"petal width (cm)":0.2,"fleur":"setosa"},{"sepal length (cm)":4.7,"sepal width (cm)":3.2,"petal length (cm)":1.3,"petal width (cm)":0.2,"fleur":"setosa"},{"sepal length (cm)":4.6,"sepal width (cm)":3.1,"petal lengt'
[ ]:
Q3 : relire les données avec le module json#
[ ]:
[ ]:
Q4 : essayez avec les format XML (ou HTML), SQL, SAS, Excel…#
[ ]:
Q5 : données non structurées#
On ajoute les endroits où ces fleurs sont présentes. On voudrait que toutes les informations soient présentes dans le même fichier. Comment fait-on ?
[4]:
locations = {
"virginica": ["Florida", "Georgia"],
"setosa": ["Maine", "Alaska", "Quebec"],
"versicolor": ["Quebec", "Georgia", "Ireland", "Main"],
}
La question sous-jacente est : vaut-il mieux avoir deux fichiers plats, l’un pour les données décrivant les fleurs, l’autre pour les localisations ou un seul fusionnant les deux informations ?
[ ]:
Q6 : le texte, ça prend trop de place, zippons#
Avec le module zipfile.
[ ]:
Q7 : que vous inspire protobuf ?#
[ ]:
Réponses#
Q1 : écriture des données au format CSV#
[5]:
from io import StringIO
buffer = StringIO()
df.to_csv(buffer, index=False)
text = buffer.getvalue()
text[:300]
[5]:
'sepal length (cm),sepal width (cm),petal length (cm),petal width (cm),fleur\n5.1,3.5,1.4,0.2,setosa\n4.9,3.0,1.4,0.2,setosa\n4.7,3.2,1.3,0.2,setosa\n4.6,3.1,1.5,0.2,setosa\n5.0,3.6,1.4,0.2,setosa\n5.4,3.9,1.7,0.4,setosa\n4.6,3.4,1.4,0.3,setosa\n5.0,3.4,1.5,0.2,setosa\n4.4,2.9,1.4,0.2,setosa\n4.9,3.1,1.5,0.1,s'
[6]:
df.to_csv("fleurs.csv", index=False)
[7]:
import os
os.listdir(".")
[7]:
['coloriage_carre.ipynb',
'histogramme_rapide.ipynb',
'dummy.json',
'exercice_json_xml.ipynb',
'exercice_regex.ipynb',
'tableau_contingence.ipynb',
'nbheap.ipynb',
'recherche_dichotomique.ipynb',
'classes_2048.ipynb',
'profile_gini.ipynb',
'pivot_gauss.ipynb',
'profiling_example.ipynb',
'classes_metro.ipynb',
'exercice_pi.ipynb',
'code_multinomial.ipynb',
'images',
'code_liste_tuple.ipynb',
'tri_nlnd.ipynb',
'fleurs.csv',
'structures_donnees_conversion.ipynb',
'exercice_serialisation_json.ipynb']
[8]:
import pandas
df2 = pandas.read_csv("fleurs.csv")
[9]:
df2.head()
[9]:
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | fleur | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
[10]:
virtuel = StringIO(text)
df3 = pandas.read_csv(virtuel)
df3.head()
[10]:
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | fleur | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
Q2 : écriture des données au format JSON#
[11]:
json_text = df.to_json(orient="records")
json_text[:400]
[11]:
'[{"sepal length (cm)":5.1,"sepal width (cm)":3.5,"petal length (cm)":1.4,"petal width (cm)":0.2,"fleur":"setosa"},{"sepal length (cm)":4.9,"sepal width (cm)":3.0,"petal length (cm)":1.4,"petal width (cm)":0.2,"fleur":"setosa"},{"sepal length (cm)":4.7,"sepal width (cm)":3.2,"petal length (cm)":1.3,"petal width (cm)":0.2,"fleur":"setosa"},{"sepal length (cm)":4.6,"sepal width (cm)":3.1,"petal lengt'
Q3 : relire les données avec le module json#
[12]:
import json
[13]:
res = json.loads(json_text)
[14]:
for i, r in enumerate(res):
print(i, type(r), r)
if i >= 5:
break
0 <class 'dict'> {'sepal length (cm)': 5.1, 'sepal width (cm)': 3.5, 'petal length (cm)': 1.4, 'petal width (cm)': 0.2, 'fleur': 'setosa'}
1 <class 'dict'> {'sepal length (cm)': 4.9, 'sepal width (cm)': 3.0, 'petal length (cm)': 1.4, 'petal width (cm)': 0.2, 'fleur': 'setosa'}
2 <class 'dict'> {'sepal length (cm)': 4.7, 'sepal width (cm)': 3.2, 'petal length (cm)': 1.3, 'petal width (cm)': 0.2, 'fleur': 'setosa'}
3 <class 'dict'> {'sepal length (cm)': 4.6, 'sepal width (cm)': 3.1, 'petal length (cm)': 1.5, 'petal width (cm)': 0.2, 'fleur': 'setosa'}
4 <class 'dict'> {'sepal length (cm)': 5.0, 'sepal width (cm)': 3.6, 'petal length (cm)': 1.4, 'petal width (cm)': 0.2, 'fleur': 'setosa'}
5 <class 'dict'> {'sepal length (cm)': 5.4, 'sepal width (cm)': 3.9, 'petal length (cm)': 1.7, 'petal width (cm)': 0.4, 'fleur': 'setosa'}
[15]:
res[3]["sepal width (cm)"]
[15]:
3.1
[16]:
virtuel = StringIO(json_text)
res2 = json.load(virtuel)
res2[:3]
[16]:
[{'sepal length (cm)': 5.1,
'sepal width (cm)': 3.5,
'petal length (cm)': 1.4,
'petal width (cm)': 0.2,
'fleur': 'setosa'},
{'sepal length (cm)': 4.9,
'sepal width (cm)': 3.0,
'petal length (cm)': 1.4,
'petal width (cm)': 0.2,
'fleur': 'setosa'},
{'sepal length (cm)': 4.7,
'sepal width (cm)': 3.2,
'petal length (cm)': 1.3,
'petal width (cm)': 0.2,
'fleur': 'setosa'}]
Q4 : essayez avec les format XML (ou HTML), SQL, SAS, Excel…#
[17]:
html_text = df.to_html(index=False)
[18]:
print(html_text[:500])
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th>sepal length (cm)</th>
<th>sepal width (cm)</th>
<th>petal length (cm)</th>
<th>petal width (cm)</th>
<th>fleur</th>
</tr>
</thead>
<tbody>
<tr>
<td>5.1</td>
<td>3.5</td>
<td>1.4</td>
<td>0.2</td>
<td>setosa</td>
</tr>
<tr>
<td>4.9</td>
<td>3.0</td>
<td>1.4</td>
<td>0.2</td>
<td>setosa</td>
</tr>
[19]:
df_html = pandas.read_html(html_text)
df_html[0].tail()
[19]:
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | fleur | |
---|---|---|---|---|---|
145 | 6.7 | 3.0 | 5.2 | 2.3 | virginica |
146 | 6.3 | 2.5 | 5.0 | 1.9 | virginica |
147 | 6.5 | 3.0 | 5.2 | 2.0 | virginica |
148 | 6.2 | 3.4 | 5.4 | 2.3 | virginica |
149 | 5.9 | 3.0 | 5.1 | 1.8 | virginica |
[20]:
df_html = pandas.read_html(html_text + html_text)
len(df_html)
[20]:
2
Q5 : données non structurées#
[21]:
df.head()
[21]:
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | fleur | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
[22]:
locations = {
"virginica": ["Florida", "Georgia"],
"setosa": ["Maine", "Alaska", "Quebec"],
"versicolor": ["Quebec", "Georgia", "Ireland", "Main"],
}
[23]:
obs = []
for fleur, loc in locations.items():
for l in loc:
obs.append({"fleur": fleur, "location": l})
obs
[23]:
[{'fleur': 'virginica', 'location': 'Florida'},
{'fleur': 'virginica', 'location': 'Georgia'},
{'fleur': 'setosa', 'location': 'Maine'},
{'fleur': 'setosa', 'location': 'Alaska'},
{'fleur': 'setosa', 'location': 'Quebec'},
{'fleur': 'versicolor', 'location': 'Quebec'},
{'fleur': 'versicolor', 'location': 'Georgia'},
{'fleur': 'versicolor', 'location': 'Ireland'},
{'fleur': 'versicolor', 'location': 'Main'}]
[24]:
df_locations = pandas.DataFrame(obs)
df_locations
[24]:
fleur | location | |
---|---|---|
0 | virginica | Florida |
1 | virginica | Georgia |
2 | setosa | Maine |
3 | setosa | Alaska |
4 | setosa | Quebec |
5 | versicolor | Quebec |
6 | versicolor | Georgia |
7 | versicolor | Ireland |
8 | versicolor | Main |
[25]:
merged = df.merge(df_locations, left_on="fleur", right_on="fleur")
merged.head(10)
[25]:
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | fleur | location | |
---|---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa | Maine |
1 | 5.1 | 3.5 | 1.4 | 0.2 | setosa | Alaska |
2 | 5.1 | 3.5 | 1.4 | 0.2 | setosa | Quebec |
3 | 4.9 | 3.0 | 1.4 | 0.2 | setosa | Maine |
4 | 4.9 | 3.0 | 1.4 | 0.2 | setosa | Alaska |
5 | 4.9 | 3.0 | 1.4 | 0.2 | setosa | Quebec |
6 | 4.7 | 3.2 | 1.3 | 0.2 | setosa | Maine |
7 | 4.7 | 3.2 | 1.3 | 0.2 | setosa | Alaska |
8 | 4.7 | 3.2 | 1.3 | 0.2 | setosa | Quebec |
9 | 4.6 | 3.1 | 1.5 | 0.2 | setosa | Maine |
[26]:
merged.shape
[26]:
(450, 6)
[27]:
locations
[27]:
{'virginica': ['Florida', 'Georgia'],
'setosa': ['Maine', 'Alaska', 'Quebec'],
'versicolor': ['Quebec', 'Georgia', 'Ireland', 'Main']}
[28]:
obs2 = []
for fleur, loc in locations.items():
obs2.append({"fleur": fleur, "location": loc})
obs2
[28]:
[{'fleur': 'virginica', 'location': ['Florida', 'Georgia']},
{'fleur': 'setosa', 'location': ['Maine', 'Alaska', 'Quebec']},
{'fleur': 'versicolor', 'location': ['Quebec', 'Georgia', 'Ireland', 'Main']}]
[29]:
df_locations2 = pandas.DataFrame(obs2)
df_locations2
[29]:
fleur | location | |
---|---|---|
0 | virginica | [Florida, Georgia] |
1 | setosa | [Maine, Alaska, Quebec] |
2 | versicolor | [Quebec, Georgia, Ireland, Main] |
[30]:
merged = df.merge(df_locations2, left_on="fleur", right_on="fleur")
merged.head(10)
[30]:
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | fleur | location | |
---|---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa | [Maine, Alaska, Quebec] |
1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa | [Maine, Alaska, Quebec] |
2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa | [Maine, Alaska, Quebec] |
3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa | [Maine, Alaska, Quebec] |
4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa | [Maine, Alaska, Quebec] |
5 | 5.4 | 3.9 | 1.7 | 0.4 | setosa | [Maine, Alaska, Quebec] |
6 | 4.6 | 3.4 | 1.4 | 0.3 | setosa | [Maine, Alaska, Quebec] |
7 | 5.0 | 3.4 | 1.5 | 0.2 | setosa | [Maine, Alaska, Quebec] |
8 | 4.4 | 2.9 | 1.4 | 0.2 | setosa | [Maine, Alaska, Quebec] |
9 | 4.9 | 3.1 | 1.5 | 0.1 | setosa | [Maine, Alaska, Quebec] |
[31]:
json_text = merged.to_json(orient="records")
json_text[:200]
[31]:
'[{"sepal length (cm)":5.1,"sepal width (cm)":3.5,"petal length (cm)":1.4,"petal width (cm)":0.2,"fleur":"setosa","location":["Maine","Alaska","Quebec"]},{"sepal length (cm)":4.9,"sepal width (cm)":3.0'
[32]:
df.to_excel("data.xlsx", index=False)
[33]:
dfe = pandas.read_excel("data.xlsx", engine="openpyxl")
dfe.tail()
[33]:
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | fleur | |
---|---|---|---|---|---|
145 | 6.7 | 3.0 | 5.2 | 2.3 | virginica |
146 | 6.3 | 2.5 | 5.0 | 1.9 | virginica |
147 | 6.5 | 3.0 | 5.2 | 2.0 | virginica |
148 | 6.2 | 3.4 | 5.4 | 2.3 | virginica |
149 | 5.9 | 3.0 | 5.1 | 1.8 | virginica |
Q6 : le texte, ça prend trop de place, zippons#
[1]:
from zipfile import ZipFile
with ZipFile("data.zip", "w") as myzip:
myzip.write("data.xlsx")
myzip.write("fleurs.csv")
The Kernel crashed while executing code in the the current cell or a previous cell. Please review the code in the cell(s) to identify a possible cause of the failure. Click <a href='https://aka.ms/vscodeJupyterKernelCrash'>here</a> for more info. View Jupyter <a href='command:jupyter.viewOutput'>log</a> for further details.
[ ]:
import glob
glob.glob("*.zip")
Q7 : que vous inspire protobuf ?#
Protobuf est un format de sérialisation au même titre que pickle. La sérialisation désigne un procédé qui permet d’enregister tout un tas d’information sous la forme d’ensemble d’octets contigü. En gros, on enregistre un ensemble de variables en même quel qu’il soit, dans un unique fichier contigü. json est un format utilisé par les outils de sérialisation. Tout est en texte et la plupart du temps, les nombres réelles prennent moins de place quand ils sont stockés comme ils sont sockés en mémoire sur 8 octets. C’est le premier problème résolu par protobuf. Ensuite, le format json est très générique mais il suggère de stocker le nom des colonnes à chaque ligne, c’est une information qui est dupliqué à chaque ligne… protobuf propose une façon de ne pas les sotcker du tout. Je passe les détails. Ils reviendront quand le problème de communiquer efficacement des données se posera.
[ ]: