3

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]
MSeifert
  • 145,886
  • 38
  • 333
  • 352
bloomwoood
  • 81
  • 1
  • 1
  • 3
  • 1
    Did you try something yourself? – Mohammad Yusuf Jan 30 '17 at 10:07
  • Show a coding attempt, so `n` is 3 in your example? – Chris_Rands Jan 30 '17 at 10:07
  • 8
    Shouldn't the result be `[2,5,8]`? – wvdz Jan 30 '17 at 10:11
  • 1
    Can you explain the question a bit more? About what is n, and how should the o/p come as. Currently you just have the input and output without any definitions of n. If we assume n is 3, then it makes a bit of sense regarding the o/p, but you may want something entirely different. And also tell us what you tried so far. And shouldn't the result be [2,5,8] instead of returning redundant data? – Shaurya Chaudhuri Jan 30 '17 at 10:13
  • @wvdz I need to keep the same number of elements in the `list2`, so I would like to write the average to the first 2 elements in the block as well – bloomwoood Jan 30 '17 at 10:17
  • It's very hard to understand the question, if you want to return 'n' in every 3 steps, you could use `list2 = list1[0::3]`. What does average has to do anything in this case? http://stackoverflow.com/questions/1403674/pythonic-way-to-return-list-of-every-nth-item-in-a-larger-list – ShellRox Jan 30 '17 at 10:21

5 Answers5

5

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)]))
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • 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 – Chris_Rands Jan 30 '17 at 10:19
3

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]
Community
  • 1
  • 1
MSeifert
  • 145,886
  • 38
  • 333
  • 352
1

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)
khelili miliana
  • 3,730
  • 2
  • 15
  • 28
1
>>> 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.

John Machin
  • 81,303
  • 11
  • 141
  • 189
1

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])
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219