web-dev-qa-db-fra.com

Comment prendre les N premiers éléments d'un générateur ou d'une liste en Python?

Avec linq je voudrais

var top5 = array.Take(5);

Comment faire cela avec Python?

264
Jader Dias

Trancher une liste

top5 = array[:5]
  • Pour découper une liste, il existe une syntaxe simple: array[start:stop:step]
  • Vous pouvez omettre n'importe quel paramètre. Ce sont tous valables: array[start:], array[:stop], array[::step]

Trancher un générateur

 import itertools
 top5 = itertools.islice(my_list, 5) # grab the first five elements
  • Vous ne pouvez pas découper un générateur directement en Python. itertools.islice() va envelopper un objet dans un nouveau générateur de découpage en utilisant la syntaxe itertools.islice(generator, start, stop, step)

  • Rappelez-vous que couper un générateur l’épuisera partiellement. Si vous souhaitez conserver l'intégralité du générateur, commencez par le transformer en tuple ou en liste, par exemple: result = Tuple(generator)

419
lunixbochs
import itertools

top5 = itertools.islice(array, 5)
105
Jader Dias

À mon goût, il est également très concis de combiner "Zip ()" avec "xrange (n)" (ou "range (n)" en Python3), qui fonctionne bien sur les générateurs et semble être plus flexible pour les changements de général.

# Option #1: taking the first n elements as a list
[x for _, x in Zip(xrange(n), generator)]

# Option #2, using 'next()' and taking care for 'StopIteration'
[next(generator) for _ in xrange(n)]

# Option #3: taking the first n elements as a new generator
(x for _, x in Zip(xrange(n), generator))

# Option #4: yielding them by simply preparing a function
# (but take care for 'StopIteration')
def top_n(n, generator):
    for _ in xrange(n): yield next(generator)
32
Shaikovsky

La réponse de @ Shaikovsky est excellente (… et fortement modifiée depuis que j'ai posté cette réponse ), mais je voulais clarifier quelques points.

[next(generator) for _ in range(n)]

C'est l'approche la plus simple, mais jette StopIteration si le générateur est épuisé prématurément.


Par ailleurs, les approches suivantes renvoient des éléments jusqu’àn, ce qui est sans doute préférable dans la plupart des cas:

Liste: [x for _, x in Zip(range(n), records)]

Générateur: (x for _, x in Zip(range(n), records))

21
Bede Constantinides

La réponse pour savoir comment faire cela peut être trouvée ici

>>> generator = (i for i in xrange(10))
>>> list(next(generator) for _ in range(4))
[0, 1, 2, 3]
>>> list(next(generator) for _ in range(4))
[4, 5, 6, 7]
>>> list(next(generator) for _ in range(4))
[8, 9]

Notez que le dernier appel demande le prochain 4 alors qu'il n'en reste que 2. L'utilisation de list() au lieu de [] est ce qui permet à la compréhension de se terminer sur l'exception StopIteration qui est levée par next().

13
ebergerson

Voulez-vous dire les premiers N éléments, ou les N les plus grands?

Si vous voulez le premier:

top5 = sequence[:5]

Cela fonctionne également pour les N éléments les plus grands, en supposant que votre séquence soit triée par ordre décroissant. (Votre exemple LINQ semble le supposer également.)

Si vous voulez le plus gros, et que cela ne soit pas trié, la solution la plus évidente est de le trier en premier:

l = list(sequence)
l.sort(reverse=True)
top5 = l[:5]

Pour une solution plus performante, utilisez un min-tas (merci Thijs):

import heapq
top5 = heapq.nlargest(5, sequence)
5
Thomas

Avec itertools vous obtiendrez un autre objet générateur, de sorte que dans la plupart des cas, vous aurez besoin d'une autre étape pour prendre les N premiers éléments (N). Il existe au moins deux solutions plus simples (un peu moins efficace en termes de performances mais très pratique) pour obtenir les éléments prêts à être utilisés à partir d'un generator:

Utilisation de la compréhension de liste:

first_N_element=[generator.next() for i in range(N)]

Autrement:

first_N_element=list(generator)[:N]

N est le nombre d'éléments à prendre (par exemple, N = 5 pour les cinq premiers éléments).

3
G M