MovingSum of list of integers

Question:

I want to calculate the moving sum of a list of integers with a window of size 3. I have a class as such:

class MovingSum:
    def __init__(self, window=3):
       self.window = window
    
    def push(self, nums: List[int]):
       pass

    def belongs(self, total) -> bool:
       pass

I need to calculate the moving sum of 3 numbers and keep track of total. Example:
Movingsum.push([1, 2, 3, 4]) will calculate the sum of (1, 2, 3) and 4, hence it keeps two totals, which is 6 and 4. Then next calls to Movingsum.push([10]) will update the total and hence we have the folloing totals: 6 and 14. Then Movingsum.push([20]) will update the total and hence we have 6 and 34. Now, Next call to Movingsum.push([10, 20, 30]) will hence have 3 totals calculated: 6, 34 and 60 etc. Hence i need to keep track of the running totals. I’m having trouble updating the total i have already calculated.

My attempt:

def __init__(self, window=3):
   self.window = window
   self.totals = set()
   self.count = 0

def push(self, nums: List[int]):
    total = 0
    for num in nums:
        total += num
        self.count += 1
        if self.count % 3 == 0:
           self.totals.add(total)
           self.count = 0

def belongs(self, total) -> bool:
    return total in self.totals

where the belongs function needs to check if the total has already been calculated. I’m having trouble figuring out how to update the new totals. Thanks

Moving sum needs to be calculated for 3 numbers before moving to next 3 etc
test case:
Start:

nums = [1, 2]
MovingSum.push(nums)
Now total is 3

MovingSum.push([10, 20])

Now total is 13 and 20 (since on first push, we calculated total of two numbers which had value 3, but we need total of 3 numbers. Hence update total to 3 + 10 (which has 3 numbers), and since only one number remaining, we have two totals: 13 and 20

MovingSum.push([40])

update the total: 13, 60 (since total of 20 has only one number)

MovingSum.push([100, 20, 30])

Update the total: 13, 160, 50 (3rd total is 20 + 30 which is two numbers)

MovingSum.push([40, 100])

Update the total: 13, 160, 90, 100 (since 90 is sum of 20 + 30 + 40) and we have one number remaining which is 100.

Asked By: turtle_in_mind

||

Answers:

If I understand you correctly, you have a constant stream of numbers coming in, and you want the total of each n-item window (which I’ll call a group) within that. So, you’ll need a list of totals of all the groups so far, and you’ll need to keep track of the running total of the items within the current (partial) group, as well as the number in the partial group.

The code below achieves this — I’m sure it could be cleaner using features from itertools and the like, but I’ve tried to keep it simple so you can see what’s going on.

class MovingSum:

    window_size: int
    totals: list[int]
    count: int

    def __init__(self, window_size: int = 3):
        self.window_size = window_size
        self.totals = []
        self.count = 0

    def push(self, nums: list[int]) -> None:
        offset = 0

        # Handle partial set as a special case, since the final total
        # needs incrementing rather than a new total being added.
        if self.count > 0:
            items = nums[:self.window_size - self.count]
            offset = len(items)
            self.totals[-1] += sum(items)
            self.count = (self.count + offset) % self.window_size

        # Iterate over the remaining items, and stop once we run out.
        # Whilst we have full groups, self.count will remain 0, but
        # the final group may be partial so we recalculate it each loop.
        while offset < len(nums):
            items = nums[offset:offset + self.window_size]
            self.totals.append(sum(items))
            self.count = len(items) % self.window_size
            offset += self.window_size

    def belongs(self, total: int) -> bool:
        if self.count > 0:
            return total in self.totals[:-1]
        else:
            return total in self.totals

The belongs() function above also excludes partial totals from the membership check, but your simpler version will work fine if you don’t need this extra complication.

As an aside, your belongs() function will get quite slow as totals gets large. This won’t be an issue for a few hundred items, but if you’re in the tens of thousands or more than a set() is a much more efficient way of checking for membership — it will also handle de-duplication rather conveniently.

Answered By: Cartroo
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.