Exemples pratiques numpy: techniques utiles
La bibliothèque NumPy est une bibliothèque Python utilisée pour le calcul scientifique. Il vous fournit un objet tableau multidimensionnel pour stocker et analyser les données de différentes manières. Dans ce didacticiel, vous verrez des exemples de certaines fonctionnalités fournies par NumPy qui ne sont pas toujours mises en évidence dans d'autres didacticiels. Vous aurez également l’occasion de mettre en pratique vos nouvelles compétences grâce à divers exercices.
Dans ce tutoriel, vous apprendrez comment:
- Créer tableaux multidimensionnels à partir de données stockées dans des fichiers
- Identifier et supprimer Données en double à partir d'un tableau Numpy
- Utilisez tableaux numpy structurés pour concilier les différences entre les ensembles de données
- Analyser et tracer des parties spécifiques des données hiérarchiques
- Créez des versions vectorisées de vos propres fonctions
Si vous êtes nouveau dans Numpy, c'est une bonne idée de vous familiariser avec les bases de la science des données à Python avant de commencer. En outre, vous utiliserez Matplotlib dans ce tutoriel pour créer des graphiques. Bien que ce ne soit pas essentiel, se familiariser avec Matplotlib au préalable pourrait être bénéfique.
Configuration de votre environnement de travail
Avant de pouvoir commencer ce didacticiel, vous devrez effectuer une configuration initiale. En plus de NumPy, vous devrez installer la bibliothèque Matplotlib, que vous utiliserez pour cartographier vos données. Vous utiliserez également la bibliothèque pathlib
de Python pour accéder au système de fichiers de votre ordinateur, mais il n'est pas nécessaire d'installer pathlib
car elle fait partie de la bibliothèque standard de Python.
Vous pouvez envisager d'utiliser un environnement virtuel pour vous assurer que la configuration de votre didacticiel n'interfère pas avec quoi que ce soit dans votre environnement Python existant.
Utiliser un Jupyter Notebook dans JupyterLab pour exécuter votre code au lieu d'un Python REPL est une autre option utile. Il vous permet d'expérimenter et de documenter vos résultats, ainsi que d'afficher et de modifier rapidement des fichiers. La version téléchargeable du code et les solutions d'exercices sont présentées au format Jupyter Notebook.
Les commandes de configuration des choses sur les plates-formes communes sont présentées ci-dessous:
Le premier extrait de code doit être utilisé sous Windows et le deuxième extrait de code est pour Linux + macOS :
Lancez une invite Windows PowerShell(Admin)
ou Terminal(Admin)
, selon la version de Windows que vous utilisez. Tapez maintenant les commandes suivantes :
PS> python -m venv venv\
PS> venv\Scripts\activate
(venv) PS> python -m pip install numpy matplotlib jupyterlab
(venv) PS> jupyter lab
Ici, vous créez un environnement virtuel nommé Venv\
, que vous activez ensuite. Si l'activation est réussie, le nom de l'environnement virtuel précédera votre invite PowerShell. Ensuite, vous installez Numpy
et Matplotlib
dans cet environnement virtuel, suivi par le JupyterLab
facultatif. Enfin, vous commencez JupyterLab.
Remarque: Lorsque vous activez votre environnement virtuel, vous pouvez recevoir une erreur indiquant que votre système ne peut pas exécuter le script. Les versions modernes de Windows ne vous permettent pas d'exécuter des scripts téléchargés depuis Internet comme fonctionnalité de sécurité.
Pour résoudre ce problème, vous devez taper la commande Set-ExecutionPolicy RemoteSigned
, puis répondre Y
à la question. Votre ordinateur exécutera désormais les scripts vérifiés par Microsoft. Une fois cela fait, la commande venv\Scripts\activate
devrait fonctionner.
Lancez un terminal et tapez les commandes suivantes :
$ python -m venv venv/
$ source venv/bin/activate
(venv) $ python -m pip install numpy matplotlib jupyterlab
(venv) $ jupyter lab
Ici, vous créez un environnement virtuel nommé Venv/
, que vous activez ensuite. Si l'activation est réussie, le nom de l'environnement virtuel précédera votre invite de commande. Ensuite, vous installez Numpy
et matplotlib
dans cet environnement virtuel, suivi par le JupyterLab
en option. Enfin, vous commencez JupyterLab.
Vous remarquerez que votre invite est précédée par (VENV)
. Cela signifie que tout ce que vous faites à partir de ce moment restera dans cet environnement et restera séparé des autres travaux de python que vous avez ailleurs.
Maintenant que vous avez tout configuré, il est temps de commencer la partie principale de votre parcours d'apprentissage.
Exemple Numpy 1: création de tableaux multidimensionnels à partir de fichiers
Lorsque vous créez un tableau NumPy, vous créez une structure de données hautement optimisée. L'une des raisons en est qu'un tableau NumPy stocke tous ses éléments dans une zone contiguë de mémoire. Cette technique de gestion de la mémoire signifie que les données sont stockées dans la même région mémoire, ce qui accélère les temps d'accès. Ceci est bien sûr hautement souhaitable, mais un problème survient lorsque vous devez étendre votre baie.
Supposons que vous deviez importer plusieurs fichiers dans un tableau multidimensionnel. Vous pouvez les lire dans des tableaux séparés, puis les combiner en utilisant np.concatenate()
. Cependant, cela créerait une copie de votre tableau d'origine avant d'étendre la copie avec les données supplémentaires. La copie est nécessaire pour garantir que le tableau mis à jour existera toujours de manière contiguë en mémoire, car le tableau d'origine peut avoir un contenu non lié adjacent.
La copie constante des tableaux chaque fois que vous ajoutez de nouvelles données à partir d'un fichier peut rendre le traitement lent et est un gaspillage de la mémoire de votre système. Le problème s'aggrave, plus vous ajoutez de données à votre tableau. Bien que ce processus de copie soit intégré à Numpy, vous pouvez minimiser ses effets avec ces deux étapes:
Lors de la configuration de votre tableau initial, déterminez à quel point il doit être avant en remplissant. Vous pouvez même envisager de sur-estimer sa taille pour prendre en charge les futurs ajouts de données. Une fois que vous connaissez ces tailles, vous pouvez créer votre tableau à l'avance.
La deuxième étape consiste à le remplir avec les données sources. Ces données seront insérées dans votre baie existante sans qu'il soit nécessaire de les étendre.
Ensuite, vous explorerez comment remplir un réseau Numpy tridimensionnel.
Remplir des tableaux avec des données de fichier
Dans ce premier exemple, vous utiliserez les données de trois fichiers pour remplir un tableau tridimensionnel. Le contenu de chaque fichier est illustré ci-dessous, et vous trouverez également ces fichiers dans les documents téléchargeables:
Le premier fichier comporte deux lignes et trois colonnes avec le contenu suivant :
1.1, 1.2, 1.3
1.4, 1.5, 1.6
Ce deuxième fichier, qui a également les mêmes dimensions, contient ceci :
2.1, 2.2, 2.3
2.4, 2.5, 2.6
Le troisième fichier, toujours avec les mêmes dimensions, stocke ces numéros :
3.1, 3.2, 3.3
3.4, 3.5, 3.6
Avant de continuer, ajoutez ces trois fichiers à votre dossier de programme. Les matériaux téléchargeables contiennent également deux fichiers appelés file10.csv
et file11.csv
, et vous travaillerez avec ceux-ci plus tard.
Le diagramme ci-dessous montre le tableau Numpy résultant que vous créerez à partir des trois fichiers:
Comme vous pouvez le voir, file1.csv
forme le devant de votre tableau, file2.csv
la section du milieu et file3.csv
est au début. dos.
Le code utilisé pour créer ce tableau est présenté ci-dessous. Avant d'exécuter ce code, assurez-vous de créer les trois fichiers présentés dans le diagramme ou d'utiliser les versions fournies dans les documents téléchargeables. Quoi qu'il en soit, placez-les dans le même répertoire dans lequel vous exécutez votre code, puis exécutez-le :
>>> from pathlib import Path
>>> import numpy as np
>>> array = np.zeros((3, 2, 3))
>>> print(id(array))
2250027286128
>>> for file_count, csv_file in enumerate(Path.cwd().glob("file?.csv")):
... array[file_count] = np.loadtxt(csv_file.name, delimiter=",")
...
>>> print(id(array))
2250027286128
>>> print(array.shape)
(3, 2, 3)
>>> array
array([[[1.1, 1.2, 1.3],
[1.4, 1.5, 1.6]],
[[2.1, 2.2, 2.3],
[2.4, 2.5, 2.6]],
[[3.1, 3.2, 3.3],
[3.4, 3.5, 3.6]]])
Pour commencer, vous pouvez parcourir chacun des fichiers et utiliser les informations pour déterminer la forme finale du tableau attendu. Dans cet exemple, les trois fichiers comportent le même nombre d’éléments disposés sur deux lignes et trois colonnes. Le tableau résultant aura alors une propriété shape
de (3, 2, 3)
. Jetez un autre coup d’œil au diagramme et vous pourrez voir ceci.
Les lignes 1 et 2 montrent que la bibliothèque Numpy est importée avec son alias standard de np
ainsi que la classe path
en cours de bibliothèque pathlib
. Cette bibliothèque permet à Python d'accéder au système de fichiers de votre ordinateur à l'aide d'une approche orientée objet. Les objets de la classe path
vous permettent de spécifier un chemin de fichier et de contenir également des méthodes afin que vous puissiez passer des appels système vers votre système d'exploitation. Cette fonctionnalité est utilisée plus tard dans le code.
Vous avez appris plus tôt que c'est une bonne idée de créer un tableau à l'avance avant de commencer à le remplir de données afin de réduire l'empreinte mémoire. À la ligne 4, vous créez un tableau contenant des zéros avec la forme (3, 2, 3)
comme vous l'avez déterminé précédemment.
Ensuite, vous remplissez votre tableau avec les données des fichiers. Vous créez une boucle for
à l'aide de la fonction enumerate
intégrée de Python présentée aux lignes 8 et 9. Son paramètre vous permet de parcourir un ensemble de fichiers et de renvoyer une référence à chacun. un. Il tient également à jour le nombre de fichiers rencontrés. Chaque référence de fichier est stockée dans la variable csv_file
, tandis que le compteur incrémentiel est stocké dans la variable file_count
.
Pour accéder tour à tour à chacun des trois fichiers .csv
, vous utilisez Path
. En appelant Path.cwd()
, vous dites à Python de rechercher les fichiers dans votre répertoire de travail actuel. En d’autres termes, le répertoire à partir duquel vous exécutez le programme. Ceci renvoie un objet Path
représentant le répertoire courant, à partir duquel vous appelez sa méthode .glob()
pour spécifier les noms des fichiers auxquels vous souhaitez accéder.
Dans ce cas, comme vous souhaitez accéder aux fichiers nommés file1.csv, file2.csv
et file3.csv
, vous transmettez leurs noms sous forme de chaîne file ?.csv
. Cela indique à .glob()
de sélectionner uniquement les fichiers dont les noms doivent correspondre à ces caractères exacts, mais dont le cinquième caractère peut être n'importe quel caractère, comme spécifié par le caractère générique (?
).
Malheureusement, .glob()
peut ne pas renvoyer les fichiers dans l'ordre que vous attendez. Dans cet exemple, tout fonctionne comme prévu car le nom de chaque fichier contient un seul chiffre comme cinquième caractère. S'il y avait eu un fichier nommé file11.csv
, il aurait été lu dans le mauvais ordre. Vous en apprendrez plus sur la raison pour laquelle cela est et comment le résoudre plus tard.
Remarque : Sous Windows, .glob()
produira les fichiers triés lexicographiquement. Ce n'est pas nécessairement le cas sur d'autres systèmes d'exploitation. Si vous comptez sur le tri des fichiers, vous devez également appeler explicitement sorted()
:
>>> for csv_file in sorted(Path.cwd().glob("file?.csv")):
... print(csv_file.name)
...
file1.csv
file2.csv
file3.csv
Comme vous le verrez, ce type de tri n'est pas toujours suffisant lorsque vous travaillez avec des fichiers numérotés.
Chaque fois que les boucles de boucle, vous appelez la fonction np.loadtxt()
et passez-la un nom de fichier, qui est spécifié à l'aide de sa propriété nom
. Vous lui dites également d'utiliser une virgule (,
) comme délimiteur de champ pour lui permettre de séparer chaque numéro individuel dans le fichier. Le contenu de chaque fichier est ensuite attribué au array
que vous avez créé plus tôt.
Pour vous assurer que le contenu de chaque fichier est inséré dans la bonne position le long de l'axe 0
, vous utilisez array[file_count]
. La première fois que la boucle s'exécute, le contenu de file1.csv
sera affecté à array[0]
, ou à la position 0 le long de l'axe-0. L'itération suivante dans la boucle attribuera file2.csv
à array[1]
le long de cet axe, avant que file3.csv
ne soit affecté à tableau[2]
. Regardez encore une fois le diagramme et vous verrez exactement ce qui s’est passé.
Aux lignes 5 et 11, vous avez imprimé le résultat de id(array)
. La fonction id()
renvoie l'identité d'un objet. Chaque objet a une valeur d'identité unique car chaque objet occupe une place unique dans la mémoire de l'ordinateur. Lorsque vous exécutez le code sur votre ordinateur, les numéros seront également identiques les uns aux autres, mais seront probablement différents de ceux affichés.
Aux lignes 6 et 12, les valeurs d'identité affichées prouvent que l'objet array
qui au départ ne contenait que des zéros, est le même objet tableau qui contenait plus tard le contenu de chaque fichier. Cela montre qu’un seul objet a été utilisé partout et que la mémoire a été utilisée efficacement.
Lorsque vous créez des tableaux de cette manière, c'est une bonne idée de vous assurer que chacun des fichiers d'entrée a le même nombre de lignes et de colonnes d'éléments. Ensuite, vous examinerez comment gérer les situations où vos fichiers de données ne sont pas aussi uniformes.
Traitant de différentes tailles de données
Pour commencer, vous ajouterez des données sous-dimensionnées. Vous le trouverez dans un fichier nommé short_file.csv
, qui ne comporte qu'une seule ligne :
4.1, 4.2, 4.3
Vous souhaitez l'ajouter à l'arrière de votre tableau comme indiqué ci-dessous :
Avant d'exécuter le code utilisé pour créer ce deuxième tableau, assurez-vous de télécharger ou d'ajouter le fichier nommé short_file.csv
dans le même répertoire dans lequel vous exécutez votre code :
>>> array = np.zeros((4, 2, 3))
>>> for file_count, csv_file in enumerate(Path.cwd().glob("file?.csv")):
... array[file_count] = np.loadtxt(csv_file.name, delimiter=",")
...
>>> array[3, 0] = np.loadtxt("short_file.csv", delimiter=",")
>>> array
array([[[1.1, 1.2, 1.3],
[1.4, 1.5, 1.6]],
[[2.1, 2.2, 2.3],
[2.4, 2.5, 2.6]],
[[3.1, 3.2, 3.3],
[3.4, 3.5, 3.6]],
[[4.1, 4.2, 4.3],
[0. , 0. , 0. ]]])
Cette fois, vous lisez quatre fichiers distincts, donc le tableau que vous créez initialement aura la forme de (4, 2, 3)
. Vous aurez besoin d'une dimension supplémentaire le long de l'axe 0 pour accueillir le quatrième fichier, vous le créez donc à la ligne 1.
La boucle for est utilisée pour lire les trois premiers fichiers comme auparavant. Pour lire le fichier court, vous devez indiquer à Python exactement où vous souhaitez le placer dans votre tableau. Auparavant, vous faisiez cela en indiquant une position, telle que array[2]
, pour insérer les données en position 2 le long de l'axe 0. Cette approche a fonctionné car les données que vous avez insérées remplissaient déjà le tableau existant à cette position. Mais cette fois, les choses sont différentes.
Pour dire à Python que vous souhaitez insérer le fichier court à partir du haut de la position d'index 3 dans AXIS-2, vous utilisez array [3, 0]
. Le 3
représente la position AXIS-2 comme précédemment, mais vous devez également fournir un 0
pour indiquer que les données doivent être insérées à partir de la ligne 0. Vous pouvez jeter un coup d'œil Aux lignes 18 et 19 du programme, puis sur le diagramme pour voir où les données ont été placées.
Comme précédemment, l'objet de tableau créé au début du code est le seul utilisé partout. Bien que votre code lui ait ajouté des données plusieurs fois, aucune copie inefficace n'était nécessaire car vous avez créé le tableau à l'avance.
Supposons qu'au lieu d'être trop court, le quatrième fichier soit trop long. Vous vous demandez peut-être comment gérer de tels fichiers, ce qui peut poser problème. Cette fois, vous utiliserez un fichier nommé long_file.csv
, qui comporte une ligne supplémentaire :
4.1, 4.2, 4.3
4.4, 4.5, 4.6
4.7, 4.8, 4.9
Vous souhaitez maintenant l'incorporer dans votre tableau à la position indiquée ci-dessous. Comme vous pouvez le voir sur le diagramme, le reste du tableau devra être étendu d'une ligne supplémentaire pour l'adapter :
Le code utilisé pour créer ce troisième tableau est illustré ci-dessous. Avant de l'exécuter, assurez-vous de télécharger ou de créer le fichier nommé long_file.csv
Dans le même répertoire, vous exécutez votre code:
>>> array = np.zeros((4, 2, 3))
>>> print(id(array))
2250027278352
>>> for file_count, csv_file in enumerate(Path.cwd().glob("file?.csv")):
... array[file_count] = np.loadtxt(csv_file.name, delimiter=",")
...
>>> array = np.insert(arr=array, obj=2, values=0, axis=1)
>>> array[3] = np.loadtxt("long_file.csv", delimiter=",")
>>> print(id(array))
2250027286224
>>> array
array([[[1.1, 1.2, 1.3],
[1.4, 1.5, 1.6],
[0. , 0. , 0. ]],
[[2.1, 2.2, 2.3],
[2.4, 2.5, 2.6],
[0. , 0. , 0. ]],
[[3.1, 3.2, 3.3],
[3.4, 3.5, 3.6],
[0. , 0. , 0. ]],
[[4.1, 4.2, 4.3],
[4.4, 4.5, 4.6],
[4.7, 4.8, 4.9]]])
Cette fois, comme votre tableau d'origine est trop court pour accueillir le contenu de long_file.csv
, vous devez l'allonger avec une ligne supplémentaire le long de l'axe 1. Vous pouvez ensuite ajouter le contenu de long_file.csv
. Vous faites cela à la ligne 9 en utilisant la fonction np.insert()
, qui est une fonction qui vous permet d'insérer des valeurs le long d'un axe.
Vous transmettez quatre paramètres à np.insert()
. Le paramètre arr
spécifie le tableau dans lequel vous souhaitez insérer des valeurs, tout en définissant obj
sur 2
, axis
sur 1
, et le paramètre values
à 0
, vous permet d'insérer des valeurs 0
dans la position d'index 2
le long de l'axe 1
. En d’autres termes, le long de la rangée inférieure du tableau, comme indiqué dans le diagramme. Enfin, pour ajouter le contenu de long_file.csv
dans votre tableau, vous utilisez à nouveau loadtxt()
comme indiqué à la ligne 11.
Prenez un moment pour regarder le diagramme et le tableau résultant produit par votre code et vous verrez que tout fonctionne comme prévu.
Notez que les lignes 4 et 14 indiquent que les objets du tableau avant et après l'insertion des nouvelles données sont différents. Cela se produit parce que la fonction insert()
renvoie une copie du tableau d'origine. Pour éviter ce gaspillage de mémoire, c'est vraiment une bonne idée de vous assurer de dimensionner correctement votre tableau initial avant de commencer à le remplir.
S'assurer que l'ordre des fichiers est correct
Lorsque vous exécutez le code Path.cwd().glob("file?.csv")
, vous renvoyez un itérateur générateur qui peut être utilisé pour afficher une collection de WindowsPath
ou des objets PosixPath
. Chacun d'eux représente un chemin de fichier et un nom de fichier du système d'exploitation qui correspondent au modèle file?.csv
. Cependant, l’ordre dans lequel ces fichiers sont renvoyés par le générateur n’est pas celui auquel on pourrait s’attendre.
Pour voir un exemple de cela, ajoutez les deux fichiers nommés file10.csv
et file11.csv
à partir des matériaux téléchargeables dans votre dossier existant:
10.1,10.2,10.3
10.4,10.5,10.6
Vous avez probablement déjà deviné ce qui était dans file10.csv
. Si c'est le cas, vous ne serez pas surpris lorsque vous voyez ce qui est dans file11.csv
:
11.1,11.2,11.3
11.4,11.5,11.6
Exécutez maintenant le code suivant :
>>> for csv_file in Path.cwd().glob("file*.csv"):
... print(csv_file.name)
...
file1.csv
file10.csv
file11.csv
file2.csv
file3.csv
Pour vous assurer que chacun de ces fichiers supplémentaires est vu par le générateur, vous devez ajuster les critères de correspondance au fichier * .csv
. Le caractère ( *
) Wildcard représente n'importe quel nombre de caractères inconnus. Si vous aviez conservé le caractère (?
) Wildcard, alors seuls les fichiers contenant un caractère après le fichier de chaîne
auraient été inclus. Même avec cet ajout, quelque chose semble encore mal.
Remarque : Sous Linux et macOS, vous devrez peut-être ajouter un appel à sorted()
pour voir les fichiers classés comme ci-dessus.
Vous verrez que les nouveaux fichiers sont placés entre file1.csv
et file2.csv
et non, comme vous vous y attendiez probablement, à la fin. La raison en est que les noms de fichiers sont triés par ordre alphanumérique. Cela signifie que le tri lit les noms de fichiers de gauche à droite et considère que tout est égal jusqu'à ce qu'il trouve une différence. Une fois cela obtenu, le tri est basé sur cette différence.
Par exemple, lorsque les caractères de chaque nom de fichier sont analysés, tout est considéré comme égal sur les quatre premiers caractères du nom de chaque fichier - dans ce cas, fichier
. Python doit alors décider lequel des cinquième personnages vient en premier. Il le fait en considérant les numéros de code de caractères Unicode de chacun d'eux.
Le code de caractère Unicode pour le caractère 1
est 49, tandis que les codes pour 2
et 3
sont respectivement 50 et 51. En conséquence, tout nom de fichier avec 1
comme cinquième caractère sera alors trié plus tôt que ceux avec 2
ou 3
à la même position.
Dans le cas de file1.csv
, file10.csv
et file11.csv
, le cinquième caractère de chaque nom de fichier est le même. Par conséquent, l’ordre de tri sera décidé par leur sixième caractère. Lorsque l'on considère leurs valeurs de caractères Unicode, le point (.) a une valeur de 46, qui précède les caractères 0
et 1
, dont les valeurs sont respectivement 48 et 49. . Par conséquent, l'ordre de tri sera file1.csv
, suivi de file10.csv
, puis de file11.csv
.
Vous pourriez être tenté d’essayer la fonction sorted()
intégrée de Python pour voir si cela aide :
>>> for csv_file in sorted(Path.cwd().glob("file*.csv")):
... print(csv_file.name)
...
file1.csv
file10.csv
file11.csv
file2.csv
file3.csv
Cependant, la fonction sorted()
vous a donné le même résultat indésirable qu'avant.
Pour lire les fichiers dans un ordre plus naturel, vous pouvez utiliser la bibliothèque natsort
. Tout d’abord, installez-le en exécutant la commande python -m pip install natsort
. Après l'installation, vous pouvez importer la fonction natsorted()
et l'utiliser à la place de la fonction sorted()
intégrée pour obtenir un tri plus naturel des fichiers. Ci-dessous le code illustrant ceci :
>>> from natsort import natsorted
>>> for csv_file in natsorted(Path.cwd().glob("file*.csv")):
... print(csv_file.name)
...
file1.csv
file2.csv
file3.csv
file10.csv
file11.csv
Vous avez enfin réussi à résoudre votre problème de tri des noms de fichiers. Vous pouvez maintenant aller plus loin dans votre code précédent et ajouter le contenu du fichier aux bons endroits de votre tableau :
>>> array = np.zeros((5, 2, 3))
>>> for file_count, csv_file in enumerate(
... natsorted(Path.cwd().glob("file*.csv"))
... ):
... array[file_count] = np.loadtxt(csv_file.name, delimiter=",")
...
>>> array
array([[[ 1.1, 1.2, 1.3],
[ 1.4, 1.5, 1.6]],
[[ 2.1, 2.2, 2.3],
[ 2.4, 2.5, 2.6]],
[[ 3.1, 3.2, 3.3],
[ 3.4, 3.5, 3.6]],
[[10.1, 10.2, 10.3],
[10.4, 10.5, 10.6]],
[[11.1, 11.2, 11.3],
[11.4, 11.5, 11.6]]])
Cette fois, vous transmettez les différents chemins de fichiers dans natsorted()
, qui les triera probablement comme vous le souhaitiez. Le résultat montre que le contenu de file10.csv
et de file11.csv
est désormais à sa place correcte dans les tableaux. Encore une fois, remarquez comment l'opérateur générique (*
) a été utilisé ici. De plus, les dimensions de cette version de array
ont été augmentées à (5, 2, 3)
pour fournir de l'espace pour les nouvelles données.
Remarque: Une astuce commune pour s'assurer que les fichiers numérotés sont gérés dans l'ordre consistent à remplir les nombres avec des zéros de premier plan. Si les fichiers avaient été nommés file01.csv
, file02.csv
, file03.csv
, file10.csv
, et file11.csv
, alors tri()
pourrait trier les noms de fichiers.
Comme vous pouvez le constater, il est parfaitement possible de construire des tableaux NumPy à partir de données contenues dans des fichiers. Cependant, vous devez vous assurer que différentes tailles sont prises en compte.
Avant de passer à autre chose, vous terminerez un exercice pour tester votre compréhension. Il s'agit du premier de plusieurs exercices inclus dans ce tutoriel pour vous aider à consolider votre apprentissage.
Testez vos compétences : remplir des tableaux avec des données de fichiers
Il est temps pour vous de tester votre compréhension de la création de tableaux à partir des données de fichiers. Voyez si vous pouvez résoudre votre premier défi illustré ci-dessous:
Recherchez les fichiers ex1a.csv
, ex1b.csv
et eX1c.csv
dans les matériaux téléchargeables. Maintenant, appliquez les techniques que vous avez apprises pour créer un tableau tridimensionnel de taille correcte qui permettra d'ajouter chaque fichier. Le contenu de chaque fichier doit être inséré de sorte qu'il touche le coin supérieur gauche d'un index séparé le long de l'axe des 0. Vous devez insérer le contenu du fichier ex1b.csv
comme une ligne, et le contenu de ex1c.csv
comme colonne.
Une solution possible est :
>>> import numpy as np
>>> from pathlib import Path
>>> array = np.zeros((3, 4, 4), dtype="i8")
>>> array[0] = np.loadtxt("ex1a.csv", delimiter=",")
>>> narrow_array = np.loadtxt("ex1b.csv", delimiter=",")
>>> narrow_array = np.insert(arr=narrow_array, values=0, obj=3, axis=0)
>>> array[1, 0] = narrow_array
>>> short_array = np.loadtxt("ex1c.csv", delimiter=",")
>>> short_array = np.insert(arr=short_array, values=0, obj=3, axis=0)
>>> array[2, :, 0] = short_array
>>> array
array([[[ 5, 10, 15, 20],
[25, 30, 35, 40],
[45, 50, 55, 60],
[65, 70, 75, 80]],
[[ 1, 3, 5, 0],
[ 0, 0, 0, 0],
[ 0, 0, 0, 0],
[ 0, 0, 0, 0]],
[[ 2, 0, 0, 0],
[ 4, 0, 0, 0],
[ 6, 0, 0, 0],
[ 0, 0, 0, 0]]])
Tout d'abord, vous parcourez les fichiers et voyez qu'un tableau de taille (3, 4, 4)
est nécessaire pour garantir que vos données correspondent. Vous créez ensuite un tableau de zéros avec cette taille et spécifiez qu’il stockera des entiers. Ensuite, vous l'attribuez à la variable array
avant de remplir array[0]
, ou axe-0, avec le contenu du fichier ex1a.csv
, qui épouse parfaitement la forme.
Le fichier ex1b.csv
n'a que trois valeurs, ce qui signifie qu'elle est trop courte pour être directement insérée dans array [1]
. Pour résoudre ce problème, vous le lisez d'abord dans étroite_array
, puis utilisez np.insert()
pour ajouter un 0
supplémentaire dans sa position d'index 3, tel que défini par ses paramètres valeurs
et obj
. Enfin, vous insérez étroite_array
dans la ligne supérieure de array
à index 1
le long de ses 0 axes à l'aide de array [1, 0]
.
Le fichier ex1c.csv
n'a également que trois valeurs. Cela signifie que vous devez l'ajuster pour l'adapter à la colonne, vous ajoutez donc un zéro supplémentaire à la fin de celle-ci. Cette fois, pour ajouter short_array
, vous utilisez array[2, :, 0]
. Il sera inséré à la position d'index 2
le long de l'axe 0, mais :, 0
signifie qu'il sera inséré vers le bas le premier colonne et non sur une ligne.
Au lieu d'étendre les tableaux que vous lisez, vous pouvez également être plus précis sur l'endroit où vous placez les valeurs. Par exemple, vous pouvez insérer short_array
dans array[2, 0:3, 0]
directement avant d'insérer le 0
supplémentaire.
Dans la section suivante, vous en apprendrez un peu sur les tableaux structurés et comment ils peuvent être utilisés pour concilier les différences entre les différents tableaux.
Numpy Exemple 2: réconcilier les données à l'aide de tableaux structurés
Avec les tableaux Numpy que vous avez créés dans la section précédente, il n'y avait aucun moyen de connaître la signification des données de chaque colonne. Ne serait-il pas bien si vous pouviez référencer des colonnes spécifiques par des noms significatifs au lieu de numéros d'index? Ainsi, par exemple, au lieu d'utiliser student_grades=résultats [:, 1]
, vous pouvez à la place utiliser student_grades=résultats ["examing_grade"]
. Bonnes nouvelles! Vous pouvez le faire en créant un tableau structuré.
Créer un tableau structuré
Un tableau structuré est un tableau NumPy avec un type de données composé d'un ensemble de tuples, chacun contenant un nom de champ et un type de données normal. Une fois que vous les avez définis, vous pouvez ensuite accéder et modifier chaque champ individuel en utilisant son nom de champ.
Le code ci-dessous fournit un exemple de la façon de créer et de référencer un tableau Numpy structuré:
>>> import numpy as np
>>> race_results = np.array(
... [
... ("At The Back", 1.2, 3),
... ("Fast Eddie", 1.3, 1),
... ("Almost There", 1.1, 2),
... ],
... dtype=[
... ("horse_name", "U12"),
... ("price", "f4"),
... ("position", "i4"),
... ],
... )
>>> race_results["horse_name"]
array(['At The Back', 'Fast Eddie', 'Almost There'], dtype='<U12')
>>> np.sort(race_results, order="position")[
... ["horse_name", "price"]
... ]
array([('Fast Eddie', 1.3), ('Almost There', 1.1), ('At The Back', 1.2)],
dtype={'names': ['horse_name', 'price'],
⮑ 'formats': ['<U12', '<f4'],
⮑ 'offsets': [0, 48], 'itemsize': 56})
>>> race_results[race_results["position"] == 1]["horse_name"]
array(['Fast Eddie'], dtype='<U12')
Le tableau structuré est défini aux lignes 3 à 14, mais commencez par regarder les lignes 4 à 8, qui définissent ce qui ressemble à un tableau NumPy normal. Il se compose de trois lignes et trois colonnes de données relatives à une course hippique. Vous pouvez facilement identifier les noms des chevaux à partir des données, mais vous aurez peut-être un peu de mal à comprendre la signification des deux autres chiffres.
Ce tableau est en fait un tableau structuré en raison de la définition de ses types de données à la ligne 9. Chaque type de données est constitué d'un nom de champ et d'un type de données associé. Les trois champs sont horse_name
, price
et position
. Leurs types de données associés sont définis à l'aide de codes de protocole d'interface de tableau. U12
définit une chaîne de 12 caractères, tandis que f4
et i4
spécifient les formats 4
à virgule flottante et entière. , respectivement.
Une fois que vous avez configuré votre tableau structuré, vous pouvez utiliser ces noms de champs pour référencer des colonnes. À la ligne 16, vous avez utilisé "horse_name"
pour afficher un tableau de noms de chevaux de course. Pour trouver l'ordre d'arrivée, vous avez passé le champ "position"
dans la fonction np.sort()
à la ligne 19. Cela a trié les coureurs dans leur ordre d'arrivée. Vous avez ensuite filtré le résultat pour afficher uniquement horse_name
et price
. Enfin, à la ligne 27, vous avez sélectionné le nom du cheval gagnant.
Réconciliation de différents tableaux
L'inclusion de noms de champs dans vos tableaux NumPy présente de nombreux objectifs utiles. Supposons que vous vouliez faire correspondre les enregistrements en faisant correspondre les noms de champs dans des tableaux NumPy distincts. Pour ce faire, vous joignez vos tableaux afin que seuls les enregistrements correspondants de chaque tableau soient affichés. Cette idée vous sera familière si vous avez déjà effectué une jointure interne SQL entre deux tables de base de données relationnelles. Le terme inner est utilisé pour définir la jointure utilisée ici.
Dans cette section, vous allez travailler avec deux nouveaux fichiers : issued_checks.csv
et cashed_checks.csv
.
Le fichier émis_checks.csv
contient quatre champs: check_id
, payee
, montant
, et date_issued
. Ce fichier simule un ensemble de chèques émis par votre entreprise à ses créanciers:
Check_ID,Payee,Amount,Date_Issued
1341,K Starmer,150.00,2024-03-29
1342,R Sunak,175.00,2024-03-29
1343,L Truss,30.00,2024-03-29
1344,B Johnson,45.00,2024-03-22
1345,T May,65.00,2024-03-22
1346,D Cameron,430.00,2024-03-22
1347,G Brown,100.00,2024-03-15
1348,T Blair,250.00,2024-03-15
1349,J Major,500.00,2024-03-15
1350,M Thatcher,220.00,2024-03-15
Le fichier cashed_checks.csv
ne contient que trois champs : check_ID
, Amount
et Date_Cashed
. Ce fichier simule un ensemble de chèques encaissés par les créanciers de votre entreprise :
Check_ID,Amount,Date_Cashed
1341,150.00,2024-04-12
1342,175.00,2024-04-16
1343,30.00,2024-04-12
1345,65.00,2024-04-12
1346,430.00,2024-04-08
1349,500.00,2024-04-08
1350,220.00,2024-04-15
Dites que vous vouliez voir le payé
, date_issued
, et date_cashed
pour les chèques qui ont été encaissés. Si vous regardez attentivement, vous verrez que les détails payée
et date_issued
ne sont pas inclus dans CashEd_Checks.csv
, tandis que les deux autres autres Les champs sont. Pour voir toutes les données dont vous avez besoin, vous devrez rejoindre les deux fichiers ensemble.
Ajoutez les fichiers issued_checks.csv
et cashed_checks.csv
au dossier de votre programme, puis exécutez ce code :
>>> import numpy.lib.recfunctions as rfn
>>> from pathlib import Path
>>> issued_dtypes = [
... ("id", "i8"),
... ("payee", "U10"),
... ("amount", "f8"),
... ("date_issued", "U10"),
... ]
>>> cashed_dtypes = [
... ("id", "i8"),
... ("amount", "f8"),
... ("date_cashed", "U10"),
... ]
>>> issued_checks = np.loadtxt(
... Path("issued_checks.csv"),
... delimiter=",",
... dtype=issued_dtypes,
... skiprows=1,
... )
>>> cashed_checks = np.loadtxt(
... Path("cashed_checks.csv"),
... delimiter=",",
... dtype=cashed_dtypes,
... skiprows=1,
... )
>>> cashed_check_details = rfn.rec_join(
... "id",
... issued_checks,
... cashed_checks,
... jointype="inner",
... )
>>> cashed_check_details[
... ["payee", "date_issued", "date_cashed"]
... ]
array([('K Starmer', '2024-03-29', '2024-04-12'),
('R Sunak', '2024-03-29', '2024-04-16'),
('L Truss', '2024-03-29', '2024-04-12'),
('T May', '2024-03-22', '2024-04-12'),
('D Cameron', '2024-03-22', '2024-04-08'),
('J Major', '2024-03-15', '2024-04-08'),
('M Thatcher', '2024-03-15', '2024-04-15')],
dtype={'names': ['payee', 'date_issued', 'date_cashed'],
⮑'formats': ['<U10', '<U10', '<U10'],
⮑'offsets': [8, 64, 104], 'itemsize': 144})
Pour vous joindre à deux tableaux Numpy, vous utilisez l'une des nombreuses fonctions d'assistance RecaRray, qui vous permettent de travailler avec des tableaux structurés. Pour y accéder, vous devez d'abord importer le numpy.lib.recfunctions
module de bibliothèque. Encore une fois, vous utiliserez la bibliothèque pathlib
pour accéder aux fichiers. Ils sont importés aux lignes 1 et 2.
Aux lignes 4 à 15, vous créez deux listes Python de tuples qui définissent les types de données à utiliser à la fois pour le fichier issued_checks.csv
et le fichier cashed_checks.csv
. Ceux-ci sont introduits dans le paramètre dtype
de np.loadtext()
pour lui permettre de lire correctement les fichiers dans les tableaux structurés NumPy.
Les fichiers réels sont lus par votre code aux lignes 17 à 29. Vous utilisez np.loadtext()
comme vous l'avez fait précédemment, mais cette fois vous définissez son paramètre skiprows
sur 1
. Cela garantira que la première ligne de chaque fichier est ignorée car elles contiennent chacune des informations d'en-tête et non des données.
Les deux tableaux Numpy lus à partir des fichiers sont ensuite réunis dans les lignes 31 à 36 en utilisant la fonction d'assistance .rec_join()
. Cette fonction utilise quatre paramètres. Le premier paramètre définit le champ avec lequel les deux tableaux seront joints. Dans ce cas, vous souhaitez rejoindre les tableaux en fonction de leurs champs id
, qui contiennent des numéros uniques identifiant chaque vérification dans les deux fichiers.
Ensuite, vous passez dans les noms des tableaux à joindre en tant que deuxième et troisième paramètres.
Le paramètre final est le plus intéressant. En spécifiant joinType="Inner"
, vous effectuez une jointure intérieure. Cela signifie que le tableau résultant ne contiendra que des enregistrements correspondants à partir des deux fichiers, fournissant tous les détails de tous les chèques encaissés. Tout enregistrement dont id
apparaît dans un fichier mais pas l'autre n'apparaîtra pas dans la sortie.
Pour confirmer que la jointure a fonctionné, examinez le résultat produit par les lignes 38 à 40. Le tableau cashed_check_details
contient le payee
, le date_issued
et Données date_cashed
. Les deux premiers de ces champs provenaient du fichier issued_checks.csv
, tandis que le dernier provenait de cashed_checks.csv
. Comme vous pouvez le constater, les enregistrements ont été correctement mis en correspondance.
Gérer les noms de champs en double
Supposons maintenant que vous souhaitiez également afficher le montant du chèque. Vous pourriez être tenté d'essayer ceci:
>>> cashed_check_details[
... [
... "payee",
... "date_issued",
... "date_cashed",
... "amount",
... ]
... ]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'amount'
Le keyerror
s'est produit car le champ montant
n'existe plus dans le tableau joint. Cela s'est produit parce que les deux fichiers de données d'origine contenaient un champ montant
. Il n'est pas possible d'avoir un tableau structuré contenant deux champs avec la même étiquette. L'opération de jointure les a renommés de manière unique.
Pour savoir comment ils s'appellent maintenant, vous affichez d'abord les types de données du tableau joint. Vous y accédez via la propriété .dtype
:
>>> cashed_check_details.dtype
dtype([('id', '<i8'), ('payee', '<U10'), ('amount1', '<f8'), ('amount2', '<f8'),
⮑ ('date_issued', '<U10'), ('date_cashed', '<U10')])
Si vous regardez attentivement le résultat, vous remarquerez que les champs amount
d'origine ont été renommés amount1
et amount2
. Pour voir leur contenu, vous devez utiliser ces noms. Dans ce scénario, les deux champs contiennent les mêmes données, peu importe lequel d’entre eux vous choisissez :
>>> cashed_check_details[
... [
... "payee",
... "date_issued",
... "date_cashed",
... "amount1",
... ]
... ]
array([('K Starmer', '2024-03-29', '2024-04-12', 150.),
('R Sunak', '2024-03-29', '2024-04-16', 175.),
('L Truss', '2024-03-29', '2024-04-12', 30.),
('T May', '2024-03-22', '2024-04-12', 65.),
('D Cameron', '2024-03-22', '2024-04-08', 430.),
('J Major', '2024-03-15', '2024-04-08', 500.),
('M Thatcher', '2024-03-15', '2024-04-15', 220.)],
dtype={'names': ['payee', 'date_issued', 'date_cashed', 'amount1'],
⮑ 'formats': ['<U10', '<U10', '<U10', '<f8'],
⮑'offsets': [8, 64, 104, 48], 'itemsize': 144})
Cette fois, les montants des chèques sont inclus dans votre sortie.
Maintenant que vous savez quels chèques vous avez émis et ce qui a été encaissé, vous voudrez peut-être voir une liste de chèques qui ne sont actuellement pas en casse:
>>> outstanding_checks = [
... check_id
... for check_id in issued_checks["id"]
... if check_id not in cashed_checks["id"]
... ]
>>> outstanding_checks
[np.int64(1344), np.int64(1347), np.int64(1348)]
>>> [int(check_id) for check_id in outstanding_checks]
[1344, 1347, 1348]
À la ligne 1, vous créez une liste nommée outstanding_checks
en utilisant une compréhension de liste. Celui-ci contiendra une liste de chèques avec une valeur id
qui se trouve dans le tableau issued_checks
mais pas dans le tableau cashed_checks
. Ceux-ci sont toujours en suspens.
Encore une fois, en utilisant le nom du champ, vous pouvez rapidement spécifier les champs à utiliser pour la comparaison et garder votre code hautement lisible.
Pour plus de commodité, vous extraire les nombres de votre tableau résultant en utilisant une deuxième compréhension de la liste à la ligne 10.
Pour être complet, vous souhaiterez peut-être savoir s’il y a eu des chèques encaissés frauduleusement sur votre compte que vous n’avez pas émis :
>>> [
... check_id
... for check_id in cashed_checks["id"]
... if check_id not in issued_checks["id"]
... ]
[]
La liste vide produite montre que, heureusement, il n'y en a pas. Ouf!
Gérer les valeurs de clé en double
Avant de rejoindre les tableaux NumPy, il est important de s'assurer qu'il n'y a pas de valeurs de clé en double. Ceux-ci peuvent entraîner des résultats indésirables car ils créent des relations un-à-plusieurs. Dans les cas où vos clés en double sont valides, par exemple lorsque vous devez effectuer une jointure un-à-plusieurs, une meilleure alternative consiste à utiliser la fonction merge()
dans pandas.
Le code ci-dessous utilise le fichier check_list_duplicates.csv
. Ce fichier a la même structure que le fichier issued_checks.csv
que vous avez utilisé précédemment, mais il existe un enregistrement en double :
>>> from pathlib import Path
>>> import numpy.lib.recfunctions as rfn
>>> issued_dtypes = [
... ("id", "i8"),
... ("payee", "U10"),
... ("amount", "f8"),
... ("date_issued", "U10"),
... ]
>>> issued_checks = np.loadtxt(
... Path("check_list_duplicates.csv"),
... delimiter=",",
... dtype=issued_dtypes,
... skiprows=1,
... )
>>> rfn.find_duplicates(np.ma.asarray(issued_checks))
masked_array(data=[(1344, 'B Johnson', 45.0, '2024-03-22'),
(1344, 'B Johnson', 45.0, '2024-03-22')],
mask=[(False, False, False, False),
(False, False, False, False)],
fill_value=(999999, 'N/A', 1e+20, 'N/A'),
dtype=[('id', '<i8'), ('payee', '<U10'),
⮑ ('amount', '<f8'), ('date_issued', '<U10')])
Pour rechercher les lignes en double dans un tableau structuré, vous pouvez utiliser la fonction d'assistance .find_duplicates()
. Cette fonction nécessite que vous lui transmettiez un tableau masqué, qui est un tableau pouvant contenir des entrées manquantes ou invalides. Bien que votre tableau convienne à cet égard, vous devez le convertir en tableau masqué avant de le transmettre à la fonction. Vous pouvez voir à la ligne 19 du résultat qu'il y a une ligne en double : l'enregistrement 1344
apparaît deux fois.
Pour vous en débarrasser, vous pouvez utiliser la fonction np.unique()
:
>>> issued_checks = np.unique(issued_checks, axis=0)
>>> issued_checks
array([(1341, 'K Starmer', 150., '2024-03-29'),
(1342, 'R Sunak', 175., '2024-03-29'),
(1343, 'L Truss', 30., '2024-03-29'),
(1344, 'B Johnson', 45., '2024-03-22'),
(1345, 'T May', 65., '2024-03-22'),
(1346, 'D Cameron', 430., '2024-03-22'),
(1347, 'G Brown', 100., '2024-03-15'),
(1348, 'T Blair', 250., '2024-03-15'),
(1349, 'J Major', 500., '2024-03-15'),
(1350, 'M Thatcher', 220., '2024-03-15')],
dtype=[('id', '<i8'), ('payee', '<U10'),
⮑ ('amount', '<f8'), ('date_issued', '<U10')])
Pour supprimer les lignes en double, vous transmettez votre tableau issued_checks
, ainsi que axis=0
à np.unique()
pour lui dire de supprimer les lignes. Cela crée un nouveau tableau et toutes les lignes en double sont supprimées, ne laissant qu'une seule instance. Si vous regardez le résultat, vous verrez que la ligne 1344
n'apparaît désormais qu'une seule fois.
Tester vos compétences: rejoindre des tableaux ensemble
Il est temps de relever votre prochain défi. Voyez si vous pouvez résoudre cet exercice :
Une compagnie aérienne contient des données sur ses passagers dans deux fichiers séparés: Passengers.csv
et Fichier Passports.csv
. Jetez un œil aux deux fichiers, que vous trouverez dans les matériaux téléchargeables, pour vous familiariser avec leur contenu. Maintenant, utilisez vos compétences pour résoudre les tâches suivantes:
Tâche 1: Produisez un tableau structuré contenant le prénom, le nom de famille et la nationalité de chaque passager.
Tâche 2 : Déterminez s'il y a des passagers sur la liste qui n'ont pas de passeport.
Tâche 3 : Déterminez s'il y a des passeports sur la liste qui n'appartiennent à aucun passager.
Une solution possible à la tâche 1 est :
>>> import numpy as np
>>> import numpy.lib.recfunctions as rfn
>>> passenger_dtype = [
... ("passenger_no", "i8"),
... ("first_name", "U20"),
... ("last_name", "U20"),
... ]
>>> passport_dtype = [
... ("passport_no", "i8"),
... ("passenger_no", "i8"),
... ("nationality", "U20"),
... ]
>>> passengers = np.unique(
... np.loadtxt(
... "passengers.csv",
... delimiter=",",
... dtype=passenger_dtype,
... skiprows=1,
... ),
... axis=0,
... )
>>> passports = np.unique(
... np.loadtxt(
... "passports.csv",
... delimiter=",",
... dtype=passport_dtype,
... skiprows=1,
... ),
... axis=0,
... )
>>> flight_details = rfn.rec_join(
... "passenger_no",
... passengers,
... passports,
... jointype="inner",
... )
>>> flight_details[
... ["first_name", "last_name", "nationality"]
... ]
rec.array([('Olivia', 'Smith', 'British'), ('Amelia', 'Jones', 'British'),
('Isla', 'Williams', 'American'),
('Ava', 'Taylor', 'American'), ('Ivy', 'Brown', 'Austrian'),
('Freya', 'Davies', 'Norwegian'), ('Lily', 'Evans', 'French'),
('Florence', 'Wilson', 'German'), ('Mia', 'Thomas', 'Danish'),
('Willow', 'Johnson', 'Dutch'), ('Noah', 'Roberts', 'Dutch'),
('Oliver', 'Robinson', 'French'),
('George', 'Thompson', 'Danish'),
('Muhammad', 'Walker', 'Dutch'), ('Leo', 'White', 'British'),
('Harry', 'Edwards', 'American'),
('Oscar', 'Hughes', 'Spanish'),
('Archie', 'Green', 'Norwegian'), ('Henry', 'Hall', 'British')],
dtype={'names': ['first_name', 'last_name', 'nationality'],
⮑ 'formats': ['<U20', '<U20', '<U20'],
⮑ 'offsets': [8, 88, 176], 'itemsize': 256})
Vous commencez par créer deux listes, passenger_dtype
et passport_dtype
, comme indiqué aux lignes 4 et 10. Chacune stocke des tuples qui seront utilisés pour définir les types de données de chaque colonne. dans les tableaux structurés qui contiendront les données du fichier. Chaque tuple se compose d'un nom de champ et d'un type de données. Vous vous souviendrez peut-être que i8
spécifie un entier, tandis que U20
définit une chaîne de vingt caractères.
Aux lignes 16 et 26, vous lisez les données de chaque fichier dans les tableaux passengers
et passsports
, respectivement. Vous utilisez également np.unique()
pour garantir la suppression des enregistrements en double. Si vous oubliez de le faire, vous verrez des valeurs N/A
apparaître dans vos résultats. Notez que vous ignorez la ligne supérieure de chaque fichier car elle contient des en-têtes.
Dans la ligne 36, vous rejoignez vos deux plans Passagers
et
en utilisant leur Passenger_No
champs comme clé de jointure. Il s'agit d'une jointure intérieure, ce qui signifie que seuls les passagers avec passeport sont inclus dans la sortie.
À la ligne 43, vous affichez les détails du vol requis. Le résultat affiche les noms des passagers du fichier passengers.csv
et leurs nationalités du fichier passports.csv
. Cette approche tire parti de la commodité et de la clarté des noms de champs pour extraire les données.
Une solution possible à la tâche 2 est:
>>> passengers_without_passports = [
... passenger
... for passenger in passengers["passenger_no"]
... if passenger not in passports["passenger_no"]
... ]
>>> passengers_without_passports
[np.int64(14)]
Pour répondre à cette question, vous utilisez une compréhension de liste qui parcourt chaque passenger_no
dans votre tableau passengers
et renvoie uniquement ceux qui n'apparaissent pas dans votre Tableau passeports
. Dans ce cas, le passager 14
n'a pas fourni les détails de son passeport.
Une solution possible à la tâche 3 est :
>>> passports_without_passengers = [
... passenger
... for passenger in passports["passenger_no"]
... if passenger not in passengers["passenger_no"]
... ]
>>> passports_without_passengers
[np.int64(21)]
Pour répondre à cette question, vous utilisez à nouveau une compréhension de la liste, mais cette fois, il traverse chaque passager_no
dans votre Passports
Array et ne renvoie que ceux qui ne pas apparaissent dans votre tableau Passagers
. Dans ce cas, le passeport appartenant au passager inexistant 21
est révélé.
Dans la section suivante, vous apprendrez à analyser les données hiérarchiques.
Exemple NumPy 3 : analyse et représentation graphique de données hiérarchiques
Les données hiérarchiques sont des données constituées de différents niveaux, chaque niveau étant lié à ceux immédiatement supérieurs et inférieurs. Il est souvent dessiné à l’aide d’un diagramme en arbre et les différents niveaux sont souvent décrits comme ayant une relation parent-enfant.
Par exemple, vous pouvez avoir une organisation avec plusieurs départements, chaque département contenant plusieurs membres du personnel. Il existe une relation hiérarchique entre l'organisation, ses données départementales et les données des membres du personnel qui travaillent dans chaque département.
Les tableaux structurés sont bien adaptés pour joindre des données hiérarchiques car ils vous permettent de référencer les données par des étiquettes. Pour utiliser NumPy avec des données hiérarchiques, vous pouvez les consolider dans un seul tableau.
Remarque : Lorsque vous rejoignez des tableaux NumPy avec rec_join()
, vous êtes limité à la création de jointures internes. Si vous avez besoin de fonctionnalités de jointure plus complètes, vous devriez plutôt envisager d'utiliser la fonction pandas merge()
.
Dans cette section, vous envisagerez d'analyser un portefeuille d'actions. Un portefeuille d'actions est le nom donné à la collection d'actions détenues dans une variété de sociétés. La création d'un portefeuille est une stratégie intelligente pour les investisseurs car elle contribue à répartir les risques d'investissement. L'idée est que les pertes subies dans certaines actions seront compensées par des gains dans d'autres.
Votre portefeuille d'actions contient des données hiérarchiques car elle se compose de plusieurs investissements, chacun avec sa propre collection de mouvements de prix quotidiens. En analysant ces données, vous pouvez voir dans quelle mesure votre portefeuille fonctionne.
Création du tableau vide
Les données utilisées dans cette section simulent certaines données hiérarchiques. Supposons que vous mainteniez un fichier contenant une liste des différentes sociétés dans lesquelles vous détiennez des partages. Dans cet exemple, vous trouverez ces informations dans le fichier portfolio.csv
inclus dans vos téléchargements. Ici, il est illustré ci-dessous:
Company,Sector
Company_A,technology
Company_B,finance
Company_C,healthcare
Company_D,technology
Company_E,finance
Company_F,healthcare
La colonne Entreprise
contient les noms des entreprises, tandis que la colonne Secteur
contient les secteurs auxquels l'entreprise appartient.
Chaque jour, au cours d'une semaine, vous téléchargez le cours des actions pour chacune des sociétés qui vous intéressent et les ajoutez à un tableau Numpy structuré nommé Portfolio
. Vous trouverez les données de prix de chaque jour dans une série de fichiers séparés nommés share_prices-n.csv
, où n
est un nombre entre un et cinq. Par exemple, share_prices-1.csv
contient les prix de lundi, share_prices-2.csv
les prix de mardi, etc.
Un exemple de fichier de cours des actions est indiqué ci-dessous:
Company,mon
Company_A,100.5
Company_B,200.1
Company_C,50.3
Company_D,110.5
Company_E,200.1
Company_F,55.3
Dans ce fichier share_prices-1.csv
, il y a deux colonnes. La colonne Société
affiche les noms des sociétés, tandis que la colonne mon
affiche les prix de chaque action de la société pour lundi. Le reste des fichiers suit un schéma similaire, à l'exception de leurs colonnes de jour, qui sont différentes.
Pour analyser ces données, votre tableau Portfolio
aura besoin de champs sept . En plus du nom de l'entreprise et du secteur auquel il appartient, vous aurez également besoin de cinq champs pour détenir les prix quotidiens de chaque entreprise. Les deux premiers champs seront des chaînes, tandis que le reste sera des flotteurs.
Le code ci-dessous crée le tableau initial portfolio
:
>>> import numpy as np
>>> from pathlib import Path
>>> days = ["mon", "tue", "wed", "thu", "fri"]
>>> days_dtype = [(day, "f8") for day in days]
>>> company_dtype = [("company", "U20"), ("sector", "U20")]
>>> portfolio_dtype = np.dtype(company_dtype + days_dtype)
>>> portfolio = np.zeros((6,), dtype=portfolio_dtype)
>>> portfolio
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.)],
dtype=[('company', '<U20'), ('sector', '<U20'), ('mon', '<f8'),
⮑ ('tue', '<f8'), ('wed', '<f8'), ('thu', '<f8'), ('fri', '<f8')])
Comme auparavant, vous utiliserez à la fois les bibliothèques numpy
et pathlib
, vous les importerez donc dans les lignes 1 et 2.
Pour définir les noms de champ et les types de données de chaque champ dans votre tableau Portfolio
, vous commencez par créer les trois listes indiquées dans les lignes 4, 5 et 6. Liste days_dtype
Contient une série de tuples, une pour chaque valeur dans le tableau des jours
que vous avez créé dans la ligne 4, avec un type de données de f8
, qui représente un numéro de point flottant. La liste company_dtype
contient des définitions des données de l'entreprise du fichier du portfolio.csv
.
Pour créer l'objet de type de données réel qui définira les types de données du tableau portfolio
, vous concaténez les listes company_dtype
et days_dtype
ensemble. Ensuite, vous convertissez le résultat en un objet dtype
à l'aide de la fonction np.dtype()
comme indiqué à la ligne 8.
L'objet dType
est ensuite introduit dans la fonction np.zeros()
comme paramètre dtype
. Vous configurez également votre tableau avec une forme de (6,)
pour fournir une ligne distincte pour les données de chaque partage. Cela produit un tableau contenant des chaînes vides dans les deux premiers champs et zéros dans le reste, comme indiqué dans la sortie.
Peupler le tableau
Maintenant que vous avez créé un tableau suffisamment grand pour stocker toutes les données dont vous avez besoin, la prochaine étape consiste à commencer à les remplir. Pour commencer, vous ajouterez les détails des sociétés stockées dans portfolio.csv
:
>>> companies = np.loadtxt(
... Path("portfolio.csv"),
... delimiter=",",
... dtype=company_dtype,
... skiprows=1,
... ).reshape((6,))
>>> portfolio[["company", "sector"]] = companies
>>> portfolio
array([('Company_A', 'technology', 0., 0., 0., 0., 0.),
('Company_B', 'finance', 0., 0., 0., 0., 0.),
('Company_C', 'healthcare', 0., 0., 0., 0., 0.),
('Company_D', 'technology', 0., 0., 0., 0., 0.),
('Company_E', 'finance', 0., 0., 0., 0., 0.),
('Company_F', 'healthcare', 0., 0., 0., 0., 0.)],
dtype=[('company', '<U20'), ('sector', '<U20'), ('mon', '<f8'),
⮑ ('tue', '<f8'), ('wed', '<f8'), ('thu', '<f8'), ('fri', '<f8')])
Vous utilisez à nouveau la fonction loadtxt()
, cette fois pour ajouter des données de portfolio.csv
dans un tableau structuré nommé entreprises
. Notez que .reshape((6,))
a été utilisé à la ligne 6 pour donner au tableau la même forme que le tableau portfolio
que vous avez créé précédemment. Ceci est nécessaire pour insérer des entreprises
dans le portefeuille
.
La ligne 8 est l'endroit où l'insertion a lieu. Les deux Company
et sector
dans portfolio
, comme vous pouvez le voir dans le sortir.
Il ne reste plus qu'à additionner les différents cours quotidiens des actions. Le code pour ce faire est présenté ci-dessous :
>>> share_prices_dtype = [("company", "U20"),("day", "f8"),]
>>> for day, csv_file in zip(
... days, sorted(Path.cwd().glob("share_prices-?.csv"))
... ):
... portfolio[day] = np.loadtxt(
... csv_file.name,
... delimiter=",",
... dtype=share_prices_dtype,
... skiprows=1,
... )["day"]
>>> portfolio
array([('Company_A', 'technology', 100.5, 101.2, 102. , 101.8, 112.5),
('Company_B', 'finance', 200.1, 199.8, 200.5, 201. , 200.8),
('Company_C', 'healthcare', 50.3, 50.5, 51. , 50.8, 51.2),
('Company_D', 'technology', 110.5, 101.2, 102. , 111.8, 97.5),
('Company_E', 'finance', 200.1, 200.8, 200.5, 211. , 200.8),
('Company_F', 'healthcare', 55.3, 50.5, 53. , 50.8, 52.2)],
dtype=[('company', '<U20'), ('sector', '<U20'), ('mon', '<f8'),
⮑ ('tue', '<f8'), ('wed', '<f8'), ('thu', '<f8'), ('fri', '<f8')])
Pour commencer, vous créez une liste définissant les deux champs dans chacun des fichiers Daily Share_Prices -
. Pour ajouter ces données dans votre tableau principal Portfolio
, vous créez une fois de plus une boucle qui itère sur chacun d'elles, mais cette fois, vous le faites en utilisant Python zip() /Code de Python > fonction. Les fichiers seront traités dans l'ordre comme vous l'avez appris plus tôt.
Dans ce cas, vous passez zip()
la liste des jours
que vous avez défini précédemment, ainsi que chacun des fichiers que vous traitez. Cela produit une série de tuples, un pour chaque jour et une paire de fichiers trouvés. Chaque jour dans le tuple est attribué à la variable day
de la boucle, tandis que chaque fichier est affecté à la variable CSV_FILE
.
Dans le corps de la boucle, chaque fichier est à nouveau lu en utilisant np.loadtxt()
, mais cette fois, au lieu de conserver l'intégralité du fichier, seules les données du champ jour
sont stockées. . Au premier tour de boucle, ces données sont insérées dans le champ mon
du tableau portfolio
, la deuxième fois elles sont insérées dans le champ tue
, et ainsi de suite. Vous l'avez fait en attribuant les données lues dans portfolio[day]
pour chaque jour différent.
La version finale du tableau contient le nom de l'entreprise ainsi que le secteur auquel il appartient. Les cinq derniers chiffres de chaque dossier sont les cours des actions du lundi au vendredi en cents.
Maintenant que vous avez combiné vos données hiérarchiques dans un tableau structuré, vous pouvez l'analyser en utilisant les noms de champ pour simplicité. Supposons que vous souhaitiez extraire une seule entreprise pour un examen plus approfondi:
>>> portfolio[portfolio["company"] == "Company_C"]
array([('Company_C', 'healthcare', 50.3, 50.5, 51., 50.8, 51.2)],
dtype=[('company', '<U20'), ('sector', '<U20'),
⮑ ('mon', '<f8'), ('tue', '<f8'), ('wed', '<f8'),
⮑ ('thu', '<f8'), ('fri', '<f8')])
Ici, vous extrayez une seule entreprise en la localisant dans la colonne entreprise
. Pour ce faire, vous utilisez portfolio["company"] == "Company_C"
, qui sélectionne les lignes dont les valeurs de colonne company
correspondent à "Company_C"
. Cette méthode est bien plus intuitive que la sélection de lignes avec indexation.
De même, si vous souhaitez voir comment les sociétés de la technologie de la technologie de votre portefeuille se produisent un vendredi, vous pouvez également sélectionner ces chiffres:
>>> portfolio[portfolio["sector"] == "technology"]["fri"]
array([112.5, 97.5])
Pour afficher les enregistrements technologiques, vous utilisez portfolio["sector"] == "technology"
. Ensuite, pour voir uniquement les enregistrements du vendredi, vous les filtrez en utilisant ["fri"]
.
Supposons que vous possédiez 250 actions dans chacune de vos sociétés technologiques. Vous voudrez peut-être voir combien d'argent ils valent à la fin de la semaine:
>>> portfolio[portfolio["sector"] == "technology"]["fri"] * 250 * 0.01
array([281.25, 243.75])
Pour ce faire, vous utilisez les techniques que vous avez déjà appris à sélectionner les chiffres du vendredi pour chacune des parties Technology
de votre portefeuille. Ensuite, vous multipliez les chiffres par 250
, qui est le nombre de partages que vous possédez. Enfin, vous multipliez le résultat par 0,01
pour transformer les montants en dollars, en gardant à l'esprit que les cours des actions sont cités dans CENTS. Si vous voulez la valeur nette, tout ce que vous avez à faire est d'utiliser sum()
:
>>> sum(portfolio[portfolio["sector"] == "technology"]["fri"] * 250 * 0.01)
np.float64(525.0)
La partie Technology
de votre portefeuille vaut 525,00 $.
Comme vous pouvez le voir, l'utilisation de tableaux structurés vous permet d'accéder aux données de manière très intuitive. Pour aller plus loin, vous pouvez également utiliser cette approche lorsque vous utilisez un tableau structuré comme base d'un graphique matplotlib. C'est ce que vous ferez ensuite.
Tracer les données
Supposons que vous souhaitiez afficher l'analyse de la partie de la technologie
de votre portefeuille sur un graphique. Encore une fois, parce que vous travaillez avec un tableau structuré, le code devient intuitif:
>>> import matplotlib.pyplot as plt
>>> tech_mask = portfolio["sector"] == "technology"
>>> tech_sector = portfolio[tech_mask]["company"]
>>> tech_valuation = portfolio[tech_mask]["fri"] * 250 * 0.01
>>> (
... plt.bar(x=tech_sector, height=tech_valuation, data=tech_valuation)[0]
... .set_color("g")
... )
>>> plt.xlabel("Tech Companies")
>>> plt.ylabel("Friday Price ($)")
>>> plt.title("Tech Share Valuation ($)")
>>> plt.show()
Vous créez d’abord le tableau d’assistance tech_mask
. Ensuite, vous créez deux tableaux qui seront utilisés dans le tracé. Le tableau tech_sector
, défini à la ligne 4, contient les noms de sociétés pour chaque société tech_sector
. Le tableau tech_valuation
, défini à la ligne 5, contient les valorisations du vendredi pour chaque entreprise tech_sector
.
Les lignes 7 à 10 créent le tracé de la barre. L'axe X contient les noms de l'entreprise tech_sector
utilisés pour créer les barres, tandis que le paramètre hauteur
contient leurs évaluations. Les valeurs tech_valuation
sont ce qui est tracé. Les lignes 11, 12, 13 et 14 produisent des étiquettes pour les axes et donnent au graphique un titre. Enfin, l'intrigue est affichée.
Si vous exécutez le code ci-dessus dans un bloc-notes Jupyter, vous n'avez pas besoin d'utiliser plt.show()
. Si vous l'exécutez dans le REPL Python standard, les références aux objets seront affichées après les lignes 11, 12 et 13. Vous pouvez simplement les ignorer et elles ont été supprimées de la sortie pour plus de clarté.
Le tracé résultant est illustré ci-dessous:
Comme vous pouvez le constater, Company_A
semble s'en sortir mieux que les deux, quoique légèrement.
Tester vos compétences: analyse et cartographie des données hiérarchiques
Avant de continuer, voici votre troisième défi :
Il vous a été demandé de rassembler les températures moyennes mensuelles quotidiennes pour chaque mois de l’année à des fins d’analyse. Les données sont stockées dans les fichiers london_temperatures.csv
, new_york_temperatures.csv
et rome_temperatures.csv
.
En utilisant les compétences que vous avez acquises, créez un tableau weather_data
structuré avec des noms de champs et des types de données sensibles contenant quatre valeurs de données pour chaque mois. Le premier doit contenir le mois, tandis que les trois autres doivent contenir la température mensuelle pour chaque ville.
Utilisez votre tableau structuré pour tracer chaque température mensuelle pour les trois villes sur un tracé linéaire.
Une solution possible pour créer le tableau est:
>>> import numpy as np
>>> from pathlib import Path
>>> cities = ["london", "new_york", "rome"]
>>> cities_dtype = [(city, "i8") for city in cities]
>>> city_files_dtype = [("month", "U20"), ("temp", "i8")]
>>> weather_data_dtype = np.dtype([("month", "U20")] + cities_dtype)
>>> weather_data = np.zeros((12,), dtype=weather_data_dtype)
>>> weather_data
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), ('', 0, 0, 0), ('', 0, 0, 0)],
dtype=[('month', '<U20'), ('london', '<i8'),
⮑ ('new_york', '<i8'), ('rome', '<i8')])
>>> for city in cities:
... temps = np.loadtxt(
... Path(f"{city}_temperatures.csv"),
... delimiter=",",
... dtype=city_files_dtype,
... )
... weather_data[["month", city]] = temps
...
>>> weather_data
array([('Jan', 5, 2, 8), ('Feb', 7, 2, 9), ('Mar', 9, 4, 12),
('Apr', 11, 11, 14), ('May', 14, 16, 21), ('Jun', 16, 22, 23),
('Jul', 19, 25, 26), ('Aug', 19, 24, 24), ('Sep', 17, 20, 22),
('Oct', 13, 14, 18), ('Nov', 10, 12, 13), ('Dec', 7, 9, 10)],
dtype=[('month', '<U20'), ('london', '<i8'),
⮑ ('new_york', '<i8'), ('rome', '<i8')])
Vous commencez par créer un tableau de taille correcte à l'avance pour accepter les données. Comme précédemment, vous utilisez des listes et des compréhensions de liste pour définir chaque champ et son type de données avant de produire le tableau weather_data
dans les lignes 9 et 10. Vous pouvez voir le résultat des lignes 11 à 14. Notez que chaque < Code> Mois Le champ est initialisé avec une chaîne vide, tandis que chaque champ entier est défini sur 0
.
À la ligne 17, vous commencez à boucler sur les villes individuelles. Pour Londres, vous lisez le fichier London_Temperatures.csv
dans un Temps
, et dans la ligne 23, vous attribuez ses données au mois
et Londres
champs de votre weather_data
Array. Les données de New York et de Rome sont lues et ajoutées de la même manière. Les étiquettes du mois sont écrasées pour chaque ville. Mais c'est bien tant qu'ils sont les mêmes dans tous les ensembles de données.
Les lignes 27 à 32 montrent le tableau complet. Une possibilité pour le tracé linéaire pourrait être la suivante :
>>> import matplotlib.pyplot as plt
>>> plt.plot(weather_data["month"], weather_data["london"])
>>> plt.plot(weather_data["month"], weather_data["new_york"])
>>> plt.plot(weather_data["month"], weather_data["rome"])
>>> plt.ylabel("Temperature (C)")
>>> plt.xlabel("Month")
>>> plt.title("Average Monthly Temperatures")
>>> plt.legend(["London", "New York", "Rome"])
>>> plt.show()
Pour construire le tracé, vous tracez les mois le long de l’axe des x et créez une ligne distincte pour chacune des températures des trois villes le long de l’axe des y. Vous ajoutez ensuite des étiquettes à vos deux axes de graphique, ainsi qu'un titre et une légende.
Votre sortie finale devrait ressembler à ceci:
Comme vous pouvez le constater, parmi les trois villes, Rome connaît des températures systématiquement plus élevées.
Dans la dernière section, vous découvrirez l'une des principales efficacités de Numpy et comment écrire des fonctions qui en profitent.
Numpy Exemple 4: Rédaction de vos propres fonctions vectorisées
L'une des efficacités de NumPy est sa capacité à effectuer des calculs sur des tableaux entiers sans que le programmeur ait à écrire des boucles lentes qui parcourent manuellement chaque ligne ou élément. Au lieu de cela, NumPy utilise le langage C sous-jacent pour effectuer le calcul sur l'ensemble du tableau. C'est ce qu'on appelle la vectorisation.
Dans cette dernière section, vous travaillerez avec le fichier full_portfolio.csv
illustré ci-dessous:
Company,Sector,Mon,Tue,Wed,Thu,Fri
Company_A,technology,100.5,101.2,102,101.8,112.5
Company_B,finance,200.1,199.8,200.5,201.0,200.8
Company_C,healthcare,50.3,50.5,51.0,50.8,51.2
Company_D,technology,110.5,101.2,102,111.8,97.5
Company_E,finance,200.1,200.8,200.5,211.0,200.8
Company_F,healthcare,55.3,50.5,53.0,50.8,52.2
Ces données vous semblent familières car il s'agit d'une fusion des fichiers utilisés dans la section précédente. La rubrique de chaque colonne a la même signification qu'auparavant.
Le code ci-dessous montre la vectorisation en action :
>>> import numpy as np
>>> from pathlib import Path
>>> share_dtypes = [
... ("company", "U20"),
... ("sector", "U20"),
... ("mon", "f8"),
... ("tue", "f8"),
... ("wed", "f8"),
... ("thu", "f8"),
... ("fri", "f8"),
... ]
>>> portfolio = np.loadtxt(
... Path("full_portfolio.csv"),
... delimiter=",",
... dtype=share_dtypes,
... skiprows=1,
... )
>>> portfolio["fri"] - portfolio["mon"]
array([ 12. , 0.7, 0.9, -13. , 0.7, -3.1])
Après avoir construit le tableau Portfolio
Array structuré, vous décidez de voir comment vos parts ont fonctionné au cours de la semaine. Pour ce faire, vous choisissez deux tableaux - un contenant le cours des actions du lundi et un contenant ceux de vendredi. Pour voir le changement hebdomadaire, vous soustraire les prix du lundi des prix du vendredi.
Remarquez à la ligne 21, que bien que vous soustiez un tableau de l'autre, Numpy soustrait chaque élément individuel des tableaux sans que vous ayez à écrire du code qui les traverse individuellement. C'est une vectorisation.
Supposons maintenant que vous obteniez un bonus supplémentaire de 10% sur les actions qui ont augmenté de plus de 1% au cours de la semaine que vous analysez. Pour trouver votre bénéfice, y compris le bonus, vous devez considérer deux cas - ces actions obtenant le bonus et celles qui ne le font pas. Pour ce faire, vous pourriez essayer ce qui suit:
>>> def profit_with_bonus(first_day, last_day):
... if last_day >= first_day * 1.01:
... return (last_day - first_day) * 1.1
... else:
... return last_day - first_day
...
>>> profit_with_bonus(portfolio["mon"], portfolio["fri"])
Traceback (most recent call last):
...
ValueError: The truth value of an array with more than one element is ambiguous.
⮑ Use a.any() or a.all()
Aux lignes 1 à 5, vous avez défini une fonction nommée profit_with_bonus()
qui vous renverra votre profit en fonction de l'augmentation du prix de votre action au cours de la semaine. Si le bénéfice est supérieur à 1 %, vous ajoutez 10 % supplémentaires en bonus.
Lorsque vous appelez votre fonction à la ligne 7, elle lève une exception ValueError
car elle ne peut pas interpréter les tableaux que vous lui avez transmis. Cela ne fonctionnera que si vous lui transmettez deux chiffres. Pour que cette fonction fonctionne avec des tableaux, continuez à lire.
Ajout de fonctionnalités de vectorisation avec np.vectorize()
Pour faire fonctionner votre fonction Profit_With_Bonus()
avec des tableaux, vous devrez le transformer en une fonction vectorisée . Une façon de le faire est d'utiliser la fonction np.vectorize()
. Cette fonction renverra une version de votre fonction d'origine, mais qui prend des tableaux en entrée au lieu de scalaires.
Le code ci-dessous transforme votre fonction en une fonction vectorisée:
>>> def profit_with_bonus(first_day, last_day):
... if last_day >= first_day * 1.01:
... return (last_day - first_day) * 1.1
... else:
... return last_day - first_day
...
>>> vectorized_profit_with_bonus = np.vectorize(profit_with_bonus)
>>> vectorized_profit_with_bonus(portfolio["mon"], portfolio["fri"])
array([ 13.2 , 0.7 , 0.99, -13. , 0.7 , -3.1 ])
Pour transformer votre fonction Profit_With_Bonus()
en une version vectorisée, vous la transmettez dans la fonction np.vectorize()
à la ligne 7. La version vectorisée est ensuite attribuée au < Code> Vectrized_Profit_With_Bonus Nom.
À la ligne 8, vous appelez la nouvelle fonction et lui transmettez les tableaux que vous souhaitez analyser. Dans cet exemple, le résultat montre votre bénéfice, y compris le bonus supplémentaire. Si vous comparez les chiffres au calcul simple des bénéfices que vous avez effectué plus tôt, vous remarquerez que vous avez reçu un bonus pour vos actions dans la première et la troisième société. Notez que vous n'avez pas besoin de modifier la fonction d'origine dans les lignes 1 à 5.
Un dernier point sur l'utilisation de np.vectorise()
pour vous à noter est que la fonction d'origine est toujours disponible si vous en avez besoin:
>>> in_profit(3, 5)
2.2
Cette fois, une seule valeur est renvoyée. Puisque 5 - 3
est 2
et que 2
représente plus de 1 % de 3
, vous ajoutez 10 % à obtenez 2.2
.
Ajout d'une fonctionnalité de vectorisation avec @np.vectorize
Comme vous venez de le voir, la fonction np.vectorize()
crée une deuxième fonction, ce qui signifie que la version originale est toujours disponible si vous avez besoin de l'utiliser. Comme alternative, vous pouvez utiliser np.vectorize
comme décorateur à la place:
>>> @np.vectorize
... def profit_with_bonus(first_day, last_day):
... if last_day >= first_day * 1.01:
... return (last_day - first_day) * 1.1
... else:
... return last_day - first_day
...
>>> profit_with_bonus(portfolio["mon"], portfolio["fri"])
array([ 13.2 , 0.7 , 0.99, -13. , 0.7 , -3.1 ])
En appliquant @np.vectorize
à votre fonction en ligne 1, la fonction profit_with_bonus()
est transformée en une version vectorisée. Cette nouvelle version fonctionne de la même manière que la fonction vectorized_profit_with_bonus()
que vous avez vue précédemment. Pour l'utiliser, appelez profit_with_bonus()
comme vous le feriez normalement, mais assurez-vous d'y transmettre vos tableaux. Cela renverra le même tableau de bénéfices qu'auparavant.
Contrairement à la version créée par la fonction np.vectorize()
, la version scalaire originale de profit_with_bonus()
n'existe plus. Cependant, si vous lui transmettez deux scalaires, cela fonctionnera toujours mais renverra le résultat sous forme de tableau :
>>> in_profit(3, 5)
array(2.2)
Comme vous pouvez le voir, la sortie est un tableau cette fois.
Utilisation des fonctionnalités de vectorisation existantes avec np.where()
De nombreuses fonctions NumPy disponibles prennent déjà en charge la vectorisation. C'est une bonne idée de consulter la documentation pour voir s'il existe déjà une fonction disponible pour répondre à vos besoins. Pour trouver votre profit sur les actions, par exemple, vous auriez pu utiliser la fonction np.where()
pour prendre en compte à la fois le cas du bonus et le cas du profit régulier au lieu d'écrire votre propre fonction :
>>> np.where(
... portfolio["fri"] > portfolio["mon"] * 1.01,
... (portfolio["fri"] - portfolio["mon"]) * 1.1,
... portfolio["fri"] - portfolio["mon"],
... )
array([ 13.2 , 0.7 , 0.99, -13. , 0.7 , -3.1 ])
Cette fois, vous passez votre condition dans np.where()
. Contrairement à votre fonction d'origine Profit_With_Bonus()
, .where()
prend en charge la vectorisation nativement. Dans ce cas, vous utilisez .where()
en lui faisant passer une comparaison entre deux tableaux. Vous le transmettez également deux tableaux calculés. Le premier représente le bénéfice avec un bonus ajouté de 10%, tandis que le second est le bénéfice simple.
Maintenant, np.where()
évaluera la condition de chaque élément et choisira l'élément correspondant dans l'un des deux tableaux. Si la condition est True
, alors il utilise l'élément du premier tableau, et si elle est False
, il sélectionne l'élément du second.
Testez vos compétences : écriture d'une fonction vectorisée
Il est temps de plonger dans votre dernier défi d'exercice. Vous avez presque fini.
Il vous a été demandé de créer deux tableaux contenant les températures minimales et maximales à partir du tableau weather_data
que vous avez créé lors de votre dernier défi. Si vous ne l'avez pas déjà fait, construisez le tableau weather_data
en exécutant le premier bloc de code de la solution de l'exercice Analyse et représentation graphique des données hiérarchiques avant d'aller plus loin.
Ensuite, écrivez une fonction nommée find_min_max()
qui accepte trois valeurs scalaires, par exemple des entiers, et renvoie les valeurs maximale et minimale. Maintenant, utilisez @np.vectorize
ou np.vectorize()
pour améliorer votre fonction afin qu'elle fonctionne avec les tableaux NumPy. Assurez-vous de pouvoir appeler la version vectorisée de votre fonction par son nom d'origine.
En tant que point de départ, votre fonction initiale pourrait ressembler à ceci:
>>> def find_min_max(first, second, third):
... min_temp = min(first, second, third)
... max_temp = max(first, second, third)
... return min_temp, max_temp
>>> find_min_max(2, 1, 3)
(1, 3)
Votre fonction initiale find_min_max()
prend trois valeurs scalaires et renvoie les valeurs les plus basses et les plus élevées. Si vous essayez en utilisant les données de votre tableau weather_data
, cela ne fonctionnera pas :
>>> london_temps = weather_data["london"]
>>> new_york_temps = weather_data["new_york"]
>>> rome_temps = weather_data["rome"]
>>> find_min_max(london_temps, new_york_temps, rome_temps)
>>> # ...
ValueError: The truth value of an array with more than one element is ambiguous.
Comme vous pouvez le voir, vous avez déclenché une exception ValueError
. Vous devrez le mettre à jour pour fonctionner avec des tableaux.
Une solution possible utilisant @np.vectorize
pourrait être :
>>> @np.vectorize
... def find_min_max(first, second, third):
... min_temp = min(first, second, third)
... max_temp = max(first, second, third)
... return min_temp, max_temp
>>> london_temps = weather_data["london"]
>>> new_york_temps = weather_data["new_york"]
>>> rome_temps = weather_data["rome"]
>>> find_min_max(london_temps, new_york_temps, rome_temps)
(array([ 2, 2, 4, 11, 14, 16, 19, 19, 17, 13, 10, 7]),
⮑ array([ 8, 9, 12, 14, 21, 23, 26, 24, 22, 18, 13, 10]))
Pour utiliser la version vectorisée de votre fonction find_min_max()
, vous le décorez avec @ np.vectorize
. Pour appeler la fonction vectorisée, vous passez directement dans les trois tableaux de données météorologiques. Le décorateur lui permet de comprendre les tableaux en entrées et de les produire sous forme de sorties. La fonction utilise les fonctions d'intégration min()
et max()
pour analyser chacun des trois éléments pour chaque mois et renvoie le résultat en deux tableaux.
Une solution alternative pourrait être de revenir à votre fonction d'origine et d'utiliser np.vectorize()
à la place :
>>> def find_min_max(first, second, third):
... min_temp = min(first, second, third)
... max_temp = max(first, second, third)
... return min_temp, max_temp
>>> london_temps = weather_data["london"]
>>> new_york_temps = weather_data["new_york"]
>>> rome_temps = weather_data["rome"]
>>> find_min_max = np.vectorize(find_min_max)
>>> find_min_max(london_temps, new_york_temps, rome_temps)
(array([ 2, 2, 4, 11, 14, 16, 19, 19, 17, 13, 10, 7]),
⮑ array([ 8, 9, 12, 14, 21, 23, 26, 24, 22, 18, 13, 10]))
Pour utiliser la version vectorisée de votre fonction find_min_max()
, vous la transmettez dans np.vectorize()
, et en attribuant la sortie à la variable find_min_max
, vous pouvez ensuite appeler la version vectorisée en utilisant son nom d'origine. Pour appeler cette version vectorisée, vous passez directement dans les trois tableaux de données météorologiques.