Python - Average every "n" elements in a list

15,154

Solution 1

You can compute the [2,5,8] list in a list comprehension like this:

list1 = [1,2,3,4,5,6,7,8,9]
n = 3

list2 = [sum(list1[i:i+n])//n for i in range(0,len(list1),n)]

Then put it back in list1 (preserving size) like you requested like this:

for i in range(len(list1)):
    list1[i] = list2[i//n]

or with a list comprehension:

list1 = [list2[i//n] for i in range(len(list1))]

Final edit: found a nice oneliner to sum it all up:

import itertools
list1 = list(itertools.chain.from_iterable([i]*n for i in [sum(list1[i:i+n])//n for i in range(0,len(list1),n)]))

Solution 2

Borrowing a bit from @Jean-François Fabre's answer but using statistics.mean (avaiable for python 3.4+):

>>> from statistics import mean
>>> from itertools import chain

>>> lst = [1,2,3,4,5,6,7,8,9]
>>> n = 3

>>> list(chain.from_iterable([mean(lst[i:i+n])]*n for i in range(0,len(lst),n)))
[2, 2, 2, 5, 5, 5, 8, 8, 8]

Solution 3

You can use mean in numpy as :

import numpy as np
list1 = [1,2,3,4,5,6,7,8,9]
np.mean(np.array(list1).reshape(-1, 3), axis=1)

Solution 4

>>> n = 3
>>> list1 = [1,2,3,4,5,6,7,8,9]
>>> [avg for avg in [sum(list1[i:i+n])//n for i in range(0,len(list1),n)] for j in range(n)]
[2, 2, 2, 5, 5, 5, 8, 8, 8]

Don't need itertools :-)

Explanation: Following splits the job into 2 steps; does that help? Which part is still unclear?

>>> n = 3
>>> list1 = [1,2,3,4,5,6,7,8,9]
>>> averages = [sum(list1[i:i+n])//n for i in range(0,len(list1),n)]
>>> print("averages: ", averages)
averages:  [2, 5, 8]
>>> list2 = [avg for avg in averages for j in range(n)]
>>> print("list2: ", list2)
list2:  [2, 2, 2, 5, 5, 5, 8, 8, 8]

UPDATE: Another way of doing a no-itertools one-liner:

>>> list2 = sum(([a]*n for a in [sum(list1[i:i+n])//n for i in range(0,len(list1),n)]), [])
[2, 2, 2, 5, 5, 5, 8, 8, 8]

Explanation: We calculate the averages as before. Then we spread them around like this:

>>> averages = [2, 5, 8]
>>> list2 = sum(([a]*n for a in averages), []) ### see note [1] below
>>> list2
[2, 2, 2, 5, 5, 5, 8, 8, 8]

which can be further unwound like this:

>>> all_items = list([a]*n for a in averages)
>>> all_items
[[2, 2, 2], [5, 5, 5], [8, 8, 8]]
>>> sum(all_items, [])
[2, 2, 2, 5, 5, 5, 8, 8, 8]
>>>

Note [1]: The first arg of sum appears at first look to be contained in unnecessary round brackets ... if you think so, try to run it without them and see what happens.

Solution 5

in case someone is looking for an all-numpy solution, these two lines work as long as the length of the array is divisible by n:

avg = np.mean(array1.reshape(-1, n), axis=1)
array2 = np.repeat(avg, n)

If the length of the array is not divisible by n, one can average the remaining elements. A function that does this could look somehting like this:

import numpy as np
def average(arr, n):
    remainder = len(arr) % n
    if remainder == 0:
        avg = np.mean(arr.reshape(-1, n), axis=1)
        avg = np.repeat(avg, n)
        return avg
    else:
        avg_head = np.mean(arr[:-remainder].reshape(-1, n), axis=1)
        avg_tail = np.mean(arr[-remainder:])
        avg_head = np.repeat(avg_head, n)
        avg_tail = np.repeat(avg_tail, remainder)
        return np.append(avg_head, avg_tail)

Examples with n=3, n=4 and n=5:

>>> array1 = np.arange(1, 10)
>>> array1
array([1, 2, 3, 4, 5, 6, 7, 8, 9])

>>> average(array1, 3)
array([2., 2., 2., 5., 5., 5., 8., 8., 8.])

>>> average(array1, 4)
array([2.5 2.5 2.5 2.5 6.5 6.5 6.5 6.5 9. ])

>>> average(array1, 5)
array([3.  3.  3.  3.  3.  7.5 7.5 7.5 7.5])
Share:
15,154
Admin
Author by

Admin

Updated on June 27, 2022

Comments

  • Admin
    Admin almost 2 years

    I need to average every n elements in Python list, n = 3 in this example:

    list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    So that the output list would be:

    list2 = [2, 2, 2, 5, 5, 5, 8, 8, 8]
    
  • Chris_Rands
    Chris_Rands over 7 years
    Decent answer, but it IMO it seems perhaps too generous to provide one when the OP has not made any effort themselves. Also it's not clear floor division is most appropriate here