
Cet article décrit comment aplatir une liste de listes (liste imbriquée) en Python. Vous pouvez utiliser itertools.chain.from_iterable(), sum() et les compréhensions de liste.
- Aplatir une liste de listes (liste 2D)
- Aplatir la liste avec itertools.chain.from_iterable()
 - Aplatir la liste avec sum()
 - Aplatir la liste avec les compréhensions de liste
 - Comparaison de vitesse
 
 - Aplatir les listes 3D et plus multidimensionnelles et les listes irrégulières
 
Utilisez ravel() ou flatten() pour aplatir un tableau NumPy ndarray.
Au contraire, consultez l’article suivant sur la façon de convertir un ndarray unidimensionnel ou une liste en deux dimensions.
Aplatir une liste de listes (liste 2D)
Aplatir la liste avec itertools.chain.from_iterable()
Vous pouvez aplatir une liste de listes avec itertools.chain.from_iterable().
import itertools
l_2d = [[0, 1], [2, 3]]
print(list(itertools.chain.from_iterable(l_2d)))
# [0, 1, 2, 3]
itertools.chain.from_iterable() renvoie un itérateur, donc si vous voulez le convertir en liste, utilisez list(). Il n’est pas nécessaire de faire une liste lors de son utilisation dans une instruction for.
Un tuple de tuples peut être manipulé de la même manière. Dans l’exemple suivant, le résultat est converti en tuple avec tuple(). Si vous avez besoin d’une liste, utilisez list().
t_2d = ((0, 1), (2, 3))
print(tuple(itertools.chain.from_iterable(t_2d)))
# (0, 1, 2, 3)
Seules les listes 2D peuvent être aplaties avec itertools.chain.from_iterable(). Dans le cas de listes 3D ou plus multidimensionnelles, le résultat est le suivant.
l_3d = [[[0, 1], [2, 3]], [[4, 5], [6, 7]]]
print(list(itertools.chain.from_iterable(l_3d)))
# [[0, 1], [2, 3], [4, 5], [6, 7]]
Une erreur est levée si elle contient des objets non itérables tels que int ou float.
l_mix = [[0, 1], [2, 3], 4]
# print(list(itertools.chain.from_iterable(l_mix)))
# TypeError: 'int' object is not iterable
Les cas avec des listes 3D ou plus multidimensionnelles et des listes irrégulières sont décrits plus loin.
Aplatir la liste avec sum()
Vous pouvez également aplatir une liste de listes avec la fonction intégrée sum().
Une valeur initiale peut être spécifiée comme second argument de sum(). Si vous passez la liste vide [], l’opération + de la liste concaténera les listes.
Notez que la valeur par défaut du deuxième argument est 0, donc s’il est omis, une erreur est générée en raison de l’opération + avec int et list.
print(sum(l_2d, []))
# [0, 1, 2, 3]
# print(sum(l_2d))
# TypeError: unsupported operand type(s) for +: 'int' and 'list'
Les tuples peuvent être manipulés de la même manière.
print(sum(t_2d, ()))
# (0, 1, 2, 3)
Comme itertools.chain.from_iterable(), cela ne fonctionne pas pour plus de listes 3D ou irrégulières.
print(sum(l_3d, []))
# [[0, 1], [2, 3], [4, 5], [6, 7]]
# print(sum(l_mix, []))
# TypeError: can only concatenate list (not "int") to list
Aplatir la liste avec les compréhensions de liste
Vous pouvez également aplatir une liste de listes avec des compréhensions de liste imbriquées.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
L’exemple ci-dessus est équivalent à la boucle for imbriquée suivante.
flat = []
for row in matrix:
    for x in row:
        flat.append(x)
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Dans le cas de la compréhension de liste ci-dessus, comme pour les autres méthodes, un seul niveau peut être aplati et une erreur est générée si des objets non itérables sont inclus.
Il est également possible de rendre l’imbrication plus profonde pour supporter plus de trois dimensions, ou de faire des branchements conditionnels selon le type d’élément, mais ce n’est pas recommandé car ce serait trop compliqué.
Consultez l’article suivant pour plus d’informations sur les compréhensions de liste.
Comparaison de vitesse
Notez que bien que sum() soit facile à utiliser, il est beaucoup plus lent que itertools.chain.from_iterable() ou que les compréhensions de liste lorsque le nombre de lignes (le nombre de listes internes) est grand. Il est préférable de ne pas utiliser sum() lorsque le nombre de lignes est important et que la vitesse de traitement et l’efficacité de la mémoire sont importantes.
Bien que vous deviez importer itertools, itertools.chain.from_iterable() est plus rapide que les compréhensions de liste.
Le code suivant est mesuré à l’aide de la commande magique %%timeit sur Jupyter Notebook. Notez que cela ne fonctionne pas sur le script Python.
5 lignes :
l_2d_5 = [[0, 1, 2] for i in range(5)]
print(l_2d_5)
# [[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]
%%timeit
list(itertools.chain.from_iterable(l_2d_5))
# 537 ns ± 4.59 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%%timeit
sum(l_2d_5, [])
# 319 ns ± 1.85 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%%timeit
[x for row in l_2d_5 for x in row]
# 764 ns ± 32.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
100 lignes :
l_2d_100 = [[0, 1, 2] for i in range(100)]
%%timeit
list(itertools.chain.from_iterable(l_2d_100))
# 6.94 µs ± 139 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%%timeit
sum(l_2d_100, [])
# 35.5 µs ± 1.2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit
[x for row in l_2d_100 for x in row]
# 13.5 µs ± 959 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
10000 lignes :
l_2d_10000 = [[0, 1, 2] for i in range(10000)]
%%timeit
list(itertools.chain.from_iterable(l_2d_10000))
# 552 µs ± 79.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
sum(l_2d_10000, [])
# 343 ms ± 2.19 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%%timeit
[x for row in l_2d_10000 for x in row]
# 1.11 ms ± 110 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Aplatir les listes 3D et plus multidimensionnelles et les listes irrégulières
Il est nécessaire de définir une nouvelle fonction pour aplatir des listes 3D et plus multidimensionnelles ou des listes irrégulières.
L’exemple de code suivant est basé sur l’article suivant.
import collections
def flatten(l):
    for el in l:
        if isinstance(el, collections.abc.Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el
Le type d’élément el est vérifié par isinstance() et traité de manière récursive.
Déterminez si el est itérable par collections.abc.Iterable. Vous devez importer les collections de bibliothèque standard.
La chaîne str et la chaîne d’octets bytes sont également itérables, elles sont donc exclues. S’il n’est pas exclu, il sera séparé pour chaque caractère.
Cette fonction peut être utilisée dans tous les cas.
print(list(flatten(l_2d)))
# [0, 1, 2, 3]
print(list(flatten(l_3d)))
# [0, 1, 2, 3, 4, 5, 6, 7]
print(list(flatten(l_mix)))
# [0, 1, 2, 3, 4]
Peu importe si divers objets itérables tels que des listes, des tuples et des plages sont inclus.
l_t_r_mix = [[0, 1], (2, 3), 4, range(5, 8)]
print(list(flatten(l_t_r_mix)))
# [0, 1, 2, 3, 4, 5, 6, 7]
Si vous souhaitez uniquement gérer la liste, vous n’avez pas besoin d’importer des collections. Les tuples et la plage ne sont pas aplatis, mais cela suffira dans la plupart des cas.
def flatten_list(l):
    for el in l:
        if isinstance(el, list):
            yield from flatten_list(el)
        else:
            yield el
print(list(flatten_list(l_2d)))
# [0, 1, 2, 3]
print(list(flatten_list(l_3d)))
# [0, 1, 2, 3, 4, 5, 6, 7]
print(list(flatten_list(l_mix)))
# [0, 1, 2, 3, 4]
print(list(flatten_list(l_t_r_mix)))
# [0, 1, (2, 3), 4, range(5, 8)]
Vous pouvez spécifier plusieurs types par tuple dans le deuxième argument de isinstance().
def flatten_list_tuple_range(l):
    for el in l:
        if isinstance(el, (list, tuple, range)):
            yield from flatten_list_tuple_range(el)
        else:
            yield el
print(list(flatten_list_tuple_range(l_t_r_mix)))
# [0, 1, 2, 3, 4, 5, 6, 7]
