web-dev-qa-db-fra.com

Python itère dans un tableau tout en trouvant la moyenne des k premiers éléments

Supposons que j'ai un tableau Python a=[3, 5, 2, 7, 5, 3, 6, 8, 4]. Mon objectif est d’itérer dans ce tableau 3 éléments à la fois, en renvoyant la moyenne des 2 éléments les plus importants.

En utilisant le tableau ci-dessus, lors de l'étape d'itération, les trois premiers éléments sont [3, 5, 2] et la moyenne des 2 premiers éléments est 4. Les trois prochains éléments sont [5, 2, 7] et la moyenne des 2 premiers éléments est 6. Les trois suivants sont [2, 7, 5] et la moyenne des 2 premiers éléments est encore 6 ... ...

Par conséquent, le résultat pour le tableau ci-dessus serait [4, 6, 6, 6, 5.5, 7, 7].

Quel est le meilleur moyen d'écrire une telle fonction?

19
Student

Solution

Vous pouvez utiliser des découpes fantaisistes de votre liste pour manipuler des sous-ensembles d'éléments. Il suffit de saisir chaque sous-liste de trois éléments, d'effectuer un tri pour trouver les deux premiers éléments, puis de trouver la moyenne simple (ou moyenne) et de l'ajouter à une liste de résultats.

Code

def get_means(input_list):
    means = []
    for i in xrange(len(input_list)-2):
        three_elements = input_list[i:i+3]
        sum_top_two = sum(three_elements) - min(three_elements)
        means.append(sum_top_two/2.0)
    return means

Exemple

Vous pouvez voir votre exemple d’entrée (et le résultat souhaité) comme suit:

print(get_means([3, 5, 2, 7, 5, 3, 6, 8, 4]))
# [4.0, 6.0, 6.0, 6.0, 5.5, 7.0, 7.0]

Et plus...

Il existe d’autres bonnes réponses qui entrent dans des réponses plus orientées sur les performances, notamment l’utilisation d’un générateur pour éviter les listes de mémoire volumineuses: https://stackoverflow.com/a/49001728/416500

14
foslock

Je crois en diviser le code en 2 parties. Dans ce cas, il s’agirait d’obtenir la fenêtre glissante, d’obtenir les 2 principaux éléments et de calculer la moyenne. moyen le plus propre de le faire utilise des générateurs

Fenêtre coulissante

Légère variation dans la réponse de evamicur avec tee, islice et Zip pour créer la fenêtre:

def windowed_iterator(iterable, n=2):
    iterators = itertools.tee(iterable, n)
    iterators = (itertools.islice(it, i, None) for i, it in enumerate(iterators))
    yield from Zip(*iterators)

windows = windowed_iterator(iterable=a, n=3)
[(3, 5, 2), (5, 2, 7), (2, 7, 5), (7, 5, 3), (5, 3, 6), (3, 6, 8), (6, 8, 4)]

2 premiers éléments

pour calculer la moyenne des 2 valeurs les plus élevées, vous pouvez utiliser l'une des méthodes utilisées dans les autres réponses, je pense que la variable heapq est la plus claire

from heapq import nlargest
top_n = map(lambda x: nlargest(2, x), windows)

ou équivalent

top_n = (nlargest(2, i) for i in windows)
[[5, 3], [7, 5], [7, 5], [7, 5], [6, 5], [8, 6], [8, 6]]

signifier

from statistics import mean
means = map(mean, top_n)
[4, 6, 6, 6, 5.5, 7, 7]
12
Maarten Fabré

Le code suivant fait ce dont vous avez besoin:

[sum(sorted(a[i:i + 3])[-2:]) / 2 for i in range(len(a) - 2)]

Étant donné votre a=[3, 5, 2, 7, 5, 3, 6, 8, 4], retourne:

[4.0, 6.0, 6.0, 6.0, 5.5, 7.0, 7.0]
8
damores

itertools a une recette soignée pour extraire des paires d’éléments de tout élément itérable, pas seulement indexable. Vous pouvez l’adapter légèrement pour extraire les triplets à la place:

def tripletwise(iterable):
    a, b, c = itertools.tee(iterable, 3)
    next(b, None)
    next(itertools.islice(c, 2, 2), None)
    return Zip(a, b, c)

En utilisant cela, vous pouvez simplifier l'itération sur tous les triplets:

def windowed_means(iterable):
    return [
        (sum(window) - min(window)) / 2.0
        for window in tripletwise(iterable)
    ]
6
Mathias Ettinger

Solution uniquement par itérateur

la solution de foslok est définitivement satisfaisante, mais je voulais jouer et en faire une version avec des générateurs. Il stocke uniquement un deque de longueur (window_size) Lorsqu’il parcourt la liste d’origine, puis trouve les valeurs n_largest et en calcule la moyenne. 

import itertools as it
from collections import deque
from heapq import nlargest
from statistics import mean

def windowed(iterable, n):
    _iter = iter(iterable)
    d = deque((it.islice(_iter, n)), maxlen=n)
    yield Tuple(d)
    for i in _iter:
        d.append(i)
        yield Tuple(d)

a = [3, 5, 2, 7, 5, 3, 6, 8, 4]
means = [mean(nlargest(2, w)) for w in windowed(a, 3)]
print(means)   

résultat:

[4, 6, 6, 6, 5.5, 7, 7]

Ainsi, pour modifier le nombre d'éléments (taille de la fenêtre) ou les n éléments les plus grands, il suffit de modifier les arguments des fonctions respectives. Cette approche évite également l'utilisation du découpage, ce qui permet de l'appliquer plus facilement aux éléments itérables que vous ne pouvez ou ne souhaitez pas découper. 

Les horaires

def deque_version(iterable, n, k):
    means = (mean(nlargest(n, w)) for w in windowed(iterable, k))
    for m in means:
        pass

def tee_version(iterable, n, k):
    means = (mean(nlargest(n, w)) for w in windowed_iterator(iterable, k))
    for m in means:
        pass

a = list(range(10**5))


n = 3 
k = 2
print("n={} k={}".format(n, k))
print("Deque")
%timeit deque_version(a, n, k)
print("Tee")
%timeit tee_version(a, n, k)

n = 1000 
k = 2
print("n={} k={}".format(n, k))
print("Deque")
%timeit deque_version(a, n, k)
print("Tee")
%timeit tee_version(a, n, k)

n = 50
k = 25
print("n={} k={}".format(n, k))
print("Deque")
%timeit deque_version(a, n, k)
print("Tee")
%timeit tee_version(a, n, k)


result:

n=3 k=2
Deque
1.28 s ± 3.07 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Tee
1.28 s ± 16.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
n=1000 k=2
Deque
1.28 s ± 8.72 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Tee
1.27 s ± 2.92 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
n=50 k=25
Deque
2.46 s ± 10.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Tee
2.47 s ± 2.45 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Donc, apparemment, le tee-shirt itertools vs deque n'a pas beaucoup d'importance. 

3
evamicur

En tant qu’approche vectorielle utilisant Numpy, vous pouvez effectuer les opérations suivantes:

np.sort(np.column_stack((a[:-2], a[1:-1], a[2:])))[:,-2:].mean(axis=1)

Démo:

In [13]: a=np.array([3, 5, 2, 7, 5, 3, 6, 8, 4])

In [14]: np.sort(np.column_stack((a[:-2], a[1:-1], a[2:])))[:,-2:].mean(axis=1)
Out[14]: array([4. , 6. , 6. , 6. , 5.5, 7. , 7. ])
3
Kasrâmvd

Vous avez besoin d'un itérateur de fenêtre coulissante avec la moyenne de deux éléments maximum. Je vais essayer de produire une solution générique qui peut être utilisée avec une fenêtre glissante de taille n où n est un nombre réel positif.

from itertools import islice

def calculate_means(items, window_length=3):
     stop_seq = window_length - 1
     sliding_window = [sorted(islice(items[x:],window_length),reverse=True) for x in range(len(items)-stop_seq)]
     return [sum(a[:stop_seq])/stop_seq for a in sliding_window]

>>> calculate_means([3, 5, 2, 7, 5, 3, 6, 8, 4])
>>> [4.0, 6.0, 6.0, 6.0, 5.5, 7.0, 7.0]
1
bro-grammer

Pour mémoire, voici une version fonctionnelle:

>>> f=lambda values:[] if len(values)<=2 else [(sum(values[:3])-min(values[:3]))/2]+f(values[1:])
>>> f([3, 5, 2, 7, 5, 3, 6, 8, 4])
[4.0, 6.0, 6.0, 6.0, 5.5, 7.0, 7.0]
>>> f([3, 5, 2])
[4.0]
>>> f([3, 5])
[]
1
jferard

Utiliser la compréhension de la liste 

from statistics import mean

yourList=[3, 5, 2, 7, 5, 3, 6, 8, 4]

k = 3

listYouWant = [mean(x) for x in [y[1:k] for y in [sorted(yourList[z:z+k]) for z in xrange(len(yourList)) if z < len(yourList) -(k-1)]]]

rendements [4.0, 6.0, 6.0, 6.0, 5.5, 7.0, 7.0]

1
John H

Vous pouvez essayer ça!

>>> a
[3, 5, 2, 7, 5, 3, 6, 8, 4]
>>> n
3
>>> m
2
>>> [sum(sorted(a[i*n:i*n+n])[1:])/m for i in range(len(a)/n)]
[4, 6, 7]

C'est,

>>> a
[3, 5, 2, 7, 5, 3, 6, 8, 4]
>>> n
3
>>> [i for i in range(len(a)/n)]
[0, 1, 2]
>>> m=2
>>> [a[i*n:i*n+n] for i in range(len(a)/n)]
[[3, 5, 2], [7, 5, 3], [6, 8, 4]]
>>> [sorted(a[i*n:i*n+n]) for i in range(len(a)/n)]
[[2, 3, 5], [3, 5, 7], [4, 6, 8]]
>>> [sorted(a[i*n:i*n+n])[1:] for i in range(len(a)/n)]
[[3, 5], [5, 7], [6, 8]]
>>> [sum(sorted(a[i*n:i*n+n])[1:]) for i in range(len(a)/n)]
[8, 12, 14]
>>> [sum(sorted(a[i*n:i*n+n])[1:])/m for i in range(len(a)/n)]
[4, 6, 7]
1
Keerthana Prabhakaran

Vous pouvez aussi regarder cela du point de vue des générateurs: 

a=[3, 5, 2, 7, 5, 3, 6, 8, 4]

def gen_list():
    for i in range(0, len(a) - 3):
        yield sorted(a[i:i + 3], reverse=True)

apply_division = map(lambda x: sum(x[:2]) / len(x[:2]), gen_list())


if __name__=="__main__":
    result = list(apply_division)
    print(result)
[4.0, 6.0, 6.0, 6.0, 5.5, 7.0]
1
Mahdi Ghelichi
a=[3, 5, 2, 7, 5, 3, 6, 8, 4]
mean_list = [
    mean(x)
        for x in [
            y[1:3]
                for y in [
                    sorted(a[z:z+3])
                        for z in range(len(a))
                            if z < len(a) -2
                ]
        ]
]
1
Rahul

En utilisant l'algorithme window window et l'outil tiers more_itertools.windowed :

import statistics as stats

import more_itertools as mit


lst = [3, 5, 2, 7, 5, 3, 6, 8, 4]

[stats.mean(sorted(w)[1:]) for w in mit.windowed(lst, 3)]
# [4, 6, 6, 6, 5.5, 7, 7]

Voir aussi le message de _Maarten Fabré related .

1
pylang

Pour trier trois nombres, nous avons besoin d’un maximum de trois comparaisons. Pour trouver le plus petit des trois nombres, nous n'avons besoin que de deux par sélection rapide. Nous n'avons également pas besoin de faire de copies de sous-listes:

a,b,c

a < b
? (a < c ? a : c)
: (b < c ? b : c)
def f(A):
  means = [None] * (len(A) - 2)

  for i in xrange(len(A) - 2):
    if A[i] < A[i+1]:
      means[i] = (A[i+1] + A[i+2]) / 2.0 if A[i] < A[i+2] else (A[i] + A[i+1]) / 2.0
    else:
      means[i] = (A[i] + A[i+2]) / 2.0 if A[i+1] < A[i+2] else (A[i] + A[i+1]) / 2.0

  return means

print f([3, 5, 2, 7, 5, 3, 6, 8, 4])
0
גלעד ברקן

Ne triez pas vos sous-listes, cette opération est nlog(n)! À la place, recherchez les deux plus grands nombres avec un algorithme O(n). Cela augmentera l'efficacité de votre solution. Le gain d’efficacité sera plus visible si vous travaillez sur un problème plus vaste de "recherche de la somme de la plus haute m sur une fenêtre mobile de k éléments" pour les grands m et k.

def largestTwoMeans(myList):
    means = []
    for i in xrange(len(myList)-2):
        subList = myList[i:i+3]
        first, second = -float("inf"), -float("inf")
        for f in subList:       
            if f >= first:
                first, second = f, first
            Elif first > f > second:
                second = f
        means.append((first+second)/2.0)
    return means

print largestTwoMeans(myList)
Out[222]: [4.0, 6.0, 6.0, 6.0, 5.5, 7.0, 7.0]

Voici la version du générateur:

def largestTwoMeans(myList):
    for i in xrange(len(myList)-2):
        subList = myList[i:i+3]
        first, second = -float("inf"), -float("inf")
        for f in subList:       
            if f >= first:
                first, second = f, first
            Elif first > f > second:
                second = f
        yield (first+second)/2.0

print list(largestTwoMeans(myList))
Out[223]: [4.0, 6.0, 6.0, 6.0, 5.5, 7.0, 7.0]
0
FatihAkici