Python >> Tutoriel Python >  >> Python Tag >> NumPy

Tableaux structurés NumPy et tableaux d'enregistrement

Prérequis

  • Principes de base de Python
  • Les bases de Numpy

Résultats d'apprentissage du didacticiel

  • Comment les données structurées peuvent-elles être formées ?
  • Numpy Structured Array – Création, attribution de données et opérations
  • Création d'un type de données structuré (dtype)
  • Allocation de mémoire aux tableaux structurés
  • Record Arrays – Comment il est lié aux tableaux structurés
  • Comprendre les exigences du package Pandas

Les tableaux structurés sont des formes spéciales de tableaux NumPy. Ils stockent des données composées et hétérogènes, contrairement aux tableaux NumPy normaux qui stockent des données homogènes. Vous pouvez créer un tableau structuré, par exemple, avec la commande suivante :np.dtype({'names':('person_names', 'person_ages', 'is_python_programmer'), 'formats': ('U9', 'i8', 'bool')}) . Ce tableau structuré aurait trois colonnes avec trois types de données différents tels que définis dans les tuples.

Nous discuterons en détail des tableaux structurés Numpy. Ils forment l'épine dorsale de la trame de données Pandas. Cet article vous donne une base solide pour le package Pandas.

Pourquoi les tableaux structurés ?

Imaginons un scénario où nous avons une planète dans laquelle seulement 4 personnes existent maintenant. Les informations que nous connaissons à leur sujet sont leurs noms, leurs âges et s'ils sont des programmeurs Python. La façon naïve de stocker ces données consiste à utiliser des listes.

>>> person_names = ['Alice', 'Chris', 'Bob', 'Priyatham']
>>> person_ages = [42, 29, 42, 25]
>>> is_python_programmer = [False, True, False, True]

Alice et Bob sont les personnages inventés dans un article de recherche sur la cryptographie en 1978. Ils sont devenus très célèbres dans le domaine de la cryptographie et de l'informatique. Chris est le fondateur de Finxter et moi-même Priyatham.

Mais si vous observez, rien n'indique qu'il existe une relation entre les trois listes. En méditant davantage sur cette pensée, vous pourriez arriver au tour d'une liste de listes comme solution.

Composons toutes les informations des personnes individuelles dans des listes séparées. En les liant tous à nouveau dans une liste séparée, nous avons,

>>> Alice_info = ['Alice', 42, False]
>>> Chris_info = ['Chris', 29, True]
>>> Bob_info = ['Bob', 42, False]
>>> Priyatham_info = ['Priyatham', 25, True]
        
>>> planet_info = [Alice_info, Chris_info, Bob_info, Priyatham_info]
>>> planet_info
[['Alice', 42, False], ['Chris', 29, True], ['Bob', 42, False], ['Priyatham', 25, True]]

L'affectation de la liste ci-dessus peut être visualisée comme suit,

Vous pouvez voir que les listes individuelles internes sont stockées à différents endroits de la mémoire. Si nous voulons accéder à tous les noms des personnes qui se trouvent sur notre planète, nous devons parcourir toutes les listes. C'est un processus très coûteux car nous devons parcourir différents emplacements de mémoire.

Numpy Structured Array peut stocker et rendre les mêmes données accessibles très efficacement. Pour ce faire, il stocke le tableau complet au même emplacement mémoire dans un tableau contigu. Numpy utilise l'API du langage C dans les coulisses, ce qui le rend extrêmement rapide.

Comment construire et affecter des données à des tableaux structurés Numpy

Construisons d'abord des tableaux Numpy normaux comme les listes naïves et étudions-les.

>>> import numpy as np
>>> person_names_arr = np.array(person_names)
>>> person_ages_arr  = np.array(person_ages)
>>> is_python_prog_arr = np.array(is_python_programmer)
>>> person_names_arr
array(['Alice', 'Chris', 'Bob', 'Priyatham'], dtype='<U9')
>>> person_ages_arr
array([42, 29, 42, 25])
>>> is_python_prog_arr
array([False,  True, False,  True])

Les tableaux numpy sont principalement caractérisés par leurs types de données. Nous pouvons accéder au type de données en utilisant le dtype attribut de l'objet tableau Numpy.

>>> person_names_arr.dtype
dtype('<U9')
>>> person_ages_arr.dtype
dtype('int64')
>>> is_python_prog_arr.dtype
dtype('bool')

Vous pouvez voir ci-dessus que chaque tableau connaît ses informations de type explicites et n'a qu'un seul type.

Tableau structuré Numpy est créé à l'aide d'un type de données spécial (dtype ) appelé type de données structurées. Un type de données structurées peut avoir plusieurs types avec des noms qui leur sont respectivement attribués.

Créons un tableau structuré Numpy en utilisant un type de données structuré. Nous pouvons nous référer aux types ci-dessus pour créer des types de données dans le tableau.

>>> struct_arr = np.zeros(4, dtype = [('person_names', 'U9'), ('person_ages', 'i8'), ('is_python_programmer', 'bool')])
>>> struct_arr
array([('', 0, False), ('', 0, False), ('', 0, False), ('', 0, False)],
      dtype=[('person_names', '<U9'), ('person_ages', '<i8'), ('is_python_programmer', '?')])

Le tableau structuré vide créé ci-dessus peut être interprété et visualisé comme,

Nous pouvons utiliser les index de ligne ou de colonne pour affecter les informations de nos personnes au tableau structuré ci-dessus.

1. Affectation à l'aide d'index de colonne :

>>> struct_arr['person_names'] = person_names
>>> struct_arr['person_ages'] = person_ages
>>> struct_arr['is_python_programmer'] = is_python_programmer
>>> struct_arr
array([('Alice', 42, False), ('Chris', 29,  True), ('Bob', 42, False),
       ('Priyatham', 25,  True)],
      dtype=[('person_names', '<U9'), ('person_ages', '<i8'), ('is_python_programmer', '?')])

2. Affectation à l'aide des index de lignes :

>>> struct_arr[0] = tuple(Alice_info)
>>> struct_arr[1] = tuple(Chris_info)
>>> struct_arr[2] = tuple(Bob_info)
>>> struct_arr[3] = tuple(Priyatham_info)
>>> struct_arr
array([('Alice', 42, False), ('Chris', 29,  True), ('Bob', 42, False),
       ('Priyatham', 25,  True)],
      dtype=[('person_names', '<U9'), ('person_ages', '<i8'), ('is_python_programmer', '?')])

En suivant l'une des deux méthodes d'affectation, les tableaux structurés sont remplis de nos informations. Cela peut être interprété et visualisé comme,

Accès aux données et opérations sur les tableaux structurés

Nous pouvons maintenant accéder très efficacement à n'importe quel élément présent n'importe où dans le tableau. Nous bénéficions d'un avantage supplémentaire du type de données structurées ainsi que des fonctionnalités de tableau NumPy normales telles que les agrégations, la diffusion, etc. Les mêmes index de colonne et de ligne que nous avons utilisés pour attribuer des données peuvent être utilisés pour accéder aux éléments du tableau.

Pour obtenir tous les noms de toutes les personnes présentes sur notre planète,

>>> struct_arr['person_names']
array(['Alice', 'Chris', 'Bob', 'Priyatham'], dtype='<U9')

Pour obtenir les informations présentes dans les première et deuxième lignes du tableau,

>>> struct_arr[0]
('Alice', 42, False)
>>> struct_arr[1]
('Chris', 29, True)

Pour obtenir les mêmes informations ci-dessus, nous pouvons utiliser numpy.where( ) fonction. Pour ce faire, nous devons connaître exactement le nom de la personne dont nous voulons récupérer les informations. Cela utilise le masquage booléen NumPy en interne.

>>> struct_arr[np.where(struct_arr['person_names'] == 'Alice')]
array([('Alice', 42, False)],
      dtype=[('person_names', '<U9'), ('person_ages', '<i8'), ('is_python_programmer', '?')])
>>> struct_arr[np.where(struct_arr['person_names'] == 'Chris')]
array([('Chris', 29,  True)],
      dtype=[('person_names', '<U9'), ('person_ages', '<i8'), ('is_python_programmer', '?')])

Afin d'obtenir les noms des 2 dernières personnes, le découpage d'index négatif de Python ainsi que la sélection de Structured Array peuvent être utilisés.

>>> struct_arr[-2:]['person_names']
array(['Bob', 'Priyatham'], dtype='<U9')

Pour obtenir les noms des programmeurs Python sur notre planète, nous utilisons à nouveau le masquage booléen,

>>> struct_arr[struct_arr['is_python_programmer']]['person_names']
array(['Chris', 'Priyatham'], dtype='<U9')

Nous pouvons voir d'en haut que les programmeurs python sont moins âgés que les autres sur notre planète. Alors, obtenons l'âge maximum des programmeurs Python et l'âge minimum des programmeurs non-python. Ensuite, nous pouvons obtenir un âge moyen à l'aide duquel nous pouvons commenter l'évolution du langage de programmation Python sur notre planète.

>>> python_prog_max_age = np.max(struct_arr[struct_arr['is_python_programmer']]['person_ages'])
>>> non_python_prog_min_age = np.min(struct_arr[struct_arr['is_python_programmer'] == False]['person_ages'])
>>> python_prog_max_age
29
>>> non_python_prog_min_age
42
>>> separation_age = int((python_prog_max_age + non_python_prog_min_age)/2)
>>> separation_age
35

Disons qu'il y a d'autres personnes dont nous ignorons l'existence sur notre planète. Mais sur la base des données dont nous disposons, avant 35 ans, aucun ou très peu de programmeurs python n'existaient sur notre planète. Le langage de programmation Python est récemment devenu populaire parmi les jeunes.

Si vous souhaitez effectuer des opérations plus délicates et compliquées sur ces données, envisagez de passer au package Pandas.

Types de données structurées – Tableaux structurés

Jetez un œil aux chaînes de type Array-protocol (‘U9’, ‘i8’, ‘?’) dans le tableau structuré ci-dessus. Le premier caractère fait référence au type de données et le suivant spécifie le nombre d'octets pour chaque élément de ce type. Unicode ('U9') et booléen ('?') sont des exceptions. Dans le type de chaîne Unicode, le nombre suivant spécifie le nombre de caractères maximum mais pas d'octets. Les valeurs booléennes (vrai et faux) sont les résultats possibles des questions oui/non. Comme c'est une question, les développeurs principaux de Numpy ont peut-être donné '?' comme chaîne de type pour les valeurs booléennes (juste ma pensée).

Toutes les chaînes de type possibles utilisées pour créer des tableaux NumPy comme fourni par la documentation sont ;

Personnage Description Exemple
‘?’ Booléen np.dtype(‘ ?’)
‘b’ Octet signé np.dtype(‘b’)
‘B’ Octet non signé np.dtype(‘B’)
‘je’ Entier signé np.dtype(‘i8’)
‘u’ Entier non signé np.dtype(‘u4’)
‘f’ Point flottant np.dtype(‘f2’)
‘c’ Flottant flottant complexe np.dtype(‘c16’)
‘m’ Timedelta np.dtype(‘m8’)
‘M’ Dateheure np.dtype(‘M’)
‘O’ Objets Python np.dtype(‘O’)
‘S’, ‘a’ Chaîne (terminée par zéro) np.dtype(‘S5’)
« O » Chaîne Unicode np.dtype(‘U’)
‘V’ Données brutes (vide) np.dtype(‘V’)

Pour d'autres façons de construire des objets de type de données au lieu de chaînes de type de protocole Array, veuillez vous référer à ce lien de documentation.

Trois principales façons de créer des types de données structurés

Numpy fournit un numpy.dtype fonction pour créer des objets de type de données. Nous pouvons nous référer aux types ci-dessus pour créer des types de données. Il existe 2 manières principales de créer des types de données structurées ;

1. Utiliser le dictionnaire avec des noms et des formats comme clés (titres)

>>> dt_dict = np.dtype({'names':('person_names', 'person_ages', 'is_python_programmer'),
...                     'formats': ('U9', 'i8', 'bool')})
>>> dt_dict
dtype([('person_names', '<U9'), ('person_ages', '<i8'), ('is_python_programmer', '?')])

La valeur de la clé names est un tuple d'index de colonne que nous utilisons dans Structured Array. La valeur de la clé de formats est un tuple de chaînes de type pour les colonnes respectivement.

>>> dt_dict.names
('person_names', 'person_ages', 'is_python_programmer')
>>> dt_dict.fields
mappingproxy({'person_names': (dtype('<U9'), 0),               'person_ages': (dtype('int64'), 36),               'is_python_programmer': (dtype('bool'), 44)})
>>> dt_dict.itemsize
45 >>> struct_arr.itemsize 45

Un élément de notre tableau structuré est l'information sur une seule personne sur notre planète. La mémoire allouée pour un seul élément est de 45 octets, comme indiqué dans l'attribut itemsize.

Si vous observez le résultat de dt_dict.fields, vous pouvez voir l'allocation de mémoire en octets et la distribution de mémoire sur les index. Nous savons que la chaîne de type ‘

Toute cette explication peut être visualisée à l'aide de la figure ci-dessous.

2. Utiliser la liste des tuples

>>> dt_tupl = np.dtype([('person_names', '<U9'), ('person_ages', '<i8'), ('is_python_programmer', 'bool')])
>>> dt_tupl
dtype([('person_names', '<U9'), ('person_ages', '<i8'), ('is_python_programmmer', '?')])
>>> dt_tupl.names
('person_names', 'person_ages', 'is_python_programmer')

Dans cette méthode, un type de données structurées est créé à l'aide d'une liste de tuples. Chaque tuple se compose d'un nom d'index et de son type.

Le résultat de dt_tupl.names conclut que les noms d'index seront créés automatiquement à partir des tuples.

3. Utilisation d'une chaîne de types séparés par des virgules

>>> dt_str = np.dtype('U9, i8, bool')
>>> dt_str
dtype([('f0', '<U9'), ('f1', '<i8'), ('f2', '?')])
>>> dt_str.names
('f0', 'f1', 'f2')

Lorsque nous ne nous soucions pas des noms de champs, nous pouvons utiliser ce type de type de données structurées. Il attribue automatiquement des noms de champs 'f0', 'f1', 'f2'…. en fonction du nombre de types présents.

Enregistrement des tableaux

Les tableaux d'enregistrement sont essentiellement des tableaux structurés avec une fonctionnalité supplémentaire. L'accès au champ d'index nommé en tant qu'attribut avec les clés de dictionnaire est fourni.

>>> rec_arr = np.rec.array(struct_arr)
>>> rec_arr['person_names']
array(['Alice', 'Chris', 'Bob', 'Priyatham'], dtype='<U9')
>>> rec_arr.person_names
array(['Alice', 'Chris', 'Bob', 'Priyatham'], dtype='<U9')
>>> rec_arr is struct_arr
False
>>> rec_arr == struct_arr
rec.array([ True,  True,  True,  True],
          dtype=bool)

Le moyen le plus simple de créer des tableaux d'enregistrements consiste à utiliser numpy.rec.array( ) fonction. Le champ person_names est accessible en tant qu'attribut avec l'index basé sur la clé du dictionnaire ci-dessus. Record Array prend le Structured Array et crée un autre objet différent à partir du Structured Array. Le résultat de rec_arr == struct_arr prouve que les deux ont les mêmes valeurs avec sa fonctionnalité supplémentaire.

L'inconvénient du tableau d'enregistrement est qu'il est plus lent que le tableau structuré en raison de sa fonctionnalité supplémentaire.

Étapes suivantes :passer aux Pandas

Les tableaux structurés sont l'effort des développeurs NumPy pour avoir une capacité à domicile pour traiter les données structurées. Mais, lorsqu'il s'agit de données structurées sous forme de tableaux, un monde d'opérations supplémentaires est possible. Pandas est un outil très mature pour gérer toutes ces opérations. Veuillez envisager un saut vers le package Pandas si vous avez affaire à de telles données structurées décrites dans l'article.


Post précédent