Numpy, la brique de base de la data science

Numpy constitue la brique de base de l’écosystème de la data science en Python. Toutes les librairies de manipulation de données, de modélisation et de visualisation reposent, de manière plus ou moins directe, sur Numpy. Il est donc indispensable de revoir quelques notions sur ce package avant d’aller plus loin.

Tutoriel
Manipulation
Author

Lino Galiana

Published

2024-04-12

Pour essayer les exemples présents dans ce tutoriel :

Download nbviewer Onyxia Onyxia
Open In Colab githubdev

Il est recommandé de régulièrement se référer à la cheatsheet numpy et à la doc officielle en cas de doute sur une fonction.

Dans ce chapitre, on ne dérogera pas à la convention qui s’est imposée d’importer Numpy de la manière suivante :

import numpy as np

Nous allons également fixer la racine du générateur aléatoire de nombres afin d’avoir des résultats reproductibles :

np.random.seed(12345)

Si les scripts suivants sont exécutés dans un Notebook Jupyter, il est recommandé d’utiliser les paramètres suivants pour contrôler le rendu:

from IPython.core.interactiveshell import InteractiveShell

InteractiveShell.ast_node_interactivity = "all"

Le concept d’array

Le concept central de NumPy (Numerical Python) est l’array qui est un tableau de données multidimensionnel.

L’array numpy peut être unidimensionnel et s’apparenter à un vecteur (1d-array), bidimensionnel et ainsi s’apparenter à une matrice (2d-array) ou, de manière plus générale, prendre la forme d’un objet multidimensionnel (Nd-array).

Les tableaux simples (uni ou bi-dimensionnels) sont faciles à se représenter et seront particulièrement utilisés dans le paradigme des DataFrames mais la possibilité d’avoir des objets multidimensionnels permettra d’exploiter des structures très complexes.

Un DataFrame sera construit à partir d’une collection d’array uni-dimensionnels (les variables de la table), ce qui permettra d’effectuer des opérations cohérentes (et optimisées) avec le type de la variable.

Par rapport à une liste,

  • un array ne peut contenir qu’un type de données (integer, string, etc.), contrairement à une liste.
  • les opérations implémentées par numpy seront plus efficaces et demanderont moins de mémoire

Les données géographiques constitueront une construction un peu plus complexe qu’un DataFrame traditionnel. La dimension géographique prend la forme d’un tableau plus profond, au moins bidimensionnel (coordonnées d’un point).

Créer un array

On peut créer un array de plusieurs manières. Pour créer un array à partir d’une liste, il suffit d’utiliser la méthode array:

np.array([1, 2, 5])
array([1, 2, 5])

Il est possible d’ajouter un argument dtype pour contraindre le type du array :

np.array([["a", "z", "e"], ["r", "t"], ["y"]], dtype="object")
array([list(['a', 'z', 'e']), list(['r', 't']), list(['y'])], dtype=object)

Il existe aussi des méthodes pratiques pour créer des array:

  • séquences logiques : np.arange (suite) ou np.linspace (interpolation linéaire entre deux bornes)
  • séquences ordonnées : array rempli de zéros, de 1 ou d’un nombre désiré : np.zeros, np.ones ou np.full
  • séquences aléatoires : fonctions de génération de nombres aléatoires : np.rand.uniform, np.rand.normal, etc.
  • tableau sous forme de matrice identité : np.eye

Ceci donne ainsi, pour les séquences logiques:

np.arange(0, 10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.arange(0, 10, 3)
array([0, 3, 6, 9])
np.linspace(0, 1, 5)
array([0.  , 0.25, 0.5 , 0.75, 1.  ])

Pour un array initialisé à 0:

np.zeros(10, dtype=int)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

ou initialisé à 1:

np.ones((3, 5), dtype=float)
array([[1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.]])

ou encore initialisé à 3.14:

array([[3.14, 3.14, 3.14, 3.14, 3.14],
       [3.14, 3.14, 3.14, 3.14, 3.14],
       [3.14, 3.14, 3.14, 3.14, 3.14]])

Enfin, pour créer la matrice \(I_3\):

np.eye(3)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

Indexation et slicing

Logique dans le cas d’un array unidimensionnel

La structure la plus simple est l’array unidimensionnel:

x = np.arange(10)
print(x)
[0 1 2 3 4 5 6 7 8 9]

L’indexation est dans ce cas similaire à celle d’une liste:

  • le premier élément est 0
  • le énième élément est accessible à la position \(n-1\)

La logique d’accès aux éléments est ainsi la suivante :

x[start:stop:step]

Avec un array unidimensionnel, l’opération de slicing (garder une coupe du array) est très simple. Par exemple, pour garder les K premiers éléments d’un array, on fera:

x[: (K - 1)]

En l’occurrence, on sélectionne le K\(^{eme}\) élément en utilisant

x[K - 1]

Pour sélectionner uniquement un élément, on fera ainsi:

x = np.arange(10)
x[2]
2

Les syntaxes qui permettent de sélectionner des indices particuliers d’une liste fonctionnent également avec les arrays.

Sur la performance

Un élément déterminant dans la performance de numpy par rapport aux listes, lorsqu’il est question de slicing est qu’un array ne renvoie pas une copie de l’élément en question (copie qui coûte de la mémoire et du temps) mais simplement une vue de celui-ci.

Lorsqu’il est nécessaire d’effectuer une copie, par exemple pour ne pas altérer l’array sous-jacent, on peut utiliser la méthode copy:

x_sub_copy = x[:2, :2].copy()

Filtres logiques

Il est également possible, et plus pratique, de sélectionner des données à partir de conditions logiques (opération qu’on appelle un boolean mask). Cette fonctionalité servira principalement à effectuer des opérations de filtre sur les données.

Pour des opérations de comparaison simples, les comparateurs logiques peuvent être suffisants. Ces comparaisons fonctionnent aussi sur les tableaux multidimensionnels grâce au broadcasting sur lequel nous reviendrons :

x = np.arange(10)
x2 = np.array([[-1, 1, -2], [-3, 2, 0]])
print(x)
print(x2)
[0 1 2 3 4 5 6 7 8 9]
[[-1  1 -2]
 [-3  2  0]]
x == 2
x2 < 0
array([[ True, False,  True],
       [ True, False, False]])

Pour sélectionner les observations relatives à la condition logique, il suffit d’utiliser la logique de slicing de numpy qui fonctionne avec les conditions logiques

Lorsque c’est possible, il est recommandé d’utiliser les fonctions logiques de numpy (optimisées et qui gèrent bien la dimension). Parmi elles, on peut retrouver:

  • count_nonzero
  • isnan
  • any ; all ; notamment avec l’argument axis
  • np.array_equal pour vérifier, élément par élément, l’égalité

Soit

x = np.random.normal(0, size=(3, 4))

un array multidimensionnel et

y = np.array([np.nan, 0, 1])

un array unidimensionnel présentant une valeur manquante.

Manipuler un array

Dans cette section, on utilisera un array multidimensionnel:

x = np.random.normal(0, size=(3, 4))

Statistiques sur un array

Pour les statistiques descriptives classiques, Numpy propose un certain nombre de fonctions déjà implémentées, qui peuvent être combinées avec l’argument axis

Fonctions de manipulation

Voici quelques fonctions pour modifier un array,

Opération Implémentation
Aplatir un array x.flatten() (méthode)
Transposer un array x.T (méthode) ou np.transpose(x) (fonction)
Ajouter des éléments à la fin np.append(x, [1,2])
Ajouter des éléments à un endroit donné (aux positions 1 et 2) np.insert(x, [1,2], 3)
Supprimer des éléments (aux positions 0 et 3) np.delete(x, [0,3])

Pour combiner des array, on peut utiliser, selon les cas, les fonctions np.concatenate, np.vstack ou la méthode .r_ (concaténation rowwise). np.hstack ou la méthode .column_stack ou .c_ (concaténation column-wise)

x = np.random.normal(size=10)

Pour ordonner un array, on utilise np.sort

x = np.array([7, 2, 3, 1, 6, 5, 4])

np.sort(x)
array([1, 2, 3, 4, 5, 6, 7])

Si on désire faire un ré-ordonnement partiel pour trouver les k valeurs les plus petites d’un array sans les ordonner, on utilise partition:

np.partition(x, 3)
array([2, 1, 3, 4, 6, 5, 7])

Broadcasting

Le broadcasting désigne un ensemble de règles permettant d’appliquer des opérations sur des tableaux de dimensions différentes. En pratique, cela consiste généralement à appliquer une seule opération à l’ensemble des membres d’un tableau numpy.

La différence peut être comprise à partir de l’exemple suivant. Le broadcasting permet de transformer le scalaire 5 en array de dimension 3:

a = np.array([0, 1, 2])

b = np.array([5, 5, 5])

a + b
a + 5
array([5, 6, 7])

Le broadcasting peut être très pratique pour effectuer de manière efficace des opérations sur des données à la structure complexe. Pour plus de détails, se rendre ici ou ici.

Une application: programmer ses propres k-nearest neighbors

Un indice pour représenter graphiquement les plus proches voisins
plt.scatter(X[:, 0], X[:, 1], s=100)

# draw lines from each point to its two nearest neighbors
K = 2

for i in range(X.shape[0]):
    for j in nearest_partition[i, : K + 1]:
        # plot a line from X[i] to X[j]
        # use some zip magic to make it happen:
        plt.plot(*zip(X[j], X[i]), color="black")

Pour la question 2, vous devriez obtenir un graphique ayant cet aspect :

Le résultat de la question 7 est le suivant :

Ai-je inventé cet exercice corsé ? Pas du tout, il vient de l’ouvrage Python Data Science Handbook. Mais, si je vous l’avais indiqué immédiatement, auriez-vous cherché à répondre aux questions ?

Par ailleurs, il ne serait pas une bonne idée de généraliser cet algorithme à de grosses données. La complexité de notre approche est \(O(N^2)\). L’algorithme implémenté par Scikit-Learn est en \(O[NlogN]\).

De plus, le calcul de distances matricielles en utilisant la puissance des cartes graphiques serait plus rapide. A cet égard, la librairie faiss offre des performances beaucoup plus satisfaisantes que celles que permettraient numpy sur ce problème précis.

Exercices supplémentaires

Le site 1 est assez central car il est référencé 2 fois. Le site 5 est lui également central puisqu’il est référencé par le site 1.

array([[0.25419178],
       [0.13803151],
       [0.13803151],
       [0.20599017],
       [0.26375504]])

D’autres idées :

  • Simulations de variables aléatoires ;
  • TCL ;

Informations additionnelles

environment files have been tested on.

Latest built version: 2024-04-12

Python version used:

'3.11.6 | packaged by conda-forge | (main, Oct  3 2023, 10:40:35) [GCC 12.3.0]'
Package Version
affine 2.4.0
aiobotocore 2.12.2
aiohttp 3.9.3
aioitertools 0.11.0
aiosignal 1.3.1
alembic 1.13.1
aniso8601 9.0.1
annotated-types 0.6.0
appdirs 1.4.4
archspec 0.2.3
astroid 3.1.0
asttokens 2.4.1
attrs 23.2.0
bcrypt 4.1.2
beautifulsoup4 4.12.3
black 24.3.0
blinker 1.7.0
blis 0.7.11
bokeh 3.4.0
boltons 23.1.1
boto3 1.34.51
botocore 1.34.51
branca 0.7.1
Brotli 1.1.0
cachetools 5.3.3
cartiflette 0.0.2
Cartopy 0.23.0
catalogue 2.0.10
cattrs 23.2.3
certifi 2024.2.2
cffi 1.16.0
charset-normalizer 3.3.2
click 8.1.7
click-plugins 1.1.1
cligj 0.7.2
cloudpathlib 0.16.0
cloudpickle 3.0.0
colorama 0.4.6
comm 0.2.2
conda 24.3.0
conda-libmamba-solver 24.1.0
conda-package-handling 2.2.0
conda_package_streaming 0.9.0
confection 0.1.4
contextily 1.6.0
contourpy 1.2.1
cryptography 42.0.5
cycler 0.12.1
cymem 2.0.8
cytoolz 0.12.3
dask 2024.4.1
dask-expr 1.0.10
debugpy 1.8.1
decorator 5.1.1
dill 0.3.8
distributed 2024.4.1
distro 1.9.0
docker 7.0.0
duckdb 0.10.1
en-core-web-sm 3.7.1
entrypoints 0.4
et-xmlfile 1.1.0
exceptiongroup 1.2.0
executing 2.0.1
fastjsonschema 2.19.1
fiona 1.9.6
flake8 7.0.0
Flask 3.0.2
folium 0.16.0
fontawesomefree 6.5.1
fonttools 4.51.0
frozenlist 1.4.1
fsspec 2023.12.2
GDAL 3.8.4
gensim 4.3.2
geographiclib 2.0
geopandas 0.12.2
geoplot 0.5.1
geopy 2.4.1
gitdb 4.0.11
GitPython 3.1.43
google-auth 2.29.0
graphene 3.3
graphql-core 3.2.3
graphql-relay 3.2.0
graphviz 0.20.3
greenlet 3.0.3
gunicorn 21.2.0
hvac 2.1.0
idna 3.6
imageio 2.34.0
importlib_metadata 7.1.0
importlib_resources 6.4.0
inflate64 1.0.0
ipykernel 6.29.3
ipython 8.22.2
ipywidgets 8.1.2
isort 5.13.2
itsdangerous 2.1.2
jedi 0.19.1
Jinja2 3.1.3
jmespath 1.0.1
joblib 1.3.2
jsonpatch 1.33
jsonpointer 2.4
jsonschema 4.21.1
jsonschema-specifications 2023.12.1
jupyter-cache 1.0.0
jupyter_client 8.6.1
jupyter_core 5.7.2
jupyterlab_widgets 3.0.10
kaleido 0.2.1
kiwisolver 1.4.5
kubernetes 29.0.0
langcodes 3.3.0
lazy_loader 0.4
libmambapy 1.5.7
llvmlite 0.42.0
locket 1.0.0
lxml 5.2.1
lz4 4.3.3
Mako 1.3.2
mamba 1.5.7
mapclassify 2.6.1
Markdown 3.6
MarkupSafe 2.1.5
matplotlib 3.8.3
matplotlib-inline 0.1.6
mccabe 0.7.0
menuinst 2.0.2
mercantile 1.2.1
mizani 0.11.1
mlflow 2.11.3
mlflow-skinny 2.11.3
msgpack 1.0.7
multidict 6.0.5
multivolumefile 0.2.3
munkres 1.1.4
murmurhash 1.0.10
mypy 1.9.0
mypy-extensions 1.0.0
nbclient 0.10.0
nbformat 5.10.4
nest_asyncio 1.6.0
networkx 3.3
nltk 3.8.1
numba 0.59.1
numpy 1.26.4
oauthlib 3.2.2
opencv-python-headless 4.9.0.80
openpyxl 3.1.2
OWSLib 0.28.1
packaging 23.2
pandas 2.2.1
paramiko 3.4.0
parso 0.8.4
partd 1.4.1
pathspec 0.12.1
patsy 0.5.6
Pebble 5.0.7
pexpect 4.9.0
pickleshare 0.7.5
pillow 10.3.0
pip 24.0
pkgutil_resolve_name 1.3.10
platformdirs 4.2.0
plotly 5.19.0
plotnine 0.13.4
pluggy 1.4.0
polars 0.20.18
preshed 3.0.9
prometheus_client 0.20.0
prometheus-flask-exporter 0.23.0
prompt-toolkit 3.0.42
protobuf 4.25.3
psutil 5.9.8
ptyprocess 0.7.0
pure-eval 0.2.2
py7zr 0.20.8
pyarrow 15.0.0
pyarrow-hotfix 0.6
pyasn1 0.5.1
pyasn1-modules 0.3.0
pybcj 1.0.2
pycodestyle 2.11.1
pycosat 0.6.6
pycparser 2.21
pycryptodomex 3.20.0
pydantic 2.7.0
pydantic_core 2.18.1
pyflakes 3.2.0
Pygments 2.17.2
PyJWT 2.8.0
pylint 3.1.0
PyNaCl 1.5.0
pynsee 0.1.7
pyOpenSSL 24.0.0
pyparsing 3.1.2
pyppmd 1.1.0
pyproj 3.6.1
pyshp 2.3.1
PySocks 1.7.1
python-dateutil 2.9.0
python-dotenv 1.0.1
python-magic 0.4.27
pytz 2024.1
pyu2f 0.1.5
pywaffle 1.1.0
PyYAML 6.0.1
pyzmq 25.1.2
pyzstd 0.15.10
QtPy 2.4.1
querystring-parser 1.2.4
rasterio 1.3.9
referencing 0.34.0
regex 2023.12.25
requests 2.31.0
requests-cache 1.2.0
requests-oauthlib 2.0.0
rpds-py 0.18.0
rsa 4.9
Rtree 1.2.0
ruamel.yaml 0.18.6
ruamel.yaml.clib 0.2.8
s3fs 2023.12.2
s3transfer 0.10.1
scikit-image 0.23.1
scikit-learn 1.4.1.post1
scipy 1.13.0
seaborn 0.13.2
setuptools 69.2.0
shapely 2.0.3
six 1.16.0
smart-open 6.4.0
smmap 5.0.0
snuggs 1.4.7
sortedcontainers 2.4.0
soupsieve 2.5
spacy 3.7.4
spacy-legacy 3.0.12
spacy-loggers 1.0.5
SQLAlchemy 2.0.29
sqlparse 0.4.4
srsly 2.4.8
stack-data 0.6.2
statsmodels 0.14.1
tabulate 0.9.0
tblib 3.0.0
tenacity 8.2.3
texttable 1.7.0
thinc 8.2.3
threadpoolctl 3.4.0
tifffile 2024.2.12
tomli 2.0.1
tomlkit 0.12.4
toolz 0.12.1
topojson 1.8
tornado 6.4
tqdm 4.66.2
traitlets 5.14.2
truststore 0.8.0
typer 0.9.4
typing_extensions 4.11.0
tzdata 2024.1
Unidecode 1.3.8
url-normalize 1.4.3
urllib3 1.26.18
wasabi 1.1.2
wcwidth 0.2.13
weasel 0.3.4
webdriver-manager 4.0.1
websocket-client 1.7.0
Werkzeug 3.0.2
wheel 0.43.0
widgetsnbextension 4.0.10
wordcloud 1.9.3
wrapt 1.16.0
xgboost 2.0.3
xlrd 2.0.1
xyzservices 2024.4.0
yarl 1.9.4
yellowbrick 1.5
zict 3.0.0
zipp 3.17.0
zstandard 0.22.0

View file history

SHA Date Author Description
005d89b 2023-12-20 17:23:04 Lino Galiana Finalise l’affichage des statistiques Git (#478)
1684220 2023-12-02 12:06:40 Antoine Palazzolo Première partie de relecture de fin du cours (#467)
1f23de2 2023-12-01 17:25:36 Lino Galiana Stockage des images sur S3 (#466)
a06a268 2023-11-23 18:23:28 Antoine Palazzolo 2ème relectures chapitres ML (#457)
889a71b 2023-11-10 11:40:51 Antoine Palazzolo Modification TP 3 (#443)
a771183 2023-10-09 11:27:45 Antoine Palazzolo Relecture TD2 par Antoine (#418)
a63319a 2023-10-04 15:29:04 Lino Galiana Correction du TP numpy (#419)
e8d0062 2023-09-26 15:54:49 Kim A Relecture KA 25/09/2023 (#412)
154f09e 2023-09-26 14:59:11 Antoine Palazzolo Des typos corrigées par Antoine (#411)
a8f90c2 2023-08-28 09:26:12 Lino Galiana Update featured paths (#396)
8082302 2023-08-25 17:48:36 Lino Galiana Mise à jour des scripts de construction des notebooks (#395)
3bdf3b0 2023-08-25 11:23:02 Lino Galiana Simplification de la structure 🤓 (#393)
9e1e6e4 2023-07-20 02:27:22 Lino Galiana Change launch script (#379)
130ed71 2023-07-18 19:37:11 Lino Galiana Restructure les titres (#374)
ef28fef 2023-07-07 08:14:42 Lino Galiana Listing pour la première partie (#369)
f21a24d 2023-07-02 10:58:15 Lino Galiana Pipeline Quarto & Pages 🚀 (#365)
7e15843 2023-02-13 18:57:28 Lino Galiana from_numpy_array no longer in networkx 3.0 (#353)
a408cc9 2023-02-01 09:07:27 Lino Galiana Ajoute bouton suggérer modification (#347)
3c880d5 2022-12-27 17:34:59 Lino Galiana Chapitre regex + Change les boites dans plusieurs chapitres (#339)
e2b53ac 2022-09-28 17:09:31 Lino Galiana Retouche les chapitres pandas (#287)
d068cb6 2022-09-24 14:58:07 Lino Galiana Corrections avec echo true (#279)
b2d4823 2022-09-21 17:36:29 Lino Galiana Relec KA 21/09 (#273)
a56dd45 2022-09-20 15:27:56 Lino Galiana Fix SSPCloud links (#270)
f10815b 2022-08-25 16:00:03 Lino Galiana Notebooks should now look more beautiful (#260)
494a85a 2022-08-05 14:49:56 Lino Galiana Images featured ✨ (#252)
d201e3c 2022-08-03 15:50:34 Lino Galiana Pimp la homepage ✨ (#249)
1ca1a8a 2022-05-31 11:44:23 Lino Galiana Retour du chapitre API (#228)
4fc58e5 2022-05-25 18:29:25 Lino Galiana Change deployment on SSP Cloud with new filesystem organization (#227)
12965ba 2022-05-25 15:53:27 Lino Galiana :launch: Bascule vers quarto (#226)
9c71d6e 2022-03-08 10:34:26 Lino Galiana Plus d’éléments sur S3 (#218)
6777f03 2021-10-29 09:38:09 Lino Galiana Notebooks corrections (#171)
2a8809f 2021-10-27 12:05:34 Lino Galiana Simplification des hooks pour gagner en flexibilité et clarté (#166)
26ea709 2021-09-27 19:11:00 Lino Galiana Règle quelques problèmes np (#154)
2fa78c9 2021-09-27 11:24:19 Lino Galiana Relecture de la partie numpy/pandas (#152)
85ba119 2021-09-16 11:27:56 Lino Galiana Relectures des TP KA avant 1er cours (#142)
2e4d586 2021-09-02 12:03:39 Lino Galiana Simplify badges generation (#130)
2f7b52d 2021-07-20 17:37:03 Lino Galiana Improve notebooks automatic creation (#120)
80877d2 2021-06-28 11:34:24 Lino Galiana Ajout d’un exercice de NLP à partir openfood database (#98)
6729a72 2021-06-22 18:07:05 Lino Galiana Mise à jour badge onyxia (#115)
4cdb759 2021-05-12 10:37:23 Lino Galiana :sparkles: :star2: Nouveau thème hugo :snake: :fire: (#105)
7f9f97b 2021-04-30 21:44:04 Lino Galiana 🐳 + 🐍 New workflow (docker 🐳) and new dataset for modelization (2020 🇺🇸 elections) (#99)
0a0d034 2021-03-26 20:16:22 Lino Galiana Ajout d’une section sur S3 (#97)
6d010fa 2020-09-29 18:45:34 Lino Galiana Simplifie l’arborescence du site, partie 1 (#57)
66f9f87 2020-09-24 19:23:04 Lino Galiana Introduction des figures générées par python dans le site (#52)
edca391 2020-09-21 19:31:02 Lino Galiana Change np.is_nan to np.isnan
f9f00cc 2020-09-15 21:05:54 Lino Galiana enlève quelques TO DO
4677769 2020-09-15 18:19:24 Lino Galiana Nettoyage des coquilles pour premiers TP (#37)
d48e68f 2020-09-08 18:35:07 Lino Galiana Continuer la partie pandas (#13)
913047d 2020-09-08 14:44:41 Lino Galiana Harmonisation des niveaux de titre (#17)
c452b83 2020-07-28 17:32:06 Lino Galiana TP Numpy (#9)
200b6c1 2020-07-27 12:50:33 Lino Galiana Encore une coquille
5041b28 2020-07-27 12:44:10 Lino Galiana Une coquille à cause d’un bloc jupyter
e8db4cf 2020-07-24 12:56:38 Lino Galiana modif des markdown
b24a1fe 2020-07-23 18:20:09 Lino Galiana Add notebook
4f8f1ca 2020-07-23 18:19:28 Lino Galiana fix typo
434d20e 2020-07-23 18:18:46 Lino Galiana Essai de yaml header
5ac02ef 2020-07-23 18:05:12 Lino Galiana Essai de md généré avec jupytext
Back to top

Citation

BibTeX citation:
@book{galiana2023,
  author = {Galiana, Lino},
  title = {Python Pour La Data Science},
  date = {2023},
  url = {https://pythonds.linogaliana.fr/},
  doi = {10.5281/zenodo.8229676},
  langid = {en}
}
For attribution, please cite this work as:
Galiana, Lino. 2023. Python Pour La Data Science. https://doi.org/10.5281/zenodo.8229676.