J'ai une liste de longueur arbitraire, et je dois la diviser en morceaux de taille égale et l'utiliser. Il existe des moyens évidents de le faire, comme garder un compteur et deux listes, et lorsque la deuxième liste se remplit, ajoutez-la à la première liste et videz la deuxième liste pour la prochaine série de données, mais cela est potentiellement extrêmement coûteux.
Je me demandais si quelqu'un avait une bonne solution à cela pour des listes de n'importe quelle longueur, par exemple en utilisant des générateurs.
Je cherchais quelque chose d'utile itertools
mais je n'ai rien trouvé d'utile. Ça aurait peut-être manqué ça.
Question connexe: Quelle est la façon la plus «pythonique» d'itérer une liste en morceaux?
Réponses:
Voici un générateur qui produit les morceaux que vous souhaitez:
Si vous utilisez Python 2, vous devez utiliser à la
xrange()
place derange()
:Vous pouvez également simplement utiliser la compréhension de liste au lieu d'écrire une fonction, bien que ce soit une bonne idée d'encapsuler des opérations comme celle-ci dans des fonctions nommées afin que votre code soit plus facile à comprendre. Python 3:
Version Python 2:
la source
Si vous voulez quelque chose de super simple:
Utiliser
xrange()
au lieu derange()
dans le cas de Python 2.xla source
max()
.Directement à partir de la (vieille) documentation Python (recettes pour itertools):
La version actuelle, comme suggéré par JFSebastian:
Je suppose que la machine à remonter le temps de Guido fonctionne - a fonctionné - fonctionnera - aura fonctionné - fonctionnait à nouveau.
Ces solutions fonctionnent car
[iter(iterable)]*n
(ou l'équivalent dans la version précédente) crée un itérateur, répété plusieursn
fois dans la liste.izip_longest
effectue ensuite un round-robin de "chaque" itérateur; comme il s'agit du même itérateur, il est avancé par chacun de ces appels, ce qui fait que chaque zip-roundrobin génère un tuple d'n
éléments.la source
list(grouper(3, range(10)))
retourne[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
et tous les tuples sont de longueur 3. Veuillez développer votre commentaire car je ne le comprends pas; comment appelez-vous une chose et comment la définissez-vous comme étant un multiple de 3 en «s'attendant à ce que votre chose soit un multiple de 3»? Merci d'avance.itertools
approche fonctionnelle sophistiquée qui révèle des boues illisibles, par rapport à une implémentation pure et simple de python purl==[1, 2, 3]
alorsf(*l)
est équivalent àf(1, 2, 3)
. Voir cette question et la documentation officielle .Je sais que c'est un peu vieux mais personne n'a encore mentionné
numpy.array_split
:la source
Je suis surpris que personne n'ait pensé à utiliser
iter
la forme à deux arguments de :Démo:
Cela fonctionne avec n'importe quel itérable et produit une sortie paresseusement. Il renvoie des tuples plutôt que des itérateurs, mais je pense néanmoins qu'il a une certaine élégance. Il ne rembourre pas non plus; si vous voulez du rembourrage, une simple variation de ce qui précède suffira:
Démo:
Comme les
izip_longest
solutions basées sur ce qui précède, les tampons ci-dessus sont toujours . Pour autant que je sache, il n'y a pas de recette itertools à une ou deux lignes pour une fonction qui optionnellement pad. En combinant les deux approches ci-dessus, celle-ci se rapproche assez:Démo:
Je pense que c'est le segment le plus court proposé qui offre un rembourrage en option.
Comme l'a observé Tomasz Gandor , les deux blocs de remplissage s'arrêteront de façon inattendue s'ils rencontrent une longue séquence de valeurs de tampon. Voici une dernière variante qui contourne ce problème de manière raisonnable:
Démo:
la source
islice(it, size)
expression de base et l'ont intégrée (comme je l'avais fait) dans une construction en boucle. Vous seul avez pensé à la version à deux arguments deiter()
(je l'ignorais complètement), ce qui la rend super élégante (et probablement la plus efficace en termes de performances). Je ne savais pas que le premier argument àiter
changer pour une fonction à 0 argument quand on donnait la sentinelle. Vous renvoyez un itérateur (pot. Infini) de morceaux, pouvez utiliser un itérateur (pot. Infini) en entrée, vous n'avez nilen()
tranche ni tableau. Impressionnant!it
itérateur. Deuxièmement, et surtout, vous vous terminerez prématurément si un morceau depadval
existe réellement dans votre itérable et doit être traité.izip_longest
approche, par exemple - je pense que cela pourrait être un compromis complexe. Mais ... lepadval
problème n'est-il pas partagé par toutes les réponses qui proposent unpadval
paramètre?()
comme sentinelle, fait correctement Ce travail est dû au fait. Lestuple(islice(it, size))
rendements()
quandit
est vide.)Voici un générateur qui fonctionne sur des itérables arbitraires:
Exemple:
la source
la source
map(None, iter)
est égalizip_longest(iter)
.*
tuple d'itérateur devant vous? Peut-être dans votre texte de réponse, mais j'ai remarqué que cela était*
utilisé de cette façon en Python auparavant. Merci!Simple mais élégant
ou si vous préférez:
la source
1
ill
est impossible de les distinguer. Tout comme0
etO
. Et parfois mêmeI
et1
.print [l[x:x+10] for x in xrange(1, len(l), 10)]
range
.Critique des autres réponses ici:
Aucune de ces réponses n'est des morceaux de taille égale, ils laissent tous un morceau de runt à la fin, donc ils ne sont pas complètement équilibrés. Si vous utilisiez ces fonctions pour distribuer le travail, vous avez intégré la perspective que l'une finisse probablement bien avant les autres, donc elle ne ferait rien pendant que les autres continueraient à travailler dur.
Par exemple, la première réponse actuelle se termine par:
Je déteste juste ce runt à la fin!
D' autres, comme
list(grouper(3, xrange(7)))
, et à lachunk(xrange(7), 3)
fois: retour[(0, 1, 2), (3, 4, 5), (6, None, None)]
. CeNone
ne sont que du rembourrage, et plutôt inélégant à mon avis. Ils ne coupent PAS uniformément les itérables.Pourquoi ne pouvons-nous pas mieux les répartir?
Ma (mes) solution (s)
Voici une solution équilibrée, adaptée d'une fonction que j'ai utilisée en production (Note en Python 3 à remplacer
xrange
parrange
):Et j'ai créé un générateur qui fait de même si vous le mettez dans une liste:
Et enfin, puisque je vois que toutes les fonctions ci-dessus renvoient des éléments dans un ordre contigu (comme ils ont été donnés):
Production
Pour les tester:
Qui imprime:
Notez que le générateur contigu fournit des morceaux dans les mêmes modèles de longueur que les deux autres, mais les éléments sont tous en ordre et ils sont aussi uniformément divisés que l'on peut diviser une liste d'éléments discrets.
la source
list(grouper(3, xrange(7)))
et la seconde, à lachunk(xrange(7), 3)
fois le retour:[(0, 1, 2), (3, 4, 5), (6, None, None)]
. CeNone
ne sont que du rembourrage, et plutôt inélégant à mon avis. Ils ne coupent PAS uniformément les itérables. Merci pour votre vote!import pandas as pd; [pd.DataFrame(np.arange(7))[i::3] for i in xrange(3)]
J'ai vu la réponse la plus impressionnante de Python dans un double de cette question:
Vous pouvez créer n-tuple pour n'importe quel n. Si
a = range(1, 15)
, alors le résultat sera:Si la liste est divisée également, vous pouvez la remplacer
zip_longest
parzip
, sinon le triplet(13, 14, None)
serait perdu. Python 3 est utilisé ci-dessus. Pour Python 2, utilisezizip_longest
.la source
zip(i, i, i, ... i)
avec des arguments "chunk_size" à zip () peut être écrit commezip(*[i]*chunk_size)
si c'est une bonne idée ou non est discutable, bien sûr.zip_longest
doit être utilisé, comme dans: stackoverflow.com/a/434411/1959808range(1, 15)
manque déjà des éléments, car il y en a 14range(1, 15)
, pas 15.Si vous connaissez la taille de la liste:
Si vous ne le faites pas (un itérateur):
Dans ce dernier cas, il peut être reformulé d'une manière plus belle si vous pouvez être sûr que la séquence contient toujours un nombre entier de morceaux de taille donnée (c'est-à-dire qu'il n'y a pas de dernier morceau incomplet).
la source
La bibliothèque toolz a
partition
pour fonction:la source
Si vous aviez une taille de bloc de 3 par exemple, vous pourriez faire:
la source: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/
J'utiliserais cela lorsque ma taille de morceau est un nombre fixe que je peux taper, par exemple «3», et ne changerais jamais.
la source
J'aime beaucoup la version du doc Python proposée par tzot et JFSebastian, mais elle a deux défauts:
J'utilise beaucoup celui-ci dans mon code:
MISE À JOUR: Une version de morceaux paresseux:
la source
while True
boucle?StopIteration
levé lorsque letuple
est vide etiterable.next()
est exécuté. Cependant, cela ne fonctionne pas correctement dans Python moderne, où la sortie d'un générateur doit être effectuéereturn
sans augmenterStopIteration
. Untry/except StopIteration: return
autour de la boucle entière (et en changeantiterable.next()
pournext(iterable)
pour la compatibilité entre versions) corrige cela avec un minimum de surcharge au moins.Où AA est un tableau, SS est une taille de bloc. Par exemple:
la source
J'étais curieux de connaître les performances des différentes approches et la voici:
Testé sur Python 3.5.1
Résultats:
la source
time
bibliothèque n'est pas une bonne idée lorsque nous avons untimeit
modulecode:
résultat:
la source
Vous pouvez également utiliser la
get_chunks
fonction deutilspie
bibliothèque comme:Vous pouvez installer
utilspie
via pip:Avertissement: je suis le créateur de la bibliothèque utilspie .
la source
À ce stade, je pense que nous avons besoin d'un générateur récursif , juste au cas où ...
En python 2:
En python 3:
De plus, en cas d'invasion extraterrestre massive, un générateur récursif décoré pourrait devenir pratique:
la source
Avec les expressions d'affectation en Python 3.8, cela devient assez agréable:
Cela fonctionne sur un itérable arbitraire, pas seulement une liste.
la source
heh, une version en ligne
la source
def chunk
au lieu dechunk=lambda
possède .__ name__ attribut 'chunk' au lieu de '<lambda>'. Le nom spécifique est plus utile dans les retraits.<lamba>
ou non est, au moins, une différence notable.usage:
la source
Une autre version plus explicite.
la source
Sans appeler len (), ce qui est bon pour les grandes listes:
Et c'est pour les itérables:
La saveur fonctionnelle de ce qui précède:
OU:
OU:
la source
len()
sur de grandes listes; c'est une opération à temps constant.Voici une liste d'approches supplémentaires:
Donné
Code
La bibliothèque standard
more_itertools
+Références
zip_longest
( Poste connexe , après liés )setdefault
(les résultats ordonnés nécessitent Python 3.6+)collections.defaultdict
(les résultats ordonnés nécessitent Python 3.6+)more_itertools.chunked
( connexe publié )more_itertools.sliced
more_itertools.grouper
( poste connexe )more_itertools.windowed
(voir aussistagger
,zip_offset
)+ Une bibliothèque tierce qui implémente les recettes itertools et plus encore.
> pip install more_itertools
la source
Voir cette référence
Python3
la source
zip(*[iter(range(7))]*3)
ne retourne[(0, 1, 2), (3, 4, 5)]
et oublie que6
de l'entrée.Puisque tout le monde ici parle d'itérateurs.
boltons
a une méthode parfaite pour cela, appeléeiterutils.chunked_iter
.Production:
Mais si vous ne voulez pas être miséricordieux sur la mémoire, vous pouvez utiliser l'ancienne méthode et stocker le plein
list
en premier lieu aveciterutils.chunked
.la source
Encore une solution
la source
la source
Pensez à utiliser des morceaux de matplotlib.cbook
par exemple:
la source