12

This is probably very simple and I'm overlooking something...

I have a long list of integers, in this case representing daily visitors to a website. I want a new list of weekly visitors. So I need to get groups of seven from the original list, sum them, and add them to a new list.

My solution seems pretty brute force, inelegant:

numweeks = len(daily) / 7
weekly = []
for x in range(numweeks):
    y = x*7
    weekly.append(sum(visitors[y:y+7]))

Is there a more efficient, or more pythonic way of doing this?

fitzgeraldsteele
  • 4,547
  • 3
  • 24
  • 25
  • 2
    This is actually a good question for codereview.stackexchange.com, which is specifically for improving code that works to make it faster/more elegant. – Winston Ewert May 26 '11 at 03:47
  • Thanks...didn't know about that stackexchange site. It's hard to know which one is appropriate for which question, especially since there are so many these days. Plus, this one seems to have all the eyeballs, so I always feel like the original is best. :) – fitzgeraldsteele May 26 '11 at 03:52
  • Nothing wrong with this code - it's straightforward and communicates its intent well. I'd just change the var names to something more descriptive than `x` and `y`, but otherwise this is preferable to any comprehension-based hack – Eli Bendersky May 26 '11 at 03:52
  • 1
    The difference is that over there you have the attention specifically of people who like reading other people's code to figure out how to do things better. – Winston Ewert May 26 '11 at 04:02

3 Answers3

15
weekly = [ sum(visitors[x:x+7]) for x in range(0, len(daily), 7)]

Or slightly less densely:

weekly = []
for x in range(0, len(daily), 7):
     weekly.append( sum(visitors[x:x+7]) )

Alternatively, using the numpy module.

by_week = numpy.reshape(visitors, (7, -1))
weekly = numpy.sum( by_week, axis = 1)

Note that this requires the number of elements in visitor be a multiple of 7. It also requires that you install numpy. However, its probably also more efficient then the other approaches.

Or for itertools code bonus:

def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

weekly = map(sum, grouper(7, visitors, 0))
Winston Ewert
  • 44,070
  • 10
  • 68
  • 83
  • +1 for including the itertools.izip_longest case, which I hoped you hadn't so I could mention it. :-) – Martijn Pieters May 26 '11 at 08:47
  • Also, for large sets of visitors or if visitors is a generator, the itertools.izip_longest and itertools.imap options are more efficient, and I'd use an `xrange` instead of a `range` call in the first example. – Martijn Pieters May 26 '11 at 08:52
0
>>> daily = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
>>> print [sum(daily[x:x+7]) for x in range(0, len(daily), 7)]
[28, 77, 105]

I am not sure if this is "pythonic", but I truly love this one-line stuff of python.

Gory Details: Comprehensions

Linmic
  • 761
  • 4
  • 12
0

Using itertools.islice:

weekly = [sum(list(itertools.islice(daily, i, i+7)))
          for i in range(0, len(daily), 7)]

Edit:

or, with math.fsum:

weekly = [math.fsum(itertools.islice(daily, i, i+7))
          for i in range(0, len(daily), 7)]
riza
  • 16,274
  • 7
  • 29
  • 29
  • Using islice like that isn't going to be very efficient. You are going to iterate over the elements in daily repeatedly that way. – Winston Ewert May 26 '11 at 13:41