.. _science2:
Bibliothèques scientifiques avancées
####################################
.. contents:: Table des matières
:local:
..
1 Pandas et xarray
1.1 Structures
1.2 Accès aux données
1.3 Manipulation des données
1.4 Regroupement et agrégation de données
1.5 Visualisations
1.6 Xarray
1.7 Autres types de tableaux
2 Astropy
2.1 Tour d'horizon (v4.0)
2.2 Démonstration
3 Autres bibliothèques scientifiques
.. _myPandas:
Pandas et xarray
================
.. index:: module: pandas
pandas_ est une bibliothèque pour la structuration et l'analyse avancée de
données *hétérogènes* (`PANel DAta`). Elle fournit notamment:
* des structures de données relationelles (« labellisées »), avec une
indexation simple ou hiérarchique (c.-à-d. à plusieurs niveaux);
* des méthodes d'alignement et d'agrégation des données, avec gestion des
données manquantes;
* un support performant des labels temporels (p.ex. dates, de par son origine
dans le domaine de l'économétrie), et des statistiques « glissantes »;
* de nombreuses fonctions d'entrée/sortie, d'analyse statistiques et de
visualisation.
Les fonctionnalités de :mod:`pandas` sont *très* riches et couvrent de nombreux
aspects (données manquantes, dates, analyse statistiques, etc.): il n'est pas
question de toutes les aborder ici. Avant de vous lancer dans une manipulation
qui vous semble complexe, bien inspecter la `documentation
`_, très complète (p.ex. les
recettes du :doc:`pandas:user_guide/cookbook`), pour vérifier qu'elle n'est pas
déjà implémentée ou documentée, ou pour identifier l'approche la plus efficace.
.. Note:: La convention utilisée ici est « `import pandas as PD` ».
.. Attention:: La bibliothèque pandas_ est maintenant considérée comme stable
(v1.x) mais xarray_ est encore en phase de développement. Nous travaillons
ici sur les versions:
* `pandas `_ :doc:`1.1 `
* `xarray `_ :doc:`0.16 `
Structures
----------
.. index::
pair: pandas; Series
pair: pandas; DataFrame
**Références:** :doc:`pandas:user_guide/dsintro`
Pandas dispose de deux grandes structures de données [#panels]_:
========================== ==== ===========
Nom de la structure Rang Description
========================== ==== ===========
:class:`pandas.Series` 1 Vecteur de données *homogènes* labellisées
:class:`pandas.DataFrame` 2 Tableau structuré de colonnes *homogènes*
========================== ==== ===========
>>> PD.Series(range(3)) # Série d'entiers sans indexation
0 0
1 1
2 2
dtype: int64
>>> PD.Series(N.random.randn(3), index=list('abc')) # Série de réels indexés
a -0.553480
b 0.081297
c -1.845835
dtype: float64
>>> PD.DataFrame(N.random.randn(3, 4))
0 1 2 3
0 1.570977 -0.677845 0.094364 -0.362031
1 -0.136712 0.762300 0.068611 1.265816
2 -0.697760 0.791288 0.449645 -1.105062
>>> PD.DataFrame([(1, N.exp(1), 'un'), (2, N.exp(2), 'deux'), (3, N.exp(3), 'trois')],
... index=list('abc'), columns='i val nom'.split())
i val nom
a 1 2.718282 un
b 2 7.389056 deux
c 3 20.085537 trois
Pour mettre en évidence la puissance de Pandas, nous utiliserons le
:ref:`catalogue des objets Messier ` vu précédemment. Le
fichier peut être importé à l'aide de la function
:func:`pandas.read_csv`, et le *dataframe* résultant est labellisé *à
la volée* par la colonne `M` (:meth:`pandas.DataFrame.set_index`) :
>>> messier = PD.read_csv("Messier.csv", comment='#') # Lecture du fichier CSV
>>> messier.set_index('M', inplace=True) # Indexation sur la colonne "M"
>>> messier.info() # Informations générales
Index: 110 entries, M1 to M110
Data columns (total 10 columns):
NGC 108 non-null object
Type 110 non-null object
Mag 110 non-null float64
Size 110 non-null float64
Distance 110 non-null float64
RA 110 non-null float64
Dec 110 non-null float64
Con 110 non-null object
Season 110 non-null object
Name 31 non-null object
dtypes: float64(5), object(5)
memory usage: 9.5+ KB
>>> messier.head(3) # Par défaut les 5 premières lignes
NGC Type Mag Size Distance RA Dec Con Season Name
M
M1 1952 Sn 8.4 5.0 1930.0 5.575 22.017 Tau winter Crab Nebula
M2 7089 Gc 6.5 12.9 11600.0 21.558 0.817 Aqr autumn NaN
M3 5272 Gc 6.2 16.2 10400.0 13.703 28.383 CVn spring NaN
.. index::
pair: pandas; Index
pair: pandas; index
pair: pandas; columns
pair: pandas; values
Un *dataframe* est caractérisé par son indexation
:attr:`pandas.DataFrame.index` et ses colonnes
:attr:`pandas.DataFrame.columns` (de type :class:`pandas.Index` ou
:class:`pandas.MultiIndex`), et les valeurs des données
:attr:`pandas.DataFrame.values`:
>>> messier.index # Retourne un Index
Index([u'M1', u'M2', u'M3', ..., u'M108', u'M109', u'M110'],
dtype='object', name=u'M', length=110)
>>> messier.columns # Retourne un Index
Index([u'NGC', u'Type', u'Mag', ..., u'Con', u'Season', u'Name'],
dtype='object')
>>> messier.dtypes # Retourne une Series indexée sur le nom des colonnes
NGC object
Type object
Mag float64
Size float64
Distance float64
RA float64
Dec float64
Con object
Season object
Name object
dtype: object
>>> messier.values
array([['1952', 'Sn', 8.4, ..., 'Tau', 'winter', 'Crab Nebula'],
['7089', 'Gc', 6.5, ..., 'Aqr', 'autumn', nan],
...,
['3992', 'Ba', 9.8, ..., 'UMa', 'spring', nan],
['205', 'El', 8.5, ..., 'And', 'autumn', nan]], dtype=object)
>>> messier.shape
(110, 10)
Une description statistique sommaire des colonnes numériques est
obtenue par :meth:`pandas.DataFrame.describe`:
>>> messier.drop(['RA', 'Dec'], axis=1).describe()
Mag Size Distance
count 110.000000 110.000000 1.100000e+02
mean 7.492727 17.719091 4.574883e+06
std 1.755657 22.055100 7.141036e+06
min 1.600000 0.800000 1.170000e+02
25% 6.300000 6.425000 1.312500e+03
50% 7.650000 9.900000 8.390000e+03
75% 8.900000 17.300000 1.070000e+07
max 10.200000 120.000000 1.840000e+07
Accès aux données
-----------------
.. index::
pair: pandas; loc
pair: pandas; iloc
pair: pandas; at
pair: pandas; iat
pair: pandas; filter
pair: pandas; query
**Référence:** :doc:`pandas:user_guide/indexing`
L'accès par colonne retourne une :class:`pandas.Series` (avec la même
indexation) pour une colonne unique, ou un nouveau
:class:`pandas.DataFrame` pour plusieurs colonnes:
>>> messier.NGC # Équivalent à messier['NGC']
M
M1 1952
M2 7089
...
M109 3992
M110 205
Name: NGC, Length: 110, dtype: object
>>> messier[['RA', 'Dec']] # = messier.filter(items=('RA', 'Dec'))
RA Dec
M
M1 5.575 22.017
M2 21.558 0.817
... ... ...
M109 11.960 53.383
M110 0.673 41.683
[110 rows x 2 columns]
L'accès par :class:`slice` retourne un nouveau *dataframe*:
>>> messier[:6:2] # Lignes 0 (inclus) à 6 (exclu) par pas de 2
NGC Type Mag Size Distance RA Dec Con Season Name
M
M1 1952 Sn 8.4 5.0 1930.0 5.575 22.017 Tau winter Crab Nebula
M3 5272 Gc 6.2 16.2 10400.0 13.703 28.383 CVn spring NaN
M5 5904 Gc 5.6 17.4 7520.0 15.310 2.083 Ser summer NaN
L'accès peut également se faire par labels via :attr:`pandas.DataFrame.loc`:
>>> messier.loc['M31'] # Retourne une Series indexée par les noms de colonne
NGC 224
Type Sp
...
Season autumn
Name Andromeda Galaxy
Name: M31, Length: 10, dtype: object
>>> messier.loc['M31', ['Type', 'Name']] # Retourne une Series
Type Sp
Name Andromeda Galaxy
Name: M31, dtype: object
>>> messier.loc[['M31', 'M51'], ['Type', 'Name']] # Retourne un DataFrame
Type Name
M
M31 Sp Andromeda Galaxy
M51 Sp Whirlpool Galaxy
>>> messier.loc['M31':'M33', ['Type', 'Name']] # De M31 à M33 *inclu*
Type Name
M
M31 Sp Andromeda Galaxy
M32 El NaN
M33 Sp Triangulum Galaxy
De façon symétrique, l'accès peut se faire par position (n° de ligne/colonne)
via :attr:`pandas.DataFrame.iloc`, p.ex.:
>>> messier.iloc[30:33, [1, -1]] # Ici, l'indice 33 n'est PAS inclu!
Type Name
M
M31 Sp Andromeda Galaxy
M32 El NaN
M33 Sp Triangulum Galaxy
>>> messier.iloc[30:33, messier.columns.get_loc('Name')] # Mix des 2 approches
M
M31 Andromeda Galaxy
M32 NaN
M33 Triangulum Galaxy
Name: Name, dtype: object
Les fonctions :attr:`pandas.DataFrame.at` et :attr:`pandas.DataFrame.iat`
permettent d'accéder *rapidement* aux données individuelles:
>>> messier.at['M31', 'NGC'] # 20× plus rapide que messier.loc['M31']['NGC']
'224'
>>> messier.iat[30, 0] # 20× plus rapide que messier.iloc[30][0]
'224'
Noter qu'il existe une façon de filtrer les données sur les colonnes
ou les labels:
>>> messier.filter(regex='M.7', axis='index').filter('RA Dec'.split())
RA Dec
M
M17 18.347 -16.183
M27 19.993 22.717
M37 5.873 32.550
M47 7.610 -14.500
M57 18.893 33.033
M67 8.840 11.817
M77 2.712 0.033
M87 12.513 12.400
M97 11.247 55.017
Comme pour :mod:`numpy`, il est possible d'opérer une sélection
booléenne:
>>> messier.loc[messier['Con'] == 'UMa', ['NGC', 'Name']]
NGC Name
M
M40 Win4 Winnecke 4
M81 3031 Bode's Galaxy
M82 3034 Cigar Galaxy
M97 3587 Owl Nebula
M101 5457 Pinwheel Galaxy
M108 3556 NaN
M109 3992 NaN
>>> messier[messier['Season'].isin('winter spring'.split())].head(3)
NGC Type Mag Size Distance RA Dec Con Season Name
M
M1 1952 Sn 8.4 5.0 1930.0 5.575 22.017 Tau winter Crab Nebula
M3 5272 Gc 6.2 16.2 10400.0 13.703 28.383 CVn spring NaN
M35 2168 Oc 5.3 28.0 859.0 6.148 24.333 Gem winter NaN
>>> messier.loc[lambda df: N.radians(df.Size / 60) * df.Distance < 1].Name
M
M27 Dumbbell Nebula
M40 Winnecke 4
M57 Ring Nebula
M73 NaN
M76 Little Dumbbell Nebula
M78 NaN
M97 Owl Nebula
Name: Name, dtype: object
>>> messier.query("(Mag < 5) & (Size > 60)").Name
M
M7 Ptolemy's Cluster
M24 Sagittarius Star Cloud
M31 Andromeda Galaxy
M42 Great Nebula in Orion
M44 Beehive Cluster
M45 Pleiades
Name: Name, dtype: object
======================== ===================== =========
Sélection Syntaxe Résultat
======================== ===================== =========
Colonne unique `df.col` or `df[col]` :class:`pandas.Series`
Liste de colonnes `df[[c1, ...]]` :class:`pandas.DataFrame`
Lignes par tranche `df[slice]` :class:`pandas.DataFrame`
Label unique `df.loc[label]` :class:`pandas.Series`
Liste de labels `df.loc[[lab1, ...]]` :class:`pandas.DataFrame`
Labels par tranche `df.loc[lab1:lab2]` :class:`pandas.DataFrame`
Ligne entière par n° `df.iloc[i]` :class:`pandas.Series`
Ligne partielle par n° `df.iloc[i, [j,...]]` :class:`pandas.Series`
Valeur par labels `df.at[lab, col]` Scalaire
Valeur par n° `df.iat[i, j]` Scalaire
Ligne par sél. booléenne `df.loc[sel]` or :class:`pandas.DataFrame`
`df[sel]` or
`df.query("sel")`
======================== ===================== =========
.. index::
pair: pandas; drop
pair: pandas; dropna
pair: pandas; fillna
:meth:`pandas.DataFrame.drop` permet d'éliminer une ou plusieurs
colonnes d'un *dataframe*:
>>> messier.drop(['RA', 'Dec'], axis=1).head(3) # Élimination de colonnes
NGC Type Mag Size Distance Con Season Name
M
M1 1952 Sn 8.4 5.0 1930.0 Tau winter Crab Nebula
M2 7089 Gc 6.5 12.9 11600.0 Aqr autumn NaN
M3 5272 Gc 6.2 16.2 10400.0 CVn spring NaN
:meth:`pandas.DataFrame.dropna` et :meth:`pandas.DataFrame.fillna`
permettent de gérer les données manquantes (*NaN*):
>>> messier.dropna(axis=0, how='any', subset=['NGC', 'Name']).head(3)
NGC Type Mag Size Distance RA Dec Con Season Name
M
M1 1952 Sn 8.4 5.0 1930.0 5.575 22.017 Tau winter Crab Nebula
M6 6405 Oc 4.2 25.0 491.0 17.668 -32.217 Sco summer Butterfly Cluster
M7 6475 Oc 3.3 80.0 245.0 17.898 -34.817 Sco summer Ptolemy's Cluster
>>> messier.fillna('', inplace=True) # Remplace les NaN à la volée
>>> messier.head(3)
NGC Type Mag Size Distance RA Dec Con Season Name
M
M1 1952 Sn 8.4 5.0 1930.0 5.575 22.017 Tau winter Crab Nebula
M2 7089 Gc 6.5 12.9 11600.0 21.558 0.817 Aqr autumn
M3 5272 Gc 6.2 16.2 10400.0 13.703 28.383 CVn spring
**Référence:** :doc:`pandas:user_guide/missing_data`
.. Attention:: par défaut, beaucoup d'opérations retournent une
*copie* de la structure, sauf si l'opération se fait « sur place »
(`inplace=True`). D'autres opérations d'accès retournent seulement
une *nouvelle vue* des mêmes données.
>>> df = PD.DataFrame(N.arange(12).reshape(3, 4),
... index=list('abc'), columns=list('ABCD')); df
A B C D
a 0 1 2 3
b 4 5 6 7
c 8 9 10 11
>>> df.drop('a', axis=0)
A B C D
b 4 5 6 7
c 8 9 10 11
>>> colA = df['A'] # Nouvelle vue de la colonne 'A'
>>> colA += 1 # Opération sur place
>>> df # la ligne 'a' est tjs là, la colonne 'A' a été modifiée
A B C D
a 1 1 2 3
b 5 5 6 7
c 9 9 10 11
Lien: `Returning a view versus a copy
`_
.. rubric:: Indéxation hiérarchique
**Références:** :doc:`pandas:user_guide/advanced`
.. index::
pair: pandas; reset_index
pair: pandas; set_index
pair: pandas; xs
pair: pandas; sort_index
:class:`pandas.MultiIndex` offre une indexation *hiérarchique*,
permettant de stocker et manipuler des données avec un nombre
arbitraire de dimensions dans des structures plus simples.
>>> saisons = messier.reset_index() # Élimine l'indexation actuelle
>>> saisons.set_index(['Season', 'Type'], inplace=True) # MultiIndex
>>> saisons.head(3)
M NGC Mag Size Distance RA Dec Con Name
Season Type
winter Sn M1 1952 8.4 5.0 1930.0 5.575 22.017 Tau Crab Nebula
autumn Gc M2 7089 6.5 12.9 11600.0 21.558 0.817 Aqr
spring Gc M3 5272 6.2 16.2 10400.0 13.703 28.383 CVn
Les informations contenues sont toujours les mêmes, mais structurées
différemment:
>>> saisons.loc['spring'].head(3) # Utilisation du 1er label
M NGC Mag Size Distance RA Dec Con Name
Type
Gc M3 5272 6.2 16.2 10400.0 13.703 28.383 CVn
Ds M40 Win4 8.4 0.8 156.0 12.373 58.083 UMa Winnecke 4
El M49 4472 8.4 8.2 18400000.0 12.497 8.000 Vir
>>> saisons.loc['spring', 'El'].head(3) # Utilisation des 2 labels
M NGC Mag Size Distance RA Dec Con Name
Season Type
spring El M49 4472 8.4 8.2 18400000.0 12.497 8.00 Vir
El M59 4621 9.6 4.2 18400000.0 12.700 11.65 Vir
El M60 4649 8.8 6.5 18400000.0 12.728 11.55 Vir
La fonction :meth:`pandas.DataFrame.xs` permet des sélections sur les
différents niveaux d'indexation:
>>> saisons.xs('spring', level='Season').head(3) # = saisons.loc['spring']
M NGC Mag Size Distance RA Dec Con Name
Type
Gc M3 5272 6.2 16.2 10400.0 13.703 28.383 CVn
Ds M40 Win4 8.4 0.8 156.0 12.373 58.083 UMa Winnecke 4
El M49 4472 8.4 8.2 18400000.0 12.497 8.000 Vir
>>> saisons.xs('El', level='Type').head(3) # Sélection sur le 2e niveau
M NGC Mag Size Distance RA Dec Con Name
Season
autumn M32 221 8.1 7.0 920000.0 0.713 40.867 And
spring M49 4472 8.4 8.2 18400000.0 12.497 8.000 Vir
spring M59 4621 9.6 4.2 18400000.0 12.700 11.650 Vir
Le (multi-)index n'est pas nécessairement trié à sa création,
:meth:`pandas.sort_index` permet d'y remédier:
>>> saisons[['M', 'NGC', 'Name']].head()
M NGC Name
Season Type
winter Sn M1 1952 Crab Nebula
autumn Gc M2 7089
spring Gc M3 5272
summer Gc M4 6121
Gc M5 5904
>>> saisons[['M', 'NGC', 'Name']].sort_index().head()
M NGC Name
Season Type
autumn El M32 221
El M110 205
Gc M2 7089
Gc M15 7078 Great Pegasus Globular
Gc M30 7099
Manipulation des données
------------------------
**Références:** :doc:`pandas:user_guide/basics`
Comme dans :mod:`numpy`, il est possible de modifier les valeurs,
ajouter/retirer des colonnes ou des lignes, tout en gérant les données
manquantes.
.. Note:: l'interopérabilité entre :mod:`pandas` et :mod:`numpy` est
totale, toutes les fonctions Numpy peuvent prendre une structure
Pandas en entrée, et s'appliquer aux colonnes appropriées:
>>> N.mean(messier, axis=0) # Moyenne sur les lignes → Series indexée sur les colonnes
Mag 7.492727e+00
Size 1.771909e+01
Distance 4.574883e+06
RA 1.303774e+01
Dec 9.281782e+00
dtype: float64
>>> N.random.seed(0)
>>> df = PD.DataFrame(
{'one': PD.Series(N.random.randn(3), index=['a', 'b', 'c']),
'two': PD.Series(N.random.randn(4), index=['a', 'b', 'c', 'd']),
'three': PD.Series(N.random.randn(3), index=['b', 'c', 'd'])})
>>> df
one three two
a 1.764052 NaN 2.240893
b 0.400157 -0.151357 1.867558
c 0.978738 -0.103219 -0.977278
d NaN 0.410599 0.950088
>>> df['four'] = df['one'] + df['two']; df # Création d'une nouvelle colonne
one three two four
a 1.764052 NaN 2.240893 4.004946
b 0.400157 -0.151357 1.867558 2.267715
c 0.978738 -0.103219 -0.977278 0.001460
d NaN 0.410599 0.950088 NaN
>>> df.sub(df.loc['b'], axis='columns') # Soustraction d'une ligne à toutes les colonnes (axis=1)
one three two four
a 1.363895 NaN 0.373335 1.737230
b 0.000000 0.000000 0.000000 0.000000
c 0.578581 0.048138 -2.844836 -2.266255
d NaN 0.561956 -0.917470 NaN
>>> df.sub(df['one'], axis='index') # Soustraction d'une colonne à toutes les lignes (axis=0 ou 'rows')
one three two four
a 0.0 NaN 0.476841 2.240893
b 0.0 -0.551514 1.467401 1.867558
c 0.0 -1.081957 -1.956016 -0.977278
d NaN NaN NaN NaN
.. index::
pair: pandas; sort_values
pair: pandas; idxmin
>>> df.sort_values(by='a', axis=1) # Tri des colonnes selon les valeurs de la ligne 'a'
one two three
a 1.764052 2.240893 NaN
b 0.400157 1.867558 -0.151357
c 0.978738 -0.977278 -0.103219
d NaN 0.950088 0.410599
>>> df.min(axis=1) # Valeur minimale le long des colonnes
a 1.764052
b -0.151357
c -0.977278
d 0.410599
dtype: float64
>>> df.idxmin(axis=1) # Colonne des valeurs minimales le long des colonnes
a one
b three
c two
d three
dtype: object
>>> df.mean(axis=0) # Moyenne sur toutes les lignes (gestion des données manquantes)
one 1.047649
three 0.052007
two 1.020315
dtype: float64
.. Note:: Si les bibliothèques d'optimisation de performances :pypi:`bottleneck`
et :pypi:`numexpr` sont installées, :mod:`pandas` en bénéficiera de façon
transparente.
Regroupement et agrégation de données
-------------------------------------
.. rubric:: Histogramme et discrétisation
.. index::
pair: pandas; value_counts
pair: pandas; cut
pair: pandas; qcut
pair: pandas; sort_index
Compter les objets Messier par constellation avec
:func:`pandas.value_counts`:
>>> PD.value_counts(messier['Con']).head(3)
Sgr 15
Vir 11
Com 8
Name: Con, dtype: int64
Partitionner les objets en 3 groupes de magnitude (par valeurs:
:func:`pandas.cut`, par quantiles: :func:`pandas.qcut`), et les
compter:
>>> PD.value_counts(PD.cut(messier['Mag'], 3)).sort_index() # Par valeurs
(1.591, 4.467] 6
(4.467, 7.333] 40
(7.333, 10.2] 64
Name: Mag, dtype: int64
>>> PD.value_counts(PD.qcut(messier['Mag'], [0, .3, .6, 1])).sort_index() # Par quantiles
(1.599, 6.697] 36
(6.697, 8.4] 38
(8.4, 10.2] 36
Name: Mag, dtype: int64
.. rubric:: *Group-by*
.. index::
pair: pandas; groupby
pair: pandas; agg
**Référence:** :doc:`pandas:user_guide/groupby`
Pandas offre la possibilité de regrouper les données selon divers
critères (:meth:`pandas.DataFrame.groupby`), de les agréger au sein
de ces groupes et de stocker les résultats dans une structure avec
indéxation hiérarchique (:meth:`pandas.DataFrame.agg`).
>>> seasonGr = messier.groupby('Season') # Retourne un DataFrameGroupBy
>>> seasonGr.groups
{'autumn': Index([u'M2', u'M15', ..., u'M103', u'M110'],
dtype='object', name=u'M'),
'spring': Index([u'M3', u'M40', ..., u'M108', u'M109'],
dtype='object', name=u'M'),
'summer': Index([u'M4', u'M5', ..., u'M102', u'M107'],
dtype='object', name=u'M'),
'winter': Index([u'M1', u'M35', ..., u'M79', u'M93'],
dtype='object', name=u'M')}
>>> seasonGr.size()
Season
autumn 14
spring 38
summer 40
winter 18
dtype: int64
>>> seasonGr.get_group('winter').head(3)
Con Dec Distance Mag NGC Name RA Size Type
M
M1 Tau 22.017 1930.0 8.4 1952 Crab Nebula 5.575 5.0 Sn
M35 Gem 24.333 859.0 5.3 2168 6.148 28.0 Oc
M36 Aur 34.133 1260.0 6.3 1960 5.602 12.0 Oc
>>> seasonGr['Size'].agg([N.mean, N.std]) # Taille moyenne et stddev par groupe
mean std
Season
autumn 24.307143 31.472588
spring 7.197368 4.183848
summer 17.965000 19.322400
winter 34.261111 29.894779
>>> seasonGr.agg({'Size': N.max, 'Mag': N.min})
Mag Size
Season
autumn 3.4 120.0
spring 6.2 22.0
summer 3.3 90.0
winter 1.6 110.0
>>> magGr = messier.groupby(
... [PD.qcut(messier['Mag'], [0, .3, .6, 1],
... labels='Bright Medium Faint'.split()),
... "Season"])
>>> magGr['Mag', 'Size'].agg({'Mag': ['count', 'mean'],
... 'Size': [N.mean, N.std]})
Mag Size
count mean mean std
Mag Season
Bright autumn 6 5.316667 45.200000 40.470878
spring 1 6.200000 16.200000 NaN
summer 15 5.673333 30.840000 26.225228
winter 13 5.138462 42.923077 30.944740
Faint autumn 4 9.225000 8.025000 4.768910
spring 30 9.236667 5.756667 2.272578
summer 7 8.971429 7.814286 9.135540
winter 3 8.566667 9.666667 6.429101
Medium autumn 4 7.500000 9.250000 3.304038
spring 7 7.714286 12.085714 5.587316
summer 18 7.366667 11.183333 4.825453
winter 2 7.550000 14.850000 8.697413
.. rubric:: Tableau croisé (*Pivot table*)
.. index::
pair: pandas; pivot_table
**Référence:** :doc:`pandas:user_guide/reshaping`
Calculer la magnitude et la taille moyennes des objets Messier selon
leur type avec :meth:`pandas.DataFrame.pivot_table`:
>>> messier['Mag Size Type'.split()].pivot_table(columns='Type')
Type As Ba Di ... Pl Sn Sp
Mag 9.0 9.85 7.216667 ... 9.050 8.4 8.495652
Size 2.8 4.80 33.333333 ... 3.425 5.0 15.160870
Visualisations
--------------
.. index:: module: seaborn
.. rubric:: Exemple:
:ref:`Démonstration Pandas/Seaborn `
(:download:`pokemon.ipynb`) sur le jeu de données :download:`Pokemon.csv`.
.. rubric:: Références:
* :doc:`pandas:user_guide/visualization`
* `Seaborn: statistical data visualization `_
.. rubric:: Autres exemples de visualisation de jeux de données complexes
(utilisation de pandas et seaborn)
* `Iris Dataset `_
* `Histoire des sujets télévisés
`_
|fr|
.. rubric:: Liens
* :doc:`Pandas tutorials `
* `Pandas Cookbook `_
* `Pandas Lessons for New Users `_
* `Practical Data Analysis `_
.. rubric:: Exercices:
* `Exercices for New Users `_
.. rubric:: Voir également:
* :rtfd:`geopandas`: extension pour des opérations spatiales sur des formes
géométriques
.. _myXarray:
Xarray
------
.. index:: module: xarray
xarray_ est une bibliothèque pour la structuration de données
*homogènes* de dimension arbitraire. Suivant la philosophie de la
bibliothèque Pandas_ dont elle est issue (et dont elle dépend), elle
permet notamment de nommer les différentes dimensions (*axes*) des
tableaux (p.ex. `x.sum(axis='time')`), d'indexer les données
(p.ex. `x.loc['M31']`), de naturellement gérer les opérations de
*broadcasting*, des opérations de regroupement et d'agrégation
(p.ex. `x.groupby(time.dayofyear).mean()`), une gestion plus facile
des données manquantes et d'alignement des tableaux indexés
(p.ex. `align(x, y, join='outer')`).
*pandas excels at working with tabular data. That suffices for many
statistical analyses, but physical scientists rely on N-dimensional arrays –
which is where xarray comes in.*
.. index::
pair: xarray; DataArray
pair: xarray; Dataset
pair: xarray; loc
pair: xarray; sel
:mod:`xarray` fournit deux structures principales, héritées du format `netCDF
`_ :
* :class:`xarray.DataArray`, un tableau N-D indexé généralisant le
:class:`pandas.Series`;
* :class:`xarray.Dataset`, un dictionnaire regroupant plusieurs `DataArray`
alignés selon une ou plusieurs dimensions, et similaire au
:class:`pandas.DataFrame`.
.. Note:: La convention utilisée ici est « `import xarray as X` ».
>>> N.random.seed(0)
>>> data = X.DataArray(N.arange(3*4, dtype=float).reshape((3, 4)), # Tableau de données
... dims=('x', 'y'), # Nom des dimensions
... coords={'x': list('abc')}, # Indexation des coordonnées en 'x'
... name='mesures', # Nom du tableau
... attrs=dict(author='Y. Copin')) # Métadonnées
>>> data
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
Coordinates:
* x (x) >> data.to_pandas() # Conversion en DataFrame à indexation simple
y 0 1 2 3
x
a 0.0 1.0 2.0 3.0
b 4.0 5.0 6.0 7.0
c 8.0 9.0 10.0 11.0
>>> data.to_dataframe() # Conversion en DataFrame multi-indexé (hiérarchique)
mesures
x y
a 0 0.0
1 1.0
2 2.0
3 3.0
b 0 4.0
1 5.0
2 6.0
3 7.0
c 0 8.0
1 9.0
2 10.0
3 11.0
>>> data.dims
('x', 'y')
>>> data.coords
Coordinates:
* x (x) >> data.values
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
>>> data.attrs
OrderedDict([('author', 'Y. Copin')])
>>> data[:, 1] # Accès par indices
array([ 1., 5., 9.])
Coordinates:
* x (x) >> data.loc['a':'b', -1] # Accès par labels
array([ 3., 7.])
Coordinates:
* x (x) >> data.sel(x=['a', 'c'], y=2)
array([ 2., 10.])
Coordinates:
* x (x) >> data.mean(dim='x') # Moyenne le long de la dimension 'x' = data.mean(axis=0)
array([ 4., 5., 6., 7.])
Dimensions without coordinates: y
>>> data2 = X.DataArray(N.arange(6).reshape(2, 3) * 10,
... dims=('z', 'x'), coords={'x': list('abd')})
>>> data2.to_pandas()
x a b d
z
0 0 10 20
1 30 40 50
>>> data.to_pandas() # REMINDER
y 0 1 2 3
x
a 0.0 1.0 2.0 3.0
b 4.0 5.0 6.0 7.0
c 8.0 9.0 10.0 11.0
>>> data2.values + data.values # Opération sur des tableaux numpy incompatibles
ValueError: operands could not be broadcast together with shapes (2,3) (3,4)
>>> data2 + data # Alignement automatique sur les coord. communes!
array([[[ 0., 1., 2., 3.],
[ 14., 15., 16., 17.]],
[[ 30., 31., 32., 33.],
[ 44., 45., 46., 47.]]])
Coordinates:
* x (x) object 'a' 'b'
Dimensions without coordinates: z, y
>>> data['isSmall'] = data.sum(dim='y') < 10; data # Booléen "Somme sur y < 10"
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
Coordinates:
* x (x) >> data.groupby('isSmall').mean(dim='x') # Regroupement et agrégation
array([[ 6., 7., 8., 9.],
[ 0., 1., 2., 3.]])
Coordinates:
* isSmall (isSmall) object False True
Dimensions without coordinates: y
.. rubric:: Exemples plus complexes:
* :doc:`xarray:examples`
.. Note:: N'oubliez pas de `citer xarray
`_
dans vos publications et présentations.
Autres types de tableaux
------------------------
Il existe de nombreuses autres bibliothèques proposant des tableaux avancés avec
une syntaxe de type `numpy`, p.ex.:
* :pypi:`awkward1`: tableaux hétérogènes de taille variable avec données
manquantes,
* :pypi:`cupy`: tableaux sur :abbr:`GPU (Graphics Processing Unit)` via CUDA,
* :pypi:`sparse`: tableaux clairsemés,
* etc.
Voir `Beyond Numpy Arrays in Python
`_ pour une
discussion.
.. _myAstropy:
Astropy
=======
.. index:: module: astropy
:rtfd:`astropy` est une bibliothèque astronomique maintenue par la communauté et
visant à fédérer les efforts jusque là disparates. Elle offre en outre une
interface unifiée à des `bibliothèques affiliées
`_ plus spécifiques.
Tour d'horizon (v4.0)
---------------------
- Structures de base:
- :mod:`astropy.constants`: constantes fondamentales (voir également
:mod:`scipy.constants`);
- :mod:`astropy.units`: unités et quantités dimensionnées;
- :mod:`astropy.nddata`: extension des :class:`numpy.ndarray` (incluant
métadonnées, masque, unité, incertitude, etc.);
- :mod:`astropy.table`: tableaux hétérogènes;
- :mod:`astropy.time`: manipulation du temps et des dates;
- :mod:`astropy.timeseries`: manipulation de séries temporelles;
- :mod:`astropy.coordinates`: systèmes de coordonnées;
- :mod:`astropy.wcs`: *World Coordinate System*;
- :mod:`astropy.modeling`: modèles et ajustements;
- :mod:`astropy.uncertainty`: manipulation d'incertitudes.
- Entrées/sorties:
- :mod:`astropy.io.fits`: fichiers FITS;
- :mod:`astropy.io.ascii`: tables ASCII;
- :mod:`astropy.io.votable`: XML *Virtual Observatory* tables;
- :mod:`astropy.io.misc`: divers;
- :mod:`astropy.vo`: accès au *Virtual Observatory*.
- Calculs astronomiques:
- :mod:`astropy.cosmology`: calculs cosmologiques;
- :mod:`astropy.convolution`: convolution et filtrage;
- :mod:`astropy.visualization`: visualisation de données;
- :mod:`astropy.stats`: méthodes astrostatistiques.
Démonstration
-------------
:ref:`Démonstration Astropy ` (:download:`astropy.ipynb`)
.. rubric:: Voir également:
* `AstroBetter tutorials
`_
.. Note:: N'oubliez pas de citer [Astropy13]_ ou de `mentionner
l'utilisation `_
d'astropy dans vos publications et présentations.
Autres bibliothèques scientifiques
==================================
Python est maintenant très largement utilisé par la communauté scientifique, et
dispose d'innombrables bibliothèques dédiées aux différents domaines de
l'analyse statistique de données, physique, chimie, etc.:
- Analyse et visualisation interactives de données: `Veusz
`_;
- Propagation des incertitudes: :pypi:`uncertainties`;
- Ajustement & optimisation (statistiques fréquentistes): :rtfd:`iminuit`,
:rtfd:`kafe`, :rtfd:`zfit` (basé sur `TensorFlow
`_));
- Analyse statistique bayesienne: `PyStan
`_;
- *Markov Chain Monte-Carlo*: :rtfd:`emcee`, :pypi:`pymc3`;
- *Machine Learning*: `mlpy `_, :pypi:`milk`,
:pypi:`mdp`, `Keras `_ et autres `modules d'intelligence
artificielle `_;
- Astronomie: `Kapteyn `_, `AstroML
`_, `HyperSpy `_;
- Mécanique quantique: `QuTiP `_;
- Électromagnétisme: `EMpy `_;
- Optique physique: :rtfd:`Physical Optics Propagation in PYthon
`, `OpticsPy `_;
- *PDE solver*: `FiPy `_, `SfePy
`_;
- Calcul symbolique: `sympy `_ (voir également
ce `tutoriel sympy
`_) et `sage
`_;
- `pyroot `_ & `rootpy
`_;
- `High Performance Computing in Python
`_;
- `Etc. `_
.. rubric:: Notes de bas de page et références bibliographiques
.. [#panels] Les structures :class:`pandas.Panel` (de rang 3), et
:class:`pandas.Panel4D` (de rang 4) et :class:`pandas.PanelND` (de rang
arbitraire) ont été respectivement **dépréciées** aux versions 0.20 et 0.19.
Utiliser une indexation hiérarchique ou :mod:`xarray`.
.. [Astropy13] :ads:`2013A&A...558A..33A`
.. |fr| image:: ../_static/france_flag_icon.png
:alt: Fr
.. |en| image:: ../_static/uk_flag_icon.png
:alt: En