#!/usr/bin/env python
# -*- coding: utf-8 -*-
# # 2A.data - DataFrame et Graphes - correction
#
# Opérations standards sur les dataframes ([pandas](http://pandas.pydata.org/)) et les matrices ([numpy](http://www.numpy.org/)). Graphiques avec [matplotlib](https://matplotlib.org/)).
# In[1]:
get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib.pyplot as plt
# In[2]:
from jyquickhelper import add_notebook_menu
add_notebook_menu()
#
Exercice 1 : créer un fichier Excel
#
# On souhaite récupérer les données [donnees_enquete_2003_television.txt](http://www.xavierdupre.fr/enseignement/complements/donnees_enquete_2003_television.txt) (source : [INSEE](http://www.insee.fr/fr/themes/detail.asp?ref_id=fd-hdv03&page=fichiers_detail/HDV03/telechargement.htm)).
#
# * ``POIDSLOG`` : Pondération individuelle relative
# * ``POIDSF`` : Variable de pondération individuelle
# * ``cLT1FREQ`` : Nombre d'heures en moyenne passées à regarder la télévision
# * ``cLT2FREQ`` : Unité de temps utilisée pour compter le nombre d'heures passées à regarder la télévision, cette unité est représentée par les quatre valeurs suivantes
# * 0 : non concerné
# * 1 : jour
# * 2 : semaine
# * 3 : mois
#
# Ensuite, on veut :
#
# 1. Supprimer les colonnes vides
# 2. Obtenir les valeurs distinctes pour la colonne ``cLT2FREQ``
# 3. Modifier la matrice pour enlever les lignes pour lesquelles l'unité de temps (cLT2FREQ) n'est pas renseignée ou égale à zéro.
# 4. Sauver le résultat au format Excel.
#
# Vous aurez peut-être besoin des fonctions suivantes :
#
# * [numpy.isnan](http://docs.scipy.org/doc/numpy/reference/generated/numpy.isnan.html)
# * [DataFrame.apply](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.apply.html)
# * [DataFrame.fillna](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.fillna.html) ou
# [DataFrame.isnull](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.isnull.html)
# * [DataFrame.copy](http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.copy.html)
# In[3]:
import pandas
from ensae_teaching_cs.data import donnees_enquete_2003_television
df = pandas.read_csv(donnees_enquete_2003_television(), sep="\t", engine="python")
df.head()
# On enlève les colonnes vides :
# In[4]:
df = df [[ c for c in df.columns if "Unnamed" not in c]]
df.head()
# In[5]:
notnull = df [ ~df.cLT2FREQ.isnull() ] # équivalent ) df [ df.cLT2FREQ.notnull() ]
print(len(df),len(notnull))
notnull.tail()
# In[6]:
notnull.to_excel("data.xlsx") # question 4
# Pour lancer Excel, vous pouvez juste écrire ceci :
# In[7]:
get_ipython().run_line_magic('system', '"data.xlsx"')
# Vous devriez voir quelque chose comme ceci :
# In[8]:
from IPython.display import Image
Image("td10exc.png")
# Questions
#
# * **Que changerait l'ajout du paramètre ``how='outer'`` dans ce cas ?**
# * **On cherche à joindre deux tables A,B qui ont chacune trois clés distinctes : $c_1, c_2, c_3$. Il y a respectivement dans chaque table $A_i$ et $B_i$ lignes pour la clé $c_i$. Combien la table finale issue de la fusion des deux tables contiendra-t-elle de lignes ?**
# L'ajout du paramètres ``how='outer'`` ne changerait rien dans ce cas car les deux tables fusionnées contiennent exactement les mêmes clés.
#
# Le nombre de lignes obtenus serait $\sum_{i=1}^{3} A_i B_i$. Il y a trois clés, chaque ligne de la table A doit être associée à toutes les lignes de la table B partageant la même clé.
# Exercice 2 : lambda fonction
#
# Ecrire une lambda fonction qui prend deux paramètres et qui est équivalente à la fonction suivante :
# In[9]:
def delta(x,y):
return max(x,y)- min(x,y)
# In[10]:
delta = lambda x,y : max(x,y)- min(x,y)
# In[11]:
delta(4,5)
# In[12]:
import random
df["select"]= df.apply( lambda row : random.randint(1,10), axis=1)
echantillon = df [ df["select"] ==1 ]
echantillon.shape, df.shape
# Exercice 3 : moyennes par groupes
#
# Toujours avec le même jeu de données ([marathon.txt](http://www.xavierdupre.fr/enseignement/complements/marathon.txt)), on veut ajouter une ligne à la fin du tableau croisé dynamique contenant la moyenne en secondes des temps des marathons pour chaque ville.
# In[13]:
from ensae_teaching_cs.data import marathon
import pandas
df = pandas.read_csv(marathon(), sep="\t", names=["ville", "annee", "temps","secondes"])
df.head()
# La solution requiert trois étapes.
#
# 1. Pour avoir la moyenne par villes, il faut grouper les lignes associées à la même villes.
# 2. Ensuite, il faut introduire ces moyennes dans la table initiale : on fusionne.
# 3. On effectue le même pivot que dans l'énoncé.
# In[14]:
# étape 1
# par défaut, la méthode groupby utilise la clé de group comme index
# pour ne pas le faire, il faut préciser as_index = False
gr = df[["ville","secondes"]].groupby("ville", as_index=False).mean()
gr.head()
# In[15]:
# étape 2 - on ajoute une colonne
tout = df.merge( gr, on="ville")
tout.head()
# In[16]:
# étape 3
piv = tout.pivot("annee","ville","secondes_x")
piv.tail()
# A partir de là, on ne voit pas trop comment s'en sortir. Voici ce que je propose :
#
# 1. On effectue un pivot sur la petite matrice des moyennes.
# 2. On ajoute ce second pivot avec le premier (celui de l'énoncé).
# In[17]:
gr["annee"] = "moyenne"
pivmean = gr.pivot("annee","ville","secondes")
pivmean
# In[18]:
piv = df.pivot("annee","ville","secondes")
pandas.concat( [ piv, pivmean ]).tail()
# En résumé, cela donne (j'ajoute aussi le nombre de marathons courus) :
# In[19]:
import pandas, urllib.request
from ensae_teaching_cs.data import marathon
df = pandas.read_csv(marathon(filename=True),
sep="\t", names=["ville", "annee", "temps","secondes"])
piv = df.pivot("annee","ville","secondes")
gr = df[["ville","secondes"]].groupby("ville", as_index=False).mean()
gr["annee"] = "moyenne"
pivmean = gr.pivot("annee","ville","secondes")
pandas.concat([piv, pivmean]).tail()
# Exercice 4 : écart entre les mariés
#
# 1. En ajoutant une colonne et en utilisant l'opération [group by](http://pandas.pydata.org/pandas-docs/stable/groupby.html), on veut obtenir la distribution du nombre de mariages en fonction de l'écart entre les mariés. Au besoin, on changera le type d'une colone ou deux.
# 2. On veut tracer un nuage de points avec en abscisse l'âge du mari, en ordonnée, l'âge de la femme. Il faudra peut-être jeter un coup d'oeil sur la documentation de la méthode [plot](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.plot.html).
# In[20]:
import urllib.request
import zipfile
import http.client
def download_and_save(name, root_url):
try:
response = urllib.request.urlopen(root_url+name)
except (TimeoutError, urllib.request.URLError, http.client.BadStatusLine):
# back up plan
root_url = "http://www.xavierdupre.fr/enseignement/complements/"
response = urllib.request.urlopen(root_url+name)
with open(name, "wb") as outfile:
outfile.write(response.read())
def unzip(name):
with zipfile.ZipFile(name, "r") as z:
z.extractall(".")
filenames = ["etatcivil2012_mar2012_dbase.zip",
"etatcivil2012_nais2012_dbase.zip",
"etatcivil2012_dec2012_dbase.zip", ]
root_url = 'http://telechargement.insee.fr/fichiersdetail/etatcivil2012/dbase/'
for filename in filenames:
download_and_save(filename, root_url)
unzip(filename)
print("Download of {}: DONE!".format(filename))
# In[21]:
import pandas
try:
from dbfread_ import DBF
use_dbfread = True
except ImportError as e :
use_dbfread = False
if use_dbfread:
print("use of dbfread")
def dBase2df(dbase_filename):
table = DBF(dbase_filename, load=True, encoding="cp437")
return pandas.DataFrame(table.records)
df = dBase2df('mar2012.dbf')
else :
print("use of zipped version")
import pyensae.datasource
data = pyensae.datasource.download_data("mar2012.zip")
df = pandas.read_csv(data[0], sep="\t", encoding="utf8", low_memory=False)
print(df.shape, df.columns)
df.head()
# In[22]:
df["ageH"] = df.apply (lambda r: 2014 - int(r["ANAISH"]), axis=1)
df["ageF"] = df.apply (lambda r: 2014 - int(r["ANAISF"]), axis=1)
df.head()
# In[23]:
df.plot(x="ageH",y="ageF", kind="scatter")
# In[24]:
df.plot(x="ageH",y="ageF", kind="hexbin")
# Exercice 5 : graphe de la distribution avec pandas
#
# Le module ``pandas`` propose un panel de graphiques standard faciles à obtenir. On souhaite représenter la distribution sous forme d'histogramme. A vous de choisir le meilleure graphique depuis la page [Visualization](http://pandas.pydata.org/pandas-docs/stable/visualization.html).
# In[25]:
df["ANAISH"] = df.apply (lambda r: int(r["ANAISH"]), axis=1)
df["ANAISF"] = df.apply (lambda r: int(r["ANAISF"]), axis=1)
df["differenceHF"] = df.ANAISH - df.ANAISF
df["nb"] = 1
dist = df[["nb","differenceHF"]].groupby("differenceHF", as_index=False).count()
df["differenceHF"].hist(figsize=(16,6), bins=50)
# Exercice 6 : distribution des mariages par jour
#
# On veut obtenir un graphe qui contient l'histogramme de la distribution du nombre de mariages par jour de la semaine et d'ajouter une seconde courbe correspond avec un second axe à la répartition cumulée.
#
# In[26]:
df["nb"] = 1
dissem = df[["JSEMAINE","nb"]].groupby("JSEMAINE",as_index=False).sum()
total = dissem["nb"].sum()
repsem = dissem.cumsum()
repsem["nb"] /= total
ax = dissem["nb"].plot(kind="bar")
repsem["nb"].plot(ax=ax, secondary_y=True)
ax.set_title("distribution des mariages par jour de la semaine")
# In[27]: