2 Types de données intégrés

Il est idispensable de comprendre quelques concepts de base sur les types de données Python. Contrairement aux langages spécifiques au domaine tels que MATLAB ou R, où le type de données par défaut a été choisi pour l'aspect numérique, Python est un langage de programmation général qui est également bien adapté à l'analyse de données, l'économétrie et la statistique. Par exemple, le type numérique de base de MATLAB est un tableau (utilisant la double précision, ce qui est utile pour les mathématiques en virgule flottante), alors que le type numérique de base de Python est un scalaire unidimensionnel qui peut être soit un tableau de type entier flottant de double précision, selon le formatage du nombre à l'entrée.

2.1 Noms de variables

Les noms de variables peuvent prendre de nombreuses formes, bien qu’ils ne puissent contenir que des chiffres, ils peuvent contenir des lettres (majuscules et minuscules) et des traits de soulignement (_). Ils doivent commencer par une lettre ou un trait de soulignement. De plus, certains mots sont réservés à Python et ne peuvent donc pas être utilisés pour les noms de variables (par exemple, import ou for). Par exemple,

In [ ]:
x = 1.0
X = 1.0
X1 = 1.0
x1 = 1.0
dell = 1.0
dellreturns = 1.0
dellReturns = 1.0
_x = 1.0
x_ = 1.0

sont tous des noms de variables légaux distincts. Notez que les noms qui commencent ou se terminent par un trait de soulignement, bien que légaux, ne sont pas utilisés normalement car, par convention, ils ont une signification particulière.

In [ ]:
# Non autorisés 
x: = 1.0
1X = 1
X-1 = 1
for = 1

Plusieurs variables peuvent être affectées sur la même ligne à l'aide de virgules,

In [ ]:
x, y, z = 1, 3.1415, 'a'

2.2 Types de données natifs de base

2.2.1 Numérique

Les nombres simples en Python peuvent être des entiers, des flottants ou des complexes. Ce chapitre ne couvre pas tous les types de données Python mais se concentre sur ceux qui sont les plus pertinents pour l'analyse numérique, l'économétrie et la statistique. Les types de données byte, bytearray et memoryview ne sont pas décrits.

2.2.1.1 Le type flottant

Le type de données le plus important (scalaire) pour l'analyse numérique est le type float. Malheureusement, tous les types de données numériques non complexes ne sont pas des flottants. Pour entrer un type de données float, il est nécessaire d’inclure un.(point) dans l'expression. Cet exemple utilise la fonction type() pour déterminer le type de données d'une variable.

In [ ]:
>>> x = 1
>>> type(x)
int
>>> x = 1.0
>>> type(x)
float
>>> x = float(1)
>>> type(x)
float

2.2.1.2 Nombre complèxe

Les nombres complexes sont créés en Python en utilisant j ou la fonction complex()

In [ ]:
>>> x = 1.0
>>> type(x)
float
>>> x = 1j
>>> type(x)
complex
>>> x = 2 + 3j
>>> x
(2+3j)
>>> x = complex(1)
>>> x
(1+0j)

Noter que a + bj est identique à complex(a, b), alors que complex(a) est identique à a + 0j.

2.2.1.3 Entiers : Integers (int)

Les entiers de base peuvent être entrés soit en excluant la décimale (voir le type float), soit explicitement en utilisant la fonction int(). La fonction int() peut également être utilisée pour convertir un float en entier en arrondissant à 0.

In [ ]:
>>> x = 1
>>> type(x)
int
>>> x = 1.0
>>> type(x)
float
>>> x = int(x)
>>> type(x)
int

La prise en charge (en mémoire) des nombres entiers Python a une plage illimitée car le nombre de bits utilisé pour stocker un nombre entier est dynamique.

In [ ]:
>>> x = 1
>>> x
1
>>> type(x)
int
>>> x = 2 ** 127 + 2 ** 65
>>> x
# ** denotes exponentiation, y^64 in TeX
170141183460469231768580791863303208960

2.2.2 Booléen (bool)

Le type de données booléen est utilisé pour représenter vrai et faux, à l'aide des mots clés réservés True et False. Les variables booléennes sont importantes pour le contrôle du flux de programme et sont généralement générées à la suite d'opérations logiques, bien qu'elles puissent être entrées directement.

In [ ]:
>>> x = True
>>> type(x)
bool
>>> x = bool(1)
>>> x
True
>>> x = bool(0)
>>> x
False

Les valeurs non nulles et non vides sont généralement considérées comme vraies lorsqu'elles sont évaluées par bool(). Les valeurs nulles ou vides telles que bool(0), bool(0.0), bool(0.0j), bool(None), bool('') et bool([]) sont toutes fausses.

2.2.3 Chaînes de caractères : Strings (str)

Les chaînes de caractères sont utilisées en traitement de fichiers de données, en particulier lors de l'importation ou du formatage d'une sortie. Les chaînes de caractères sont délimitées par des guillemets simples ('') ou des doubles guillemets (""), mais pas par la combinaison des deux délimiteurs.

In [ ]:
>>> x = 'abc'
>>> type(x)
str
>>> y = '"A quotation!"'
>>> print(y)
"A quotation!"

2.2.3.1 Manipulation des chaînes de caractères

Les sous-chaînes d'une chaîne sont accessibles via l'opération dite de slicing (découpage en tranches !!). Le découpage en tranches utilise [] pour contenir les indices des caractères d'une chaîne, où le premier indice est 0 et le dernier est n-1 (en supposant que la chaîne de caractères a n lettres). le Le tableau suivant décrit les types de tranches disponibles. Les plus utiles sont s[i], qui retournera le caractère en position i, s[: i], qui renvoie les premiers caractères des positions 0 à i-1, et s[i:] qui renvoie les derniers caractères des positions i à n-1. Le tableau ci-dessous fournit une liste des types de slicing pouvant être utilisés. La deuxième colonne montre que le slicing peut utiliser des indices négatifs qui indexent essentiellement la chaîne en arrière.

Slice Signification
s[:] La chaîne de caractères en entier
s[i] Le caractère à la position i
s[i:] Caractères allant de i à n-1
s[:i] Caractères allant de 0 à i-1
s[i:j] Caractères allant de i à j-1
s[i:j:m] Caractères allant de i à j-1par un pas de m
s[−i] Caractère à la position n-i
s[−i:] Caractères allant de n-i à n-1
s[:−i] Caractères allant de 0 à n-i-1
s[−j:−i] Caractères allant de n-j à n-i-1 avec -j<-i
s[−j:−i:m] Caractères allant de n-j à n-i-1 par un pas de m
In [ ]:
>>> text = 'Python strings are sliceable.'
>>> text[0]
'P'
>>> text[10]
'i'
>>> L = len(text)
>>> text[L] # Error
IndexError: string index out of range
>>> text[L-1]
'.'
>>> text[:10]
'Python str'
>>> text[10:]
'ings are sliceable.'

2.2.4 Les listes

Les listes sont un type de données conteneur intégré qui contient d'autres données. Une liste est une collection d'autres objets : flottants, entiers, nombres complexes, chaînes de caractères ou même d'autres listes. Les listes sont essentielles à la programmation Python et sont utilisées pour stocker des collections d’autres valeurs. Par exemple, une liste de flottants peut être utilisée pour représenter un vecteur (bien que les types array et matrix de NumPy soient mieux adaptés). Les listes prennent également en charge le slicing pour récupérer un ou plusieurs éléments. Les listes de base sont construites à l'aide d'accolades carrées, [], et les valeurs sont séparées par des virgules.

In [ ]:
>>> x = []
>>> type(x)
builtins.list
>>> x=[1,2,3,4]
>>> x
[1,2,3,4]
# 2-dimensional list (list of lists)
>>> x = [[1,2,3,4], [5,6,7,8]]
>>> x
[[1, 2, 3, 4], [5, 6, 7, 8]]
# Jagged list, not rectangular
>>> x = [[1,2,3,4] , [5,6,7]]
>>> x
[[1, 2, 3, 4], [5, 6, 7]]
# Mixed data types
>>> x = [1,1.0,1+0j,'one',None,True]
>>> x
[1, 1.0, (1+0j), 'one', None, True]

Ces exemples montrent que les listes peuvent être régulières, imbriquées et peuvent contenir n’importe quel mélange de types de données, y compris d’autres listes.

2.2.4.1 Manipuler les listes

Les listes, comme les chaînes de caractères, peuvent être manipulées. L'opération de slicing est similaire, bien que les listes puissent être manipulées plus que les chaînes de caractères. Les listes peuvent être multidimensionnelles alors que les chaînes de caractères sont toujours de dimension $1 \times n$. L'opération de slicing de listes de base est identique à celui des chaînes de caractères, et des opérations telles que x[:], x[1:], x[:1] et x[-3:] peuvent toutes être utilisées.

La manipulation d'une liste par défaut utilise une unité de pas (taille d'un pas). Il est possible d’utiliser d’autres pas en utilisant un troisième entrée dans l'instruction x[i:j:m]i est l'indice de début, j est l'indice à fin (exclusif) et m est la longueur du pas. Par exemple, x[::2] sélectionnera un élément sur deux d’une liste et est équivalent à x[0:n:2]n=len(x). Le pas peut également être négatif, ce qui peut être utilisé pour sélectionner éléments d'une liste dans l'ordre inverse. Par exemple, x[::-1] inversera une liste et revient à x[0:n:-1]. Des exemples d'accès à des éléments de listes à une dimension sont présentés ici:

In [ ]:
>>> x = [0,1,2,3,4,5,6,7,8,9]
>>> x[0]
0
>>> x[5]
5
>>> x[10] # Error
IndexError: list index out of range
>>> x[4:]
[4, 5, 6, 7, 8, 9]
>>> x[:4]
[0, 1, 2, 3]
>>> x[1:4]
[1, 2, 3]
>>> x[-0]
0
>>> x[-1]
9
>>> x[-10:-1]
[0, 1, 2, 3, 4, 5, 6, 7, 8]

La liste peut être multidimensionnelle et le slicing peut être effectué directement dans des dimensions supérieures. Pour simplifier, soit une liste en 2 dimensions x=[[1,2,3,4], [5,6,7,8]]. Si l'indexation simple est utilisée, x[0] renverra la première liste (interne) et x[1] renverra la deuxième liste (interne). La liste renvoyée par x[0] pouvant être manipulées à son tour, la liste interne peut être manipulée directement à l’aide de x[0][0] ou x[0][1:4].

In [ ]:
>>> x = [[1,2,3,4], [5,6,7,8]]
>>> x[0]
[1, 2, 3, 4]
>>> x[1]
[5, 6, 7, 8]
>>> x[0][0]
1
>>> x[0][1:4]
[2, 3, 4]
>>> x[1][-4:-1]
[5, 6, 7]

2.2.4.2 Fonctions associées à un objet list

Un certain nombre de fonctions sont disponibles pour manipuler des listes. Les plus utiles sont

In [ ]:
>>> x = [0,1,2,3,4,5,6,7,8,9]
>>> x.append(0)
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> len(x)
11
>>> x.extend([11,12,13])
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 11, 12, 13]
>>> x.pop(1)
1
>>> x
[0, 2, 3, 4, 5, 6, 7, 8, 9, 0, 11, 12, 13]
>>> x.remove(0)
>>> x
[2, 3, 4, 5, 6, 7, 8, 9, 0, 11, 12, 13]

Les éléments peuvent également être supprimés des listes en utilisant le mot-clé del en combinaison avec un slice.

In [ ]:
>>> x = [0,1,2,3,4,5,6,7,8,9]
>>> del x[0]
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x[:3]
[1, 2, 3]
>>> del x[:3]
>>> x
[4, 5, 6, 7, 8, 9]
>>> del x[1:3]
>>> x
[4, 7, 8, 9]
>>> del x[:]
>>> x
[]

2.2.5 Tuples (tuple) ou n-uplet

Un tuple est pratiquement identique à une liste avec une différence importante: les tuples sont immuables. L'immuabilité signifie qu'un tuple ne peut pas être changé une fois créé. Il n'est pas possible d'ajouter, de supprimer ou de remplacer des éléments dans un tuple. Toutefois, si un tuple contient un type de données mutable, par exemple un tuple contenant une liste, le contenu pouvant être modifié.

Les tuples sont construits en utilisant des parenthèses (()) à la place des crochets ([]) utilisés pour créer des listes. Les tuples peuvent être manipulés de la même manière que les listes. Une liste peut être convertie en un tuple à l'aide de tuple() (De même, un tuple peut être converti en liste à l'aide de list()).

In [ ]:
>>> x =(0,1,2,3,4,5,6,7,8,9)
>>> type(x)
tuple
>>> x[0]
0
>>> x[-10:-5]
(0, 1, 2, 3, 4)
>>> x = list(x)
>>> type(x)
list
>>> x = tuple(x)
>>> type(x)
tuple
>>> x= ([1,2],[3,4])
>>> x[0][1] = -10
>>> x # Contents can change, elements cannot
([1, -10], [3, 4])

Noter que les tuples contenant un seul élément doivent contenir une virgule lors de leur création, de sorte que l'instruction x = (2,) revient assigner un tuple à x, alors que x=(2) assignera 2 à x. Ce dernier interprète les parenthèses comme si elles faisaient partie d'une formule mathématique plutôt que d'être utilisées pour construire un tuple. x = tuple([2]) peut également être utilisé pour créer un tuple à un seul élément. Les listes n'ont pas ce problème puisque les crochets n'ont pas cette ambiguïté.

In [ ]:
>>> x =(2)
>>> type(x)
int
>>> x = (2,)
>>> type(x)
tuple
>>> x = tuple([2])
>>> type(x)
tuple

2.2.5.1 Fonctions aux méthodes associées aux tuples

Les tuples sont immuables et n'ont donc que les méthodes index et count, qui fonctionnent de la même manière que le type list.

2.2.6 Le type dictionary (dict)

Les dictionnaires sont moins fréquemment utilisés que les autres types de données décrits précédemment. Cependant, ils sont couramment utilisés pour transmettre des options à d'autres fonctions telles que les optimiseurs (choix d'hyperparamètres par validation croisée). Il est donc important de connaître les dictionnaires. Les dictionnaires en Python sont composés de clés (mots) et valeurs (définitions). Les clés de dictionnaires doivent être des types de données immuables uniques (par exemple, une chaîne de caractères, la clé la plus courante, des entiers ou des tuples contenant des types immuables), et les valeurs peuvent contenir tout type de données Python valide. L'accès aux valeurs se fait à l'aide des clés.

In [ ]:
>>> data = {'age': 34, 'children' : [1,2], 1: 'apple'}
>>> type(data)
dict
>>> data['age']
34

Les valeurs associées à une clé existante peuvent être mises à jour en attribuant une affectation à la clé dans le dictionnaire.

In [ ]:
>>> data['age'] = 'xyz'
>>> data['age']
'xyz'

De nouvelles paires clé-valeur peuvent être ajoutées en définissant une nouvelle clé et en lui attribuant une valeur.

In [ ]:
>>> data['name'] = 'abc'
>>> data
{1: 'apple', 'age': 'xyz', 'children': [1, 2], 'name': 'abc'}

Les paires clé-valeur peuvent être supprimées à l'aide du mot clé réservé del.

In [ ]:
>>> del data['age']
>>> data
{1: 'apple', 'children': [1, 2], 'name': 'abc'}

2.2.7 Le type Set (set, frozenset)

Un set est une collection d'éléments uniques. set et frozenset ne diffèrent que par le fait que ce dernier est immuable (et donc plus performant en temps de manipulation), donc un set est similaire à une liste, tandis que frozenset est similaire à un tuple. Bien que les sets ne soient souvent utilisés, ils peuvent s'avérer très utiles lorsqu'on travaille sur des chaînes de caractères de différentes tailles.

2.2.7.1 Les méthodes (ou fonctions) associées au type set

In [ ]:
>>> x = set(['MSFT','GOOG','AAPL','HPQ','MSFT'])
>>> x
{'AAPL', 'GOOG', 'HPQ', 'MSFT'}
>>> x.add('CSCO')
>>> x
{'AAPL', 'CSCO', 'GOOG', 'HPQ', 'MSFT'}
>>> y = set(['XOM', 'GOOG'])
>>> x.intersection(y)
{'GOOG'}
>>> x.difference(y)
{'AAPL', 'CSCO', 'HPQ', 'MSFT'}
>>> x = x.union(y)
>>> x
{'AAPL', 'CSCO', 'GOOG', 'HPQ', 'MSFT', 'XOM'}
>>> x.remove('XOM')
{'AAPL', 'CSCO', 'GOOG', 'HPQ', 'MSFT'}

Le type frozenset supporte les mêmes méthodes sauf add et remove.

2.2.8 Les séquences : le type range (xrange dans Python 2.x)

Le type range est souvent rencontré dans une boucle for. range(a, b, i) crée la séquence $a, a + i, a + 2i,\ldots , a + (m - 1)i$ où $m=\lceil\frac{b - a}{i}\rceil$. En d'autres termes, il trouve tous les entiers $x$ commençant par $a$ tel que $a \le x \le b$ et où deux valeurs consécutives sont séparées par $i$. range peut être appelé avec un ou deux paramètres range(a,b) est identique à range(a,b,1) et range(b) est identique à range(0,b,1).

In [ ]:
>>> x = range(10)
>>> type(x)
range

>>> print(x)
range(0, 10)

>>> list(x)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> x = range(3,10)
>>> list(x)
[3, 4, 5, 6, 7, 8, 9]

>>> x = range(3,10,3)
>>> list(x)
[3, 6, 9]

Le type range n'est pas techniquement une liste, c'est pourquoi l'instruction print(x) affiche range(0,10). La conversion explicite avec la liste produit une liste qui permet d’afficher les valeurs. Techniquement, range est un itérateur qui ne nécessite pas réellement l’espace de stockage d’une liste. Noter que si on utilise Python 2.7, range et xrange sont tous les deux présents. xrange est l'itérateur de boucle de méthode préféré car il utilise plus efficacement la mémoire, en particulier lors d'une itération sur une large plage de valeurs.

2.3 Python et la gestion de la mémoire

Python utilise un système d’allocation de mémoire hautement optimisé pour éviter l’allocation de mémoire inutile. Par conséquent, lorsqu’une variable est affectée à une autre (par exemple, y = x), celles-ci désignent les mêmes données dans la mémoire de l’ordinateur. Pour vérifier cela, id() peut être utilisé pour déterminer le numéro d'identification unique d'une donnée.

In [ ]:
>>> x = 1
>>> y = x
>>> id(x)
94370604417824

>>> id(y)
94370604417824

x = 2.0
>>> id(x)
140637454240944

>>> id(y)
94370604417824

Sur cet exemple, l'affectation initiale y=x a généré deux variables avec le même ID. Cependant, une fois que x a été modifié, son identifiant a changé, mais pas l'identifiant de y, indiquant que les données de chaque variable ont été stockées dans des emplacements différents. Ce comportement est à la fois sûr et efficace et est commun aux types de base de Python: int, float, complexe, string, tuple, frozenset et range.

2.3.1 Exemple des listes

Les listes étant modifiables, les affectations ne créent pas de copie et les modifications apportées à l'une ou l'autre des variables affectent les deux.

In [ ]:
>>> x = [1, 2, 3]
>>> y = x
>>> y[0] = -10
>>> y
[-10, 2, 3]
>>> x
[-10, 2, 3]

L'opération de slicing d'une liste crée une copie de la liste et de tous les types immuables de la liste.

In [ ]:
>>> x = [1, 2, 3]
>>> y = x[:]

>>> id(x)

>>> id(y)

>>> x=[[0,1],[2,3]]
>>> y = x[:]

>>> y
[[0, 1], [2, 3]]

>>> id(x[0])

>>> id(y[0])

>>> x[0][0]

>>> id(x[0][0])

>>> id(y[0][0])

>>> y[0][0] = -10.0

>>> y
[[-10.0, 1], [2, 3]]

>>> x
[[-10.0, 1], [2, 3]]

On remarque que l'opération de slicing attribue un nouvel ID mais les éléments internes restent liés. Pour copier les listes imbriquées, le module copy est indispensable.

In [ ]:
>>> import copy as cp
>>> x=[[0,1],[2,3]]
>>> y = cp.deepcopy(x)
>>> y[0][0] = -10.0
>>> y
[[-10.0, 1], [2, 3]]
>>> x
[[0, 1], [2, 3]]

Un peu d'entraînement

Initialiser une liste contenant 4, 3.1415, 1.0, 2+4j, 'Hello', 'World'.

  • Supprimer 1.0 si sa position est connue. Si sa position est inconnue, que doit-on faire ?

  • Comment la liste [1.0, 2 + 4j, 'Hello'] peut-elle être ajoutée à la liste existante ?

  • Comment peut-on inverser la liste ?

  • Dans la liste étendue, comment peut-on compter l'occurrence de 'Hello' ?