Lecture Notes

Collected lectures notes in computational biology

Exercice: Cytomégalovirus humain en Ouganda (et intro à pandas)

Ceci est un exemple de README.ipynb, un notebook que je vous conseille de créer dès que vous recevez un nouveau jeu de données. Le but est de centraliser au même endroit:

  • Une notice de la provenance et du contenu des données, comment les récupérer à nouveaux
  • Une visualisation rapide du contenu (descriptive seulement, vous pouvez prendre des notes en markdown), ici j'ai juste fait des commentaires sur pandas.
  • Une vérification de la cohérence des données
  • Quelques transformations préalables à l'analyse
  • Bien séparer ce qui vous arrive (dans raw/) et ce qui va servir à vos analyses après vérification/corrections (dans processed/)

Le Cytomégalovirus humain, (HCMV) ou Human Herpesvirus 5 est un virus de la famille des herpès virus très fréquent dans la population humaine. Voici un jeu de donnée permettant de caractériser l'interaction du HCMV avec d'autres infections transmissibles comme le VIH, la tuberculose et des facteurs de risques cardiovasculaires.

In [16]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

Origine des données

Les données dans raw ont été fournies par Guillaume Louvel lors du cours de statistique le 2x novembre 2018.

Ces données sont issues de l'article:

Stockdale, Lisa, Stephen Nash, Angela Nalwoga, Hannah Painter, Gershim Asiki, Helen Fletcher, and Robert Newton. “Human Cytomegalovirus Epidemiology and Relationship to Tuberculosis and Cardiovascular Disease Risk Factors in a Rural Ugandan Cohort.” PloS One 13, no. 2 (2018): e0192086. Lien vers PlosOne

Le jeu de donnée original peut être récupéré sur dryad:

Stockdale, Lisa, Stephen Nash, Angela Nalwoga, Hannah Painter, Gershim Asiki, Helen Fletcher, and Robert Newton. “Data from: Human Cytomegalovirus Epidemiology and Relationship to Tuberculosis and Cardiovascular Disease Risk Factors in a Rural Ugandan Cohort,” February 6, 2018. https://doi.org/10.5061/dryad.d1k17. Lien vers Dryad

In [ ]:
%%bash 
# On peut mettre un script bash dans un notebook !

# Créer l'arborescence 
mkdir -p data/raw

# Récupérer les données depuis le dossier du projet.
cp /users/mag/tpinfo/projet_HCMV.csv data/raw/

# Récupérer les données depuis la source 
wget https://datadryad.org/bitstream/handle/10255/dryad.170089/RAW_DATA.xls?sequence=1 -O data/raw/RAW_DATA.xls
In [2]:
# On peut aussi faire une seule commande bash dans le notebook avec le préfixe "!".
!head data/raw/projet_HCMV.csv
"sex";"cmv";"age";"cmvstatus";"hiv";"TB";"m_syst";"m_diast";"R22_bpgroup";"BMI";"bmi";"cholesterol";"high_d_lipid";"low_d_lipid";"hba1c";"cmv_tertile"
"Male";0,7485;0;"positive";"negative";"negative";;;"";;"";;;;;"Low"
"Male";0,6335;0;"positive";"negative";"negative";;;"";;"";;;;;"Low"
"Female";0,379;0;"positive";"negative";"negative";;;"";;"";;;;;"Low"
"Female";0,8295;0;"positive";"negative";"negative";;;"";;"";;;;;"Low"
"Female";0,517;0;"positive";"negative";"negative";;;"";;"";;;;;"Low"
"Male";1,0365;0;"positive";"positive";"negative";;;"";;"";;;;;"Medium"
"Male";1,017;0;"positive";"negative";"negative";;;"";;"";;;;;"Medium"
"Female";0,3305;0;"positive";"negative";"negative";;;"";;"";;;;;"Low"
"Female";0,139;0;"negative";"positive";"negative";;;"";;"";;;;;"Negative"

Notice des variables

Nom Description
sex Sexe des individus ('Male', 'Female')
cmv Dosage d'anticorps au HCMV
age Age en année
cmvstatus Séropositivité au CMV ('positive', 'negative')
hiv Séropositivié au HIV
TB Résultat du test salivaire à la tuberculose
m_syst Durée moyenne de systole
m_diast Durée moyenne de diastole
R22_bpgroup Hypertension
BMI Indice de masse corporelle (IMC)
cholesterol Taux sanguin de cholesterol
high_d_lipid Taux sanguin de HDL
low_d_lipid Taux sanguin de LDL
hba1c Taux d'hémoglobine glyquée
cmv_tertile Catégorisation en 3 quantiles de cmv

Lecture

In [7]:
# Ouvrir le fichier
data = pd.read_csv('data/raw/projet_HCMV.csv', sep=';', decimal=',') #=> Un objet de type pd.DataFrame
data = pd.read_excel('data/raw/RAW_DATA.xls')
print("Tableau de donnée de taille: {} lignes, {} colonnes ".format(*data.shape))
data.head()
Tableau de donnée de taille: 2174 lignes, 15 colonnes 
Out[7]:
sex cmv age_yrs cmvstatus hiv mean_syst mean_diast R22_bpgroup BMI bmi cholesterol high_d_lipid hba1c low_d_lipid cmv_tertile
0 Male 0.7485 0.0833333 positive negative NaN NaN NaN NaN NaN NaN NaN NaN NaN Low
1 Male 0.6335 0.0833333 positive negative NaN NaN NaN NaN NaN NaN NaN NaN NaN Low
2 Female 0.3790 0.166667 positive negative NaN NaN NaN NaN NaN NaN NaN NaN NaN Low
3 Female 0.8295 0.166667 positive negative NaN NaN NaN NaN NaN NaN NaN NaN NaN Low
4 Female 0.5170 0.166667 positive negative NaN NaN NaN NaN NaN NaN NaN NaN NaN Low
In [8]:
# On peut boucler sur les noms de colonnes avec:
for col in data.columns:
    print("- {} ({})".format(col,data[col].dtype), end='') # col.dtype donne le type de la colonne. 
    n = data[col].nunique() # nunique compte le nombre de valeurs uniques dans la colonne, .unique() liste ces valeurs
    print(' {} unique values {}'.format(n, ': '+str(data[col].unique()) if n <= 4 else ''))
- sex (object) 2 unique values : ['Male' 'Female']
- cmv (float64) 1610 unique values 
- age_yrs (object) 662 unique values 
- cmvstatus (object) 2 unique values : ['positive' 'negative']
- hiv (object) 3 unique values : ['negative' 'positive' 'unknown']
- mean_syst (object) 168 unique values 
- mean_diast (object) 117 unique values 
- R22_bpgroup (object) 4 unique values : [nan 'Normal' 'Pre-hypertension' 'Hypertension stage 1'
 'Hypertension stage 2']
- BMI (float64) 1025 unique values 
- bmi (object) 4 unique values : [nan 'Normal weight' 'Underweight' 'Overweight' 'Obese']
- cholesterol (float64) 430 unique values 
- high_d_lipid (float64) 273 unique values 
- hba1c (float64) 354 unique values 
- low_d_lipid (float64) 399 unique values 
- cmv_tertile (object) 4 unique values : ['Low' 'Medium' 'Negative' 'High']
In [9]:
# pandas utilise un type nommé catégorie pour les données qualitatives (comme les factors de R)
# pd. read stocke les entrées des csv comme des chaines de caractère (dtype=object par défaut)
# mais on peut les convertir en catégorie avec:
for col in ['sex', 'hiv', 'cmvstatus', 'cmv_tertile']:
    data[col] = data[col].astype('category')
In [10]:
# On peut accéder au np.array sous-jacent de chaque colonne.
data.cmv.values #=> np.array
Out[10]:
array([0.7485, 0.6335, 0.379 , ..., 0.07  , 2.043 , 1.807 ])
In [11]:
#On peut filtrer grace au "Boolean indexing", comme pour les np.array.
data[data.cmv>2.5]
Out[11]:
sex cmv age_yrs cmvstatus hiv mean_syst mean_diast R22_bpgroup BMI bmi cholesterol high_d_lipid hba1c low_d_lipid cmv_tertile
706 Female 2.5065 7.58333 positive positive NaN NaN NaN NaN NaN NaN NaN NaN NaN High
1603 Male 2.5440 34.3333 positive positive NaN NaN NaN NaN NaN NaN NaN NaN NaN High
1641 Female 2.8390 35.6667 positive positive 101 64.5 Normal 19.023912 Normal weight 3.28 1.29 4.05 1.54 High
1788 Female 2.7655 43.75 positive negative 110.5 71 Normal 22.818537 Normal weight 5.12 0.79 NaN 2.24 High
In [12]:
# On remarque que les colonnes arge_yrs, mean_syst et mean_diast qui devaient être float
# sont de type object. 

# La colonne "age" contient des valeurs qui ne sont pas des nombres...
# On le voit en faisant data['age_yrs'].unique()
print(frozenset([type(x) for x in data['age_yrs']]))
data['age_yrs'].unique()
frozenset({<class 'str'>, <class 'int'>, <class 'float'>})
Out[12]:
array([0.0833333358168602, 0.1666666716337204, 0.25, 0.3333333432674408,
       0.4166666567325592, 0.5, 0.5833333134651184, 0.6666666865348816,
       0.75, 0.8333333134651184, 0.9166666865348816, 1,
       1.0833333730697632, 1.1666666269302368, 1.25, 1.3333333730697632,
       1.4166666269302368, 1.5, 1.5833333730697632, 1.6666666269302368,
       1.75, 1.8333333730697632, 1.9166666269302368, 2,
       2.0833332538604736, 2.1666667461395264, 2.25, 2.3333332538604736,
       2.4166667461395264, 2.5, 2.5833332538604736, 2.6666667461395264,
       2.75, 2.8333332538604736, 2.9166667461395264, 3,
       3.0833332538604736, 3.1666667461395264, 3.25, 3.3333332538604736,
       3.4166667461395264, 3.5, 3.5833332538604736, 3.6666667461395264,
       3.75, 3.8333332538604736, 3.9166667461395264, 4, 4.083333492279053,
       4.166666507720947, 4.25, 4.333333492279053, 4.416666507720947, 4.5,
       4.583333492279053, 4.666666507720947, 4.75, 4.833333492279053,
       4.916666507720947, 5, 5.083333492279053, 5.166666507720947, 5.25,
       5.333333492279053, 5.416666507720947, 5.5, 5.583333492279053,
       5.666666507720947, 5.75, 5.833333492279053, 5.916666507720947, 6,
       6.083333492279053, 6.166666507720947, 6.25, 6.333333492279053,
       6.416666507720947, 6.5, 6.583333492279053, 6.666666507720947, 6.75,
       6.833333492279053, 6.916666507720947, 7, 7.083333492279053,
       7.166666507720947, 7.333333492279053, 7.416666507720947, 7.5,
       7.583333492279053, 7.666666507720947, 7.75, 7.833333492279053,
       7.916666507720947, 8, 8.083333015441895, 8.166666984558105, 8.25,
       8.333333015441895, 8.416666984558105, 8.5, 8.583333015441895,
       8.666666984558105, 8.75, 8.833333015441895, 8.916666984558105, 9,
       9.083333015441895, 9.166666984558105, 9.25, 9.333333015441895,
       9.416666984558105, 9.5, 9.583333015441895, 9.666666984558105, 9.75,
       9.833333015441895, 9.916666984558105, 10, 10.083333015441895,
       10.166666984558105, 10.25, 10.333333015441895, 10.666666984558105,
       10.75, 10.833333015441895, 10.916666984558105, 11,
       11.083333015441895, 11.166666984558105, 11.25, 11.333333015441895,
       11.416666984558105, 11.5, 11.583333015441895, 11.666666984558105,
       11.75, 11.833333015441895, 11.916666984558105, 12,
       12.083333015441895, 12.166666984558105, 12.25, 12.333333015441895,
       12.416666984558105, 12.583333015441895, 12.666666984558105, 12.75,
       12.833333015441895, 12.916666984558105, 13, 13.083333015441895,
       13.166666984558105, 13.25, 13.333333015441895, 13.416666984558105,
       13.583333015441895, 13.666666984558105, 13.75, 13.833333015441895,
       14, 14.083333015441895, 14.166666984558105, 14.25,
       14.333333015441895, 14.416666984558105, 14.5, 14.583333015441895,
       14.666666984558105, 14.75, 14.833333015441895, 14.916666984558105,
       15, 15.083333015441895, 15.166666984558105, 15.25,
       15.333333015441895, 15.416666984558105, 15.583333015441895,
       15.666666984558105, 15.75, 15.833333015441895, 15.916666984558105,
       16, 16.08333396911621, 16.16666603088379, 16.25, 16.33333396911621,
       16.41666603088379, 16.5, 16.66666603088379, 16.75,
       16.83333396911621, 16.91666603088379, 17, 17.08333396911621,
       17.16666603088379, 17.25, 17.33333396911621, 17.41666603088379,
       17.5, 17.58333396911621, 17.66666603088379, 17.75,
       17.83333396911621, 17.91666603088379, 18, 18.08333396911621,
       18.16666603088379, 18.25, 18.33333396911621, 18.41666603088379,
       18.58333396911621, 18.75, 18.83333396911621, 18.91666603088379, 19,
       19.08333396911621, 19.16666603088379, 19.25, 19.33333396911621,
       19.41666603088379, 19.5, 19.66666603088379, 19.75,
       19.83333396911621, 19.843942642211914, 19.91666603088379, 20,
       20.08333396911621, 20.16666603088379, 20.25, 20.33333396911621,
       20.58333396911621, 20.66666603088379, 20.75, 20.83333396911621,
       20.91666603088379, 20.958248138427734, 21, 21.08333396911621,
       21.16666603088379, 21.25, 21.33333396911621, 21.41666603088379,
       21.5, 21.66666603088379, 21.75, 21.83333396911621,
       21.91666603088379, 22, 22.08333396911621, 22.16666603088379, 22.25,
       22.33333396911621, 22.41666603088379, 22.58333396911621,
       22.66666603088379, 22.75, 22.83333396911621, 22.91666603088379, 23,
       23.08333396911621, 23.16666603088379, 23.33333396911621,
       23.41666603088379, 23.66666603088379, 23.75, 23.83333396911621,
       23.91666603088379, 24, 24.16666603088379, 24.25, 24.41666603088379,
       24.66666603088379, 24.75, 24.83333396911621, 24.91666603088379, 25,
       25.08333396911621, 25.16666603088379, 25.25, 25.41666603088379,
       25.5, 25.66666603088379, 25.75, 25.83333396911621,
       25.91666603088379, 26, 26.16666603088379, 26.25, 26.33333396911621,
       26.41666603088379, 26.5, 26.58333396911621, 26.66666603088379,
       26.75, 26.91666603088379, 27, 27.08333396911621, 27.16666603088379,
       27.25, 27.41666603088379, 27.5, 27.66666603088379, 27.75,
       27.83333396911621, 28, 28.08333396911621, 28.16666603088379, 28.25,
       28.33333396911621, 28.58333396911621, 28.66666603088379, 28.75,
       28.83333396911621, 29, 29.00205421447754, 29.16666603088379, 29.25,
       29.33333396911621, 29.41666603088379, 29.544147491455078,
       29.66666603088379, 29.75, 29.84804916381836, 29.91666603088379, 30,
       30.08333396911621, 30.16666603088379, 30.25, 30.33333396911621,
       30.75, 30.83333396911621, 30.91666603088379, 31, 31.08333396911621,
       31.16666603088379, 31.25, 31.33333396911621, 31.41666603088379,
       31.58333396911621, 31.75, 31.83333396911621, 31.90691375732422,
       31.91666603088379, 32, 32.08333206176758, 32.16666793823242, 32.25,
       32.33333206176758, 32.41666793823242, 32.5, 32.66666793823242,
       32.75, 32.91666793823242, 33, 33.08333206176758, 33.16666793823242,
       33.25, 33.33333206176758, 33.41666793823242, 33.58333206176758,
       33.66666793823242, 33.75, 33.83333206176758, 33.91666793823242,
       33.98494338989258, 34, 34.08333206176758, 34.16666793823242, 34.25,
       34.33333206176758, 34.41666793823242, 34.58333206176758,
       34.66666793823242, 34.75, 34.83333206176758, 34.91666793823242, 35,
       35.16666793823242, 35.25, 35.33333206176758, 35.5,
       35.66666793823242, 35.75, 35.83333206176758, 35.91666793823242,
       36.08333206176758, 36.16666793823242, 36.25, 36.33333206176758,
       36.66666793823242, 36.75, 36.83333206176758, 36.91666793823242, 37,
       37.08333206176758, 37.16666793823242, 37.25, 37.33333206176758,
       37.41666793823242, 37.66666793823242, 37.75, 37.83333206176758, 38,
       38.16666793823242, 38.33333206176758, 38.41666793823242,
       38.66666793823242, 38.75, 38.91666793823242, 39, 39.08333206176758,
       39.16666793823242, 39.25, 39.33333206176758, 39.66666793823242,
       39.75, 39.83333206176758, 39.91666793823242, 40.08333206176758,
       40.16666793823242, 40.25, 40.41666793823242, 40.75,
       40.83333206176758, 41, 41.08333206176758, 41.16666793823242, 41.25,
       41.5, 41.66666793823242, 41.83333206176758, 41.91666793823242, 42,
       42.08333206176758, 42.16666793823242, 42.25, 42.33333206176758,
       42.41666793823242, 42.66666793823242, 42.75, 42.83333206176758, 43,
       43.08333206176758, 43.25, 43.33333206176758, 43.5,
       43.66666793823242, 43.75, 43.83333206176758, 43.91666793823242, 44,
       44.16666793823242, 44.25, 44.33333206176758, 44.41666793823242,
       44.75, 44.91666793823242, 45, 45.08333206176758, 45.16666793823242,
       45.25, 45.41666793823242, 45.5, 45.75, 45.83333206176758,
       45.91666793823242, 46, 46.08333206176758, 46.16666793823242, 46.25,
       46.33333206176758, 46.58333206176758, 46.66666793823242, 46.75,
       46.83333206176758, 46.91666793823242, 47, 47.08333206176758,
       47.16666793823242, 47.25, 47.33333206176758, 47.41666793823242,
       47.5, 47.75, 47.83333206176758, 47.91666793823242, 48,
       48.08333206176758, 48.16666793823242, 48.25, 48.58333206176758,
       48.75, 48.83333206176758, 48.91666793823242, 49, 49.08333206176758,
       49.16666793823242, 49.25, 49.33333206176758, 49.41666793823242,
       49.5, 49.66666793823242, 49.83333206176758, 50.08333206176758,
       50.16666793823242, 50.25, 50.66666793823242, 50.75,
       50.83333206176758, 51, 51.08333206176758, 51.16666793823242, 51.25,
       51.33333206176758, 51.66666793823242, 51.83333206176758,
       51.91666793823242, 52, 52.16666793823242, 52.25, 52.33333206176758,
       52.5, 52.66666793823242, 52.91666793823242, 53, 53.08333206176758,
       53.16666793823242, 53.25, 53.33333206176758, 53.41666793823242,
       53.75, 53.83333206176758, 54.08333206176758, 54.25, 54.75,
       54.91666793823242, 55, 55.08333206176758, 55.16666793823242, 55.25,
       55.33333206176758, 55.41666793823242, 55.66666793823242, 55.75,
       55.83333206176758, 55.91666793823242, 56.08333206176758, 56.25,
       56.41666793823242, 56.5, 56.66666793823242, 56.83333206176758,
       57.25, 57.33333206176758, 57.41666793823242, 57.83333206176758,
       57.91666793823242, 58, 58.16666793823242, 58.25, 58.41666793823242,
       58.66666793823242, 58.75, 58.91666793823242, 59.08333206176758,
       59.16666793823242, 59.58333206176758, 59.83333206176758,
       60.16666793823242, 60.33333206176758, 60.66666793823242, 60.75,
       60.91666793823242, 61.16666793823242, 61.25, 61.33333206176758,
       61.41666793823242, 61.66666793823242, 61.75, 61.83333206176758,
       61.91666793823242, 62, 62.08333206176758, 62.16666793823242,
       62.33333206176758, 62.41666793823242, 62.75, 63.16666793823242,
       63.25, 63.83333206176758, 64.08333587646484, 64.25,
       64.58333587646484, 64.75, 64.83333587646484, 65.16666412353516,
       65.58333587646484, 65.75, 65.83333587646484, 66.08333587646484,
       66.16666412353516, 66.25, 66.33333587646484, 66.41666412353516,
       66.58333587646484, 66.75, 67, 67.16666412353516, 67.33333587646484,
       67.5, 67.83333587646484, 68.16666412353516, 68.25, 68.75,
       68.83333587646484, 69.16666412353516, 69.83333587646484, 70.25,
       70.33333587646484, 70.83333587646484, 71, 71.08333587646484,
       71.16666412353516, 71.33333587646484, 71.66666412353516, 71.75,
       71.83333587646484, 71.91666412353516, 72, 72.16666412353516, 72.25,
       72.33333587646484, 72.91666412353516, 73, 73.08333587646484,
       73.16666412353516, 73.66666412353516, 73.75, 73.83333587646484, 74,
       74.08333587646484, 74.41666412353516, 74.75, 74.83333587646484,
       75.16666412353516, 75.33333587646484, 75.58333587646484,
       75.83333587646484, 76.16666412353516, 76.25, 76.75, 77,
       77.08333587646484, 77.25, 77.83333587646484, 77.91666412353516, 78,
       78.75, '80+'], dtype=object)
In [13]:
# On peut le corriger comme ceci 
# On créer une colonne age, numérique cette fois ci. # (on fixe tout 80+ à 80)
# Ajouter une colonne se fait simplement en assignant un itérable de la bonne longueur.
data['age'] = [a if a!='80+' else 80 for a in data.age_yrs]
In [14]:
# Les colonnes mean_diast et mean_syst contient un "don't know"
data[data.mean_diast=="Don't know"]
Out[14]:
sex cmv age_yrs cmvstatus hiv mean_syst mean_diast R22_bpgroup BMI bmi cholesterol high_d_lipid hba1c low_d_lipid cmv_tertile age
1333 Female 1.291 22.9167 positive negative Don't know Don't know Hypertension stage 2 21.784439 Normal weight 3.51 1.1 3.6 1.92 High 22.916666
In [17]:
# Correction avec une compréhension de liste:
for d in ('diast','syst'):
    data['m_'+d]= [float(m) if m!="Don't know" else np.nan for m in data['mean_'+d]]
In [18]:
# La méthode describe donne le nombre de valeur non nulles, 
# la moyenne, l'écart type et les pourcentiles des variables quantitatives
data.describe()
Out[18]:
cmv BMI cholesterol high_d_lipid hba1c low_d_lipid age m_diast m_syst
count 2174.000000 1150.000000 1186.000000 1186.000000 1176.000000 1186.000000 2174.000000 1186.000000 1186.000000
mean 1.038379 21.036491 3.492056 0.972934 3.273901 1.983557 22.614921 74.098229 122.779089
std 0.461336 3.502296 0.966336 0.438494 0.675460 0.770839 20.575627 10.294270 17.502998
min -0.003000 12.798847 1.020000 0.030000 0.770000 0.019503 0.083333 46.000000 82.500000
25% 0.803125 18.898643 2.820000 0.700000 2.900000 1.470000 5.187500 67.500000 111.500000
50% 1.019000 20.654502 3.400000 0.940000 3.290000 1.930000 16.250000 72.500000 120.000000
75% 1.304375 22.631470 4.070000 1.200000 3.650000 2.430000 35.166668 80.000000 130.000000
max 2.839000 55.900074 7.670000 4.160000 11.350000 5.290000 80.000000 125.500000 213.000000
In [19]:
# On remarque que le jeu de donnée contient une ligne où cmv est négatif 
# (alors que les auteurs ont dit que ce n'était pas le cas)
data[data.cmv<0]
Out[19]:
sex cmv age_yrs cmvstatus hiv mean_syst mean_diast R22_bpgroup BMI bmi cholesterol high_d_lipid hba1c low_d_lipid cmv_tertile age m_diast m_syst
145 Female -0.003 1.5 negative negative NaN NaN NaN NaN NaN NaN NaN NaN NaN Negative 1.5 NaN NaN
In [20]:
# On le corrige avec un indexage booléen
# Le .copy assure qu'on est entrain de travailler sur une copie et pas une vue de raw.
data = data[data.cmv>0]

data.shape
Out[20]:
(2173, 18)
In [21]:
cols = ['sex', 'hiv', 'cmvstatus', 'cmv_tertile']
fig, axes = plt.subplots(1,len(cols), figsize=(len(cols)*4,4))
# notez l'utilisation de zip pour pouvoir boucler sur les éléments de cols et de axes à la fois. 
for ax,col in zip(axes, cols):
    df = pd.value_counts(data[col]) #=> pd.DataFrame
    print(df, end='\n---\n')
    # Les dataframes ont des méthodes de plot qui appellent matplotlib.
    # https://pandas.pydata.org/pandas-docs/stable/visualization.html#
    df.plot.bar(ax=ax) #=> On peut préciser sur quel ax dessiner. 
    ax.set(title=col)
Female    1090
Male      1083
Name: sex, dtype: int64
---
negative    2033
positive     100
unknown       40
Name: hiv, dtype: int64
---
positive    1988
negative     185
Name: cmvstatus, dtype: int64
---
Medium      663
Low         663
High        662
Negative    185
Name: cmv_tertile, dtype: int64
---
In [24]:
import scipy.stats
In [23]:
cols = ['cmv','BMI']
fig, axes = plt.subplots(1,len(cols), figsize=(len(cols)*4,4))
for col,ax in zip(cols,axes):
    X = scipy.stats.norm(loc=data[col].mean(), scale=data[col].std())
    x = np.linspace(data[col].min(), data[col].max())
    ax.plot(x, X.pdf(x))
    data[col].plot.hist(ax=ax, normed=True, alpha=0.5, color='C0')
/home/guilhem/.local/lib/python3.5/site-packages/matplotlib/axes/_axes.py:6499: MatplotlibDeprecationWarning: 
The 'normed' kwarg was deprecated in Matplotlib 2.1 and will be removed in 3.1. Use 'density' instead.
  alternative="'density'", removal="3.1")
In [25]:
# L'itérateur retourné par groupby permet de séparer le jeu de donnée à partir de la valeur 
# d'une colonne. 
for sex, df in data[['sex','cmv']].groupby('sex'):
    # sex contient la valeur de data.sex (Male/Female)
    # df contient un dataframe correspondant à la valeur.
    print("=== "+sex+' ===')
    print(df.describe())
    
# L'objet groupby lui même à des méthodes pratiques qui combinent le résultat en un dataframe:
data.groupby('sex').mean() #sum...
=== Female ===
               cmv
count  1090.000000
mean      1.068265
std       0.480569
min       0.023500
25%       0.813125
50%       1.044750
75%       1.358875
max       2.839000
=== Male ===
               cmv
count  1083.000000
mean      1.009262
std       0.438453
min       0.003500
25%       0.790500
50%       0.995500
75%       1.242250
max       2.544000
Out[25]:
cmv BMI cholesterol high_d_lipid hba1c low_d_lipid age m_diast m_syst
sex
Female 1.068265 21.735216 3.707957 1.005221 3.291037 2.155617 22.244500 74.285240 120.977612
Male 1.009262 20.340193 3.267994 0.939426 3.255990 1.804994 23.007233 73.904803 124.642367
In [26]:
# On peut groupby une liste de colonnes:
data.groupby(['sex','cmvstatus']).mean()
Out[26]:
cmv BMI cholesterol high_d_lipid hba1c low_d_lipid age m_diast m_syst
sex cmvstatus
Female negative 0.131954 22.416178 4.094572 1.062917 3.413618 2.476045 15.337629 75.538462 119.115385
positive 1.159728 21.688296 3.681270 1.001238 3.282530 2.133499 22.919189 74.198582 121.106383
Male negative 0.108437 20.849242 3.139676 0.871625 3.165429 1.712627 13.694129 72.596774 122.580645
positive 1.088933 20.312223 3.275213 0.943241 3.261150 1.810191 23.830905 73.978261 124.758152
In [27]:
# La méthode boxplot peut prendre un argument groupby.
data.boxplot(column='cmv',by='sex');
In [28]:
# Scatter matrix permet d'afficher toutes les paires de variables quantitatives.
from pandas.plotting import scatter_matrix
scatter_matrix(data, figsize=(20,20));

Test de cohérence (sanity check)

In [29]:
# On peut conserver seulement certaines colonnes
data = data[['sex', 'cmv', 'age', 'cmvstatus', 'hiv', 'm_diast', 'm_syst',
       'R22_bpgroup', 'BMI', 'bmi', 'cholesterol', 'high_d_lipid', 'hba1c',
       'low_d_lipid', 'cmv_tertile', ]]
data.shape
Out[29]:
(2173, 15)
In [30]:
# À la fin de la lecture il est important de tester si le jeu de donnée est cohérent 

# Si la taille du jeu de donnée est connue à l'avance,
# C'est le bon moment pour la tester
assert data.shape[0] == 2173, 'Nombre de lignes'
assert data.shape[1] == 15, 'Nombre de colonnes'

# Tester si on a pas des valeurs absurdes ou contradictoires...
assert all(data.age>=0), 'Ages négatifs'
assert all(data.cmv>=0), 'cmv negatif'
# Tester si les données sont cohérentes avec la notice:
assert all(data.sex.isin(('Male','Female')))
assert all(data.cmvstatus.isin(('positive','negative'))) #=> is in peut aussi servir pour faire de l'indexage booléen.

Modification du jeu de donnée pour les analyses futures

In [31]:
# Ajouter une colonne se fait simplement en assignant un objet pd.Series avec un index compatible.

# On peut catégoriser une variable quantitative avec des seuils
data['age_cat'] = pd.cut(data.age, bins=[0,2,4,6,11,16,21,31,41,51,61,data.age.max()+1], 
                         right=False, include_lowest=True)

# Les pd.Series ont les mêmes règles de "broadcasting" que les np.array
data['log_m_syst'] = np.log(data.m_syst)

data[['age','age_cat', 'm_syst', 'log_m_syst']].tail()
Out[31]:
age age_cat m_syst log_m_syst
2169 80.0 [61.0, 81.0) 158.5 5.065755
2170 80.0 [61.0, 81.0) 131.5 4.879007
2171 80.0 [61.0, 81.0) 150.5 5.013963
2172 80.0 [61.0, 81.0) 140.5 4.945207
2173 80.0 [61.0, 81.0) 129.5 4.863681
In [32]:
# Enfin il est possible de joindre des tableaux de façon très facile.
data2 = pd.DataFrame({'sex':['Male','Female'], 'test':[1,2]})
# Il suffit de donner le tableau de droite, 
# celui de gauche, et le nom de la colonne sur laquelle faire la jointure.
# On peut aussi préciser si c'est une jointure à droite, à gauche, interne ou externe. 
# L'explication complète ici: https://pandas.pydata.org/pandas-docs/stable/merging.html#merging
pd.merge(left=data[['sex','cmv']], right=data2, left_on='sex', right_on='sex').head()
Out[32]:
sex cmv test
0 Male 0.7485 1
1 Male 0.6335 1
2 Male 1.0365 1
3 Male 1.0170 1
4 Male 1.4950 1

Export

In [33]:
# Crééons le dossier de sortie avec python.
# Utiliser os et os.path.join permet d'avoir un code multi plateforme (linux, mac, windows.)
import os
out_path = os.path.join('data','processed') #=> "data/processed" sous linux,mac, "data\processed" sous windows.
if not os.path.exists(out_path):
    os.mkdir(out_path)
data.to_csv(os.path.join(out_path, 'data.csv'))
In [34]:
!head data/processed/data.csv
,sex,cmv,age,cmvstatus,hiv,m_diast,m_syst,R22_bpgroup,BMI,bmi,cholesterol,high_d_lipid,hba1c,low_d_lipid,cmv_tertile,age_cat,log_m_syst
0,Male,0.7484999999999999,0.0833333358168602,positive,negative,,,,,,,,,,Low,"[0.0, 2.0)",
1,Male,0.6335,0.0833333358168602,positive,negative,,,,,,,,,,Low,"[0.0, 2.0)",
2,Female,0.379,0.1666666716337204,positive,negative,,,,,,,,,,Low,"[0.0, 2.0)",
3,Female,0.8294999999999999,0.1666666716337204,positive,negative,,,,,,,,,,Low,"[0.0, 2.0)",
4,Female,0.517,0.1666666716337204,positive,negative,,,,,,,,,,Low,"[0.0, 2.0)",
5,Male,1.0365,0.1666666716337204,positive,positive,,,,,,,,,,Medium,"[0.0, 2.0)",
6,Male,1.017,0.1666666716337204,positive,negative,,,,,,,,,,Medium,"[0.0, 2.0)",
7,Female,0.3305,0.1666666716337204,positive,negative,,,,,,,,,,Low,"[0.0, 2.0)",
8,Female,0.139,0.25,negative,positive,,,,,,,,,,Negative,"[0.0, 2.0)",