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

Ou flatbuffers, MessagePack

[ ]:

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.

[ ]:


Notebook on github