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?
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.
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
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]
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
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
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)]
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]]
from statistics import mean
means = map(mean, top_n)
[4, 6, 6, 6, 5.5, 7, 7]
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]
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)
]
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.
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.
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. ])
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]
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])
[]
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]
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]
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]
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
]
]
]
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 .
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])
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]