Numpy, Pandas et Matplotlib en express

Numpy, pandas et Matplotlib sont trois librairies incontournables de l’écosystème scientifique de Python: * Numpy permet de créer des tableaux à plusieurs dimensions contenant des nombres, et de les transformer facilement à l’aide de nombreuses formules mathématique, elle a l’avantage d’accélérer les calculs comparé à Python, * pandas permet de créer des tableaux contenant des objets de type différent, et d’effectuer des opérations très similaires à celles qu’on peut effectuer avec Excel, * Matplotlib permet de créer des figures et de les personnaliser dans les moindres détails.

Ces trois librairies, développées depuis des années et des années, sont très fournies. L’objectif de ce tutoriel est de donner une impression de ce qu’il est possible de faire avec elles.

Numpy

On importe Numpy de la manière suivante, établie par convention.

[68]:
import numpy as np

Les calculs réalisés avec Numpy sont plus rapides que les calculs effectués avec du pur Python.

[69]:
%timeit sum(range(100_000))
%timeit np.sum(np.arange(100_000, dtype=np.int64))
2.14 ms ± 103 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
77.5 µs ± 615 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

On a maintenant à disposition des dizaines et des dizaines de fonctions pour réaliser des opérations mathématiques.

[3]:
print(dir(np))
['ALLOW_THREADS', 'AxisError', 'BUFSIZE', 'CLIP', 'ComplexWarning', 'DataSource', 'ERR_CALL', 'ERR_DEFAULT', 'ERR_IGNORE', 'ERR_LOG', 'ERR_PRINT', 'ERR_RAISE', 'ERR_WARN', 'FLOATING_POINT_SUPPORT', 'FPE_DIVIDEBYZERO', 'FPE_INVALID', 'FPE_OVERFLOW', 'FPE_UNDERFLOW', 'False_', 'Inf', 'Infinity', 'MAXDIMS', 'MAY_SHARE_BOUNDS', 'MAY_SHARE_EXACT', 'MachAr', 'ModuleDeprecationWarning', 'NAN', 'NINF', 'NZERO', 'NaN', 'PINF', 'PZERO', 'RAISE', 'RankWarning', 'SHIFT_DIVIDEBYZERO', 'SHIFT_INVALID', 'SHIFT_OVERFLOW', 'SHIFT_UNDERFLOW', 'ScalarType', 'Tester', 'TooHardError', 'True_', 'UFUNC_BUFSIZE_DEFAULT', 'UFUNC_PYVALS_NAME', 'VisibleDeprecationWarning', 'WRAP', '_NoValue', '_UFUNC_API', '__NUMPY_SETUP__', '__all__', '__builtins__', '__cached__', '__config__', '__doc__', '__file__', '__git_revision__', '__loader__', '__mkl_version__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_add_newdoc_ufunc', '_arg', '_distributor_init', '_globals', '_mat', '_mklinit', '_pytesttester', 'abs', 'absolute', 'absolute_import', 'add', 'add_docstring', 'add_newdoc', 'add_newdoc_ufunc', 'alen', 'all', 'allclose', 'alltrue', 'amax', 'amin', 'angle', 'any', 'append', 'apply_along_axis', 'apply_over_axes', 'arange', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', 'arctanh', 'argmax', 'argmin', 'argpartition', 'argsort', 'argwhere', 'around', 'array', 'array2string', 'array_equal', 'array_equiv', 'array_repr', 'array_split', 'array_str', 'asanyarray', 'asarray', 'asarray_chkfinite', 'ascontiguousarray', 'asfarray', 'asfortranarray', 'asmatrix', 'asscalar', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'average', 'bartlett', 'base_repr', 'binary_repr', 'bincount', 'bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor', 'blackman', 'block', 'bmat', 'bool', 'bool8', 'bool_', 'broadcast', 'broadcast_arrays', 'broadcast_to', 'busday_count', 'busday_offset', 'busdaycalendar', 'byte', 'byte_bounds', 'bytes0', 'bytes_', 'c_', 'can_cast', 'cast', 'cbrt', 'cdouble', 'ceil', 'cfloat', 'char', 'character', 'chararray', 'choose', 'clip', 'clongdouble', 'clongfloat', 'column_stack', 'common_type', 'compare_chararrays', 'compat', 'complex', 'complex128', 'complex64', 'complex_', 'complexfloating', 'compress', 'concatenate', 'conj', 'conjugate', 'convolve', 'copy', 'copysign', 'copyto', 'core', 'corrcoef', 'correlate', 'cos', 'cosh', 'count_nonzero', 'cov', 'cross', 'csingle', 'ctypeslib', 'cumprod', 'cumproduct', 'cumsum', 'datetime64', 'datetime_as_string', 'datetime_data', 'deg2rad', 'degrees', 'delete', 'deprecate', 'deprecate_with_doc', 'diag', 'diag_indices', 'diag_indices_from', 'diagflat', 'diagonal', 'diff', 'digitize', 'disp', 'divide', 'division', 'divmod', 'dot', 'double', 'dsplit', 'dstack', 'dtype', 'e', 'ediff1d', 'einsum', 'einsum_path', 'emath', 'empty', 'empty_like', 'equal', 'errstate', 'euler_gamma', 'exp', 'exp2', 'expand_dims', 'expm1', 'extract', 'eye', 'fabs', 'fastCopyAndTranspose', 'fft', 'fill_diagonal', 'find_common_type', 'finfo', 'fix', 'flatiter', 'flatnonzero', 'flexible', 'flip', 'fliplr', 'flipud', 'float', 'float16', 'float32', 'float64', 'float_', 'float_power', 'floating', 'floor', 'floor_divide', 'fmax', 'fmin', 'fmod', 'format_float_positional', 'format_float_scientific', 'format_parser', 'frexp', 'frombuffer', 'fromfile', 'fromfunction', 'fromiter', 'frompyfunc', 'fromregex', 'fromstring', 'full', 'full_like', 'fv', 'gcd', 'generic', 'genfromtxt', 'geomspace', 'get_array_wrap', 'get_include', 'get_printoptions', 'getbufsize', 'geterr', 'geterrcall', 'geterrobj', 'gradient', 'greater', 'greater_equal', 'half', 'hamming', 'hanning', 'heaviside', 'histogram', 'histogram2d', 'histogram_bin_edges', 'histogramdd', 'hsplit', 'hstack', 'hypot', 'i0', 'identity', 'iinfo', 'imag', 'in1d', 'index_exp', 'indices', 'inexact', 'inf', 'info', 'infty', 'inner', 'insert', 'int', 'int0', 'int16', 'int32', 'int64', 'int8', 'int_', 'int_asbuffer', 'intc', 'integer', 'interp', 'intersect1d', 'intp', 'invert', 'ipmt', 'irr', 'is_busday', 'isclose', 'iscomplex', 'iscomplexobj', 'isfinite', 'isfortran', 'isin', 'isinf', 'isnan', 'isnat', 'isneginf', 'isposinf', 'isreal', 'isrealobj', 'isscalar', 'issctype', 'issubclass_', 'issubdtype', 'issubsctype', 'iterable', 'ix_', 'kaiser', 'kron', 'lcm', 'ldexp', 'left_shift', 'less', 'less_equal', 'lexsort', 'lib', 'linalg', 'linspace', 'little_endian', 'load', 'loads', 'loadtxt', 'log', 'log10', 'log1p', 'log2', 'logaddexp', 'logaddexp2', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'logspace', 'long', 'longcomplex', 'longdouble', 'longfloat', 'longlong', 'lookfor', 'ma', 'mafromtxt', 'mask_indices', 'mat', 'math', 'matmul', 'matrix', 'matrixlib', 'max', 'maximum', 'maximum_sctype', 'may_share_memory', 'mean', 'median', 'memmap', 'meshgrid', 'mgrid', 'min', 'min_scalar_type', 'minimum', 'mintypecode', 'mirr', 'mod', 'modf', 'moveaxis', 'msort', 'multiply', 'nan', 'nan_to_num', 'nanargmax', 'nanargmin', 'nancumprod', 'nancumsum', 'nanmax', 'nanmean', 'nanmedian', 'nanmin', 'nanpercentile', 'nanprod', 'nanquantile', 'nanstd', 'nansum', 'nanvar', 'nbytes', 'ndarray', 'ndenumerate', 'ndfromtxt', 'ndim', 'ndindex', 'nditer', 'negative', 'nested_iters', 'newaxis', 'nextafter', 'nonzero', 'not_equal', 'nper', 'npv', 'numarray', 'number', 'obj2sctype', 'object', 'object0', 'object_', 'ogrid', 'oldnumeric', 'ones', 'ones_like', 'outer', 'packbits', 'pad', 'partition', 'percentile', 'pi', 'piecewise', 'place', 'pmt', 'poly', 'poly1d', 'polyadd', 'polyder', 'polydiv', 'polyfit', 'polyint', 'polymul', 'polynomial', 'polysub', 'polyval', 'positive', 'power', 'ppmt', 'print_function', 'printoptions', 'prod', 'product', 'promote_types', 'ptp', 'put', 'put_along_axis', 'putmask', 'pv', 'quantile', 'r_', 'rad2deg', 'radians', 'random', 'rank', 'rate', 'ravel', 'ravel_multi_index', 'real', 'real_if_close', 'rec', 'recarray', 'recfromcsv', 'recfromtxt', 'reciprocal', 'record', 'remainder', 'repeat', 'require', 'reshape', 'resize', 'result_type', 'right_shift', 'rint', 'roll', 'rollaxis', 'roots', 'rot90', 'round', 'round_', 'row_stack', 's_', 'safe_eval', 'save', 'savetxt', 'savez', 'savez_compressed', 'sctype2char', 'sctypeDict', 'sctypeNA', 'sctypes', 'searchsorted', 'select', 'set_numeric_ops', 'set_printoptions', 'set_string_function', 'setbufsize', 'setdiff1d', 'seterr', 'seterrcall', 'seterrobj', 'setxor1d', 'shape', 'shares_memory', 'short', 'show_config', 'sign', 'signbit', 'signedinteger', 'sin', 'sinc', 'single', 'singlecomplex', 'sinh', 'size', 'sometrue', 'sort', 'sort_complex', 'source', 'spacing', 'split', 'sqrt', 'square', 'squeeze', 'stack', 'std', 'str', 'str0', 'str_', 'string_', 'subtract', 'sum', 'swapaxes', 'sys', 'take', 'take_along_axis', 'tan', 'tanh', 'tensordot', 'test', 'testing', 'tile', 'timedelta64', 'trace', 'tracemalloc_domain', 'transpose', 'trapz', 'tri', 'tril', 'tril_indices', 'tril_indices_from', 'trim_zeros', 'triu', 'triu_indices', 'triu_indices_from', 'true_divide', 'trunc', 'typeDict', 'typeNA', 'typecodes', 'typename', 'ubyte', 'ufunc', 'uint', 'uint0', 'uint16', 'uint32', 'uint64', 'uint8', 'uintc', 'uintp', 'ulonglong', 'unicode', 'unicode_', 'union1d', 'unique', 'unpackbits', 'unravel_index', 'unsignedinteger', 'unwrap', 'ushort', 'vander', 'var', 'vdot', 'vectorize', 'version', 'void', 'void0', 'vsplit', 'vstack', 'warnings', 'where', 'who', 'zeros', 'zeros_like']

L’objet le plus important est le ndarray, pour tableau à n dimensions.

[71]:
arr = np.array([1, 2, 3])
type(arr)
[71]:
numpy.ndarray
[72]:
arr = np.array([[1, 2, 3], [4, 5, 6]])
arr
[72]:
array([[1, 2, 3],
       [4, 5, 6]])
[73]:
arr.shape
[73]:
(2, 3)
[74]:
arr.T
[74]:
array([[1, 4],
       [2, 5],
       [3, 6]])
[8]:
arr.T.shape
[8]:
(3, 2)
[9]:
np.arange(10, 50, 3)
[9]:
array([10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49])
[10]:
np.zeros(shape=10)
[10]:
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
[75]:
np.zeros(shape=(2, 3))
[75]:
array([[0., 0., 0.],
       [0., 0., 0.]])
[12]:
np.zeros(shape=(2, 3, 1, 5))
[12]:
array([[[[0., 0., 0., 0., 0.]],

        [[0., 0., 0., 0., 0.]],

        [[0., 0., 0., 0., 0.]]],


       [[[0., 0., 0., 0., 0.]],

        [[0., 0., 0., 0., 0.]],

        [[0., 0., 0., 0., 0.]]]])
[13]:
np.ones(shape=(2, 5))
[13]:
array([[1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.]])
[14]:
arr = np.array([[1, 2, 3], [10, 20, 30]]).T
arr
[14]:
array([[ 1, 10],
       [ 2, 20],
       [ 3, 30]])

On accède à un élément d’un objet ndarray en précisant sa position (en partant de 0) suivant les axes de l’objet, séparés de virgules. Pour un tableau à deux dimensions (une matrice), le premier axe est celui des lignes, le deuxième celui des colonnes.

[76]:
arr[0, 0]
[76]:
1
[16]:
arr[2, 1]
[16]:
30

Pour accéder à tous les éléments suivant un axe (les colonnes, les lignes, etc.), on utilise le symbole :.

[77]:
arr[:, 0]
[77]:
array([1, 4])
[78]:
arr[:, 1]
[78]:
array([2, 5])
[19]:
arr[0, :]
[19]:
array([ 1, 10])

On peut effectuer des opérations mathématiques sur tous les éléments d’un ndarray.

[79]:
arr + 1
[79]:
array([[2, 3, 4],
       [5, 6, 7]])
[80]:
arr * 3
[80]:
array([[ 3,  6,  9],
       [12, 15, 18]])

Les ndarray sont des objets mutables.

[22]:
arr[0, 0] = -1
arr
[22]:
array([[-1, 10],
       [ 2, 20],
       [ 3, 30]])
[23]:
arr[0, :] = 1
arr
[23]:
array([[ 1,  1],
       [ 2, 20],
       [ 3, 30]])
[24]:
arr[:, 1] = 3
arr
[24]:
array([[1, 3],
       [2, 3],
       [3, 3]])
[25]:
arr[:, 1] = arr[:, 1] * arr[:, 0]
arr
[25]:
array([[1, 3],
       [2, 6],
       [3, 9]])

On peut sélectionner des éléments d’un ndarray suivant une condition.

[26]:
arr > 5
[26]:
array([[False, False],
       [False,  True],
       [False,  True]])
[82]:
arr[arr > 5]
[82]:
array([6])

On peut piocher dans les dizaines de fonction dont Numpy dispose pour transformer les données

[83]:
np.sum(arr)
[83]:
21

axis=0 signifie que l’addition est réalisée suivant le dimension 0, ce sont donc les lignes qui sont additionnées. axis=1 indique que ce sont les colonnes qui sont additionnées.

[84]:
np.sum(arr, axis=0)
[84]:
array([5, 7, 9])
[85]:
np.sum(arr, axis=1)
[85]:
array([ 6, 15])

pandas

On importe pandas de la manière suivante, établie par convention.

[86]:
import pandas as pd

L’objet le plus important est le DataFrame (tableau de données). On peut en créer de plusieurs manières différentes.

[87]:
df = pd.DataFrame({
    "name": ["Rachel", "Bob", "Bill", "Anna", "Leila", "John"],
    "age": [23, 25, 24, 30, 19, 26],
    "height": [1.85, 1.79, 1.82, 1.72, 1.95, 1.65]
})
[88]:
type(df)
[88]:
pandas.core.frame.DataFrame
[89]:
df
[89]:
name age height
0 Rachel 23 1.85
1 Bob 25 1.79
2 Bill 24 1.82
3 Anna 30 1.72
4 Leila 19 1.95
5 John 26 1.65
[90]:
df.head()
[90]:
name age height
0 Rachel 23 1.85
1 Bob 25 1.79
2 Bill 24 1.82
3 Anna 30 1.72
4 Leila 19 1.95
[91]:
df.tail()
[91]:
name age height
1 Bob 25 1.79
2 Bill 24 1.82
3 Anna 30 1.72
4 Leila 19 1.95
5 John 26 1.65
[92]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 3 columns):
name      6 non-null object
age       6 non-null int64
height    6 non-null float64
dtypes: float64(1), int64(1), object(1)
memory usage: 272.0+ bytes
[93]:
df.describe()
[93]:
age height
count 6.000000 6.000000
mean 24.500000 1.796667
std 3.619392 0.104243
min 19.000000 1.650000
25% 23.250000 1.737500
50% 24.500000 1.805000
75% 25.750000 1.842500
max 30.000000 1.950000
[94]:
df.shape
[94]:
(6, 3)

Les entrées d’un tableau sont son index (ses lignes) et ses colonnes.

[40]:
df.index
[40]:
RangeIndex(start=0, stop=6, step=1)

Pour l’instant l’index du DataFrame est juste un indice numérique démarrant à 0. On peut définir un index plus intéressant en utilisant la colonne name.

[95]:
df = df.set_index("name")
df
[95]:
age height
name
Rachel 23 1.85
Bob 25 1.79
Bill 24 1.82
Anna 30 1.72
Leila 19 1.95
John 26 1.65
[42]:
df.columns
[42]:
Index(['age', 'height'], dtype='object')

On peut indexer le tableau avec les méthodes loc et iloc.

[96]:
df.loc["Bob"]
[96]:
age       25.00
height     1.79
Name: Bob, dtype: float64
[97]:
df.loc["Bob", "height"]
[97]:
1.79
[98]:
df.iloc[1, 1]
[98]:
1.79

On peut sélectionner une colonne entière pour l’utiliser et la modifier

[99]:
df["height"]
[99]:
name
Rachel    1.85
Bob       1.79
Bill      1.82
Anna      1.72
Leila     1.95
John      1.65
Name: height, dtype: float64
[100]:
df["height"] = df["height"] + 0.2
df.head()
[100]:
age height
name
Rachel 23 2.05
Bob 25 1.99
Bill 24 2.02
Anna 30 1.92
Leila 19 2.15

On peut ajouter des colonnes.

[101]:
df["age_plus_10"] = df["age"] + 10
df.head()
[101]:
age height age_plus_10
name
Rachel 23 2.05 33
Bob 25 1.99 35
Bill 24 2.02 34
Anna 30 1.92 40
Leila 19 2.15 29

Lorsqu’on les données d’une colonne sont des nombres, on a en fait à disposition toutes les fonctions de Numpy. On a aussi bien d’autres fonctions (des méthodes en fait) disponibles.

[102]:
df["age"].sum()
[102]:
147

On peut facilement lire et écrire des fichiers (.csv par exemple) avec pandas.

[103]:
%%writefile data.csv
name,age,height,gender
Sarah,27,1.67,F
Bob,28,1.89,M
Rachel,24,1.81,F
Bill,22,1.73,M
John,26,1.67,M
Leila,19,1.78,F
Writing data.csv
[104]:
people = pd.read_csv("data.csv")
people
[104]:
name age height gender
0 Sarah 27 1.67 F
1 Bob 28 1.89 M
2 Rachel 24 1.81 F
3 Bill 22 1.73 M
4 John 26 1.67 M
5 Leila 19 1.78 F

On peut compter le nombre d’éléments identiques dans une colonne avec la méthode value_counts. Cette méthode est disponible pour les objets de type Series qui représentent en fait les colonnes. La méthode retourne aussi un objet de type `Series.

[105]:
people["gender"].value_counts()
[105]:
M    3
F    3
Name: gender, dtype: int64

On peut grouper les données et agréger les groupes obtenus à l’aide de fonctions.

[106]:
people.groupby("gender").agg({"age": ["max", "min"], "height":["mean", "std"]})
[106]:
age height
max min mean std
gender
F 27 19 1.753333 0.073711
M 28 22 1.763333 0.113725

On peut sélectionner des données suivant des conditions.

[107]:
people["gender"] == "F"
[107]:
0     True
1    False
2     True
3    False
4    False
5     True
Name: gender, dtype: bool
[109]:
girls = people[people["gender"] == "F"]
girls
[109]:
name age height gender
0 Sarah 27 1.67 F
2 Rachel 24 1.81 F
5 Leila 19 1.78 F

On peut supprimer une colonne avec la méthode drop.

[110]:
girls = girls.drop(columns=["gender"])
girls
[110]:
name age height
0 Sarah 27 1.67
2 Rachel 24 1.81
5 Leila 19 1.78
[111]:
girls.to_csv("girls.csv", index=False)
[112]:
!type  girls.csv
name,age,height
Sarah,27,1.67
Rachel,24,1.81
Leila,19,1.78
[113]:
!del /f data.csv girls.csv

Matplotlib

On importe Matplotlib de la manière suivante, établie par convention.

[114]:
import matplotlib.pyplot as plt

Matplotlib a un mode similaire à celui de Matlab, on enchaîne juste des appels à des fonctions pour réaliser des actions sur le même objet.

[115]:
plt.scatter([1, 3, 10], [-2, 3, 0])
plt.title("Titre")
plt.xlabel("X axis")
plt.savefig("comme_matlab.png")
_images/scientific_libs_91_0.png
[116]:
!comme_matlab.png

On va préférer la méthode par laquelle on modifier directement la figure au travers des objets qui la constituent.

[117]:
fig, ax = plt.subplots()
ax.scatter([1, 3, 10], [-2, 3, 0])
ax.set_title("Titre")
ax.set_xlabel("X Axis")
fig.savefig("avec_des_objets.jpg")
_images/scientific_libs_94_0.png
[118]:
!image_with_matplotlib.png
'image_with_matplotlib.png' n'est pas reconnu en tant que commande interne
ou externe, un programme ex‚cutable ou un fichier de commandes.

On peut utiliser Matplotlib directement mais on retrouve en fait la librairie un peu partout, dans pandas notamment.

[119]:
ax = df.plot(x="age", y="height", kind="scatter", title="Titre")
fig = ax.get_figure()
fig.savefig("avec_pandas.pdf")
_images/scientific_libs_97_0.png
[120]:
!avec_pandas.pdf
[67]:
!del /f comme_matlab.png avec_des_objets.jpg avec_pandas.pdf