Advice on using numpy for an efficient loop

Please tell us which board you are using.
For CircuitPython issues, ask in the Adafruit CircuitPython forum.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Daft_vagabond
 
Posts: 92
Joined: Thu May 28, 2020 2:53 pm

Advice on using numpy for an efficient loop

Post by Daft_vagabond »

I'm reading a noisy analog signal and want to smooth it out a bit. I'm doing this by taking 100 samples and finding their average and printing that instead of every reading that comes in. Everything is working just fine and here's the code for that:

Code: Select all

import analogio
import board
import math

signal = analogio.AnalogIn(board.A2)

readings = []
    
def sample_reads(readings):
    for i in range(100):
        readings.append(signal.value)
    return readings

while True:
    sample_reads(readings)
    sumof = sum(readings)
    mean = sumof / 100
    print(mean)
    readings.clear()

I know I can put most, if not all of that into the sample_reads function, but I'm just tinkering for now. While I know this isn't exactly complex math, I'm still curious if using numpy instead of math would speed things up a bit.

In this benchmark:

https://learn.adafruit.com/ulab-crunch- ... -benchmark

The "ulab only, with ndarray" completes the task much faster, and that's something I'm interested in taking advantage of. Is it as simple as importing numpy and using "np.sum" instead of "sum"? It also mentions that "ulab.numpy.std computes most quickly by moving all operations from Python to ulab". I'm guessing that just means it has the library itself do everything, instead of python?

Lastly, other than condensing some of those lines, like "mean = sum(readings) / 100", in general, might there be a more effective and fast way of finding the average of 100 samples?

EDIT: I just saw that numpy has a function for finding a mean. I'm still not clear on how to properly use it, but I imagine it's faster than using math.

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Advice on using numpy for an efficient loop

Post by adafruit_support_mike »

ulab takes advantage of what's called SIMD (Single Instruction, Multiple Data) hardware in microcontrollers like the SAMD51.. basically an array of pocket calculators running in parallel.

The idea is to load data from memory into all the calculators with one instruction, have all the calculators perform the same operation with a second instruction, then copy the results back to memory with a third instruction.
Daft_vagabond wrote:Lastly, other than condensing some of those lines, like "mean = sum(readings) / 100", in general, might there be a more effective and fast way of finding the average of 100 samples?
You can calculate a running average as:

Code: Select all

    average = ( latest_reading + ( 99 * average )) / 100
which only costs three operations per sample.

Beyond that, Python's weak type system tends to work against most bit-twiddling speed hacks. Python hides its internal representation of numbers, so there's no type distinction between integers and floating point values. That's incredibly convenient for most work, but limits your ability to play games based on specific representations.

User avatar
Daft_vagabond
 
Posts: 92
Joined: Thu May 28, 2020 2:53 pm

Re: Advice on using numpy for an efficient loop

Post by Daft_vagabond »

Very insightful, thank you!

I'm new to python, so in an effort to understand your suggested code rather than just copy/pasting it, could I ask you to help me understand it?

Code: Select all

    average = ( latest_reading + ( 99 * average )) / 100

I tried walking myself through it but I feel like I'm getting it wrong.

Does this take every new incoming value and average it with the previous 99? If so, how does it do that?

It's just so different for my idea of how to do this, which was to sample first, math later.

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Advice on using numpy for an efficient loop

Post by adafruit_support_mike »

You’re close to being correct.

For any set of N numbers, their average is the sum of all of them divided by N: averageN=sum(x1…xN)/N

For the sake of discussion let’s set N to 100 and write: average100=sum(x1…x100)/100

From there, we can flip things around a little to get: sum(x1…x100)=100*average100

Now let’s split x100 off to the side and think about what’s left: average99=sum(x1…x99)/99

We already know we can rearrange that to sum(x1…x99)=99*average99

Now, by its basic definition, sum(x1…x100)=sum(x1…x99)+x100

And from there we can do some subsitution in our definition of average100:

average100=sum(x1…x100)/100
average100=(sum(x1…x99)+x100)/100
average100=(99*average99)+x100)/100

Which is almost the same as the equation I posted before. The running average amounts to:

average101=((99*average100)+x101)/100

in which the 99*average100 term isn’t exactly equal to sum(x2…x100), but the difference is fairly small.. only (x1-average100)/99

As long as you’re working with a set of values that only change a little from one reading to the next, or one where most of the difference from one reading to the next is random noise, the error term is small enough to safely ignore.

Locked
Please be positive and constructive with your questions and comments.

Return to “Itsy Bitsy Boards”