Finding average of an array

277 views Asked by At

I am really struggling with finding weighted sum from an array. I have an array (1D size 28) called frequency[28] and same sized same indexed array called peak[28]. Arrays will always have either a value or zero. What I want to achieve is go through the array, retrieve both values from frequency and magnitude array while ignoring zero. In addition, I am not trying to find weighted average of the whole array.

I think I am not making myself clear enough.

For instance say

frequency[n] = [0,0,0, a,b, 0,0,0, c,d,e, 0,0, f]
peak[n] = [0,0,0, z,y, 0,0,0, x,w,v, 0,0, u]

Thus, I want to ignore first three bins since they are zero, and find weighted average of (a,b) paired with (z,y) and ignore next three bins and then again find average of (c,d,e) paired with (x,w,v) and so on.
Note that my values in arrays (size is fixed) are not fixed. Index where a value may appear is always varying.

I've attached snippet of the code retrieving the arrays. Any suggestion or guidance would be a great help!

// peak search
threshold = 0;
for (ctr=0; ctr<n1; ctr++)
{   
    peak[ctr] = 0; // initialise arrays
    freq_bin[ctr] =0;
    frequency[ctr] = 0;

    // magnitude calculation from fft output
    fft_mag[ctr] = 10*(sqrt((fft_output[ctr].r * fft_output[ctr].r) + (fft_output[ctr].i * fft_output[ctr].i)))/(n);
    threshold = 12; 
    if (fft_mag[ctr] >= threshold) // extract fft magnitudes only above threshold
    {
        peak[ctr] = fft_mag[ctr]; // store magnitude above threshold into peak array
        freq_bin[ctr] = ctr;        // location of each magnitude above threshold
        frequency[ctr] = (freq_bin[ctr]*(10989/n)); // frequency calculation from magnitude location        
    }
}

My apologies for not commenting the codes.

  • peak[ctr] contains peak magnitudes of fft output
  • frequency[ctr] contains frequency value of corresponding fft peak magnitudes.

And I have multiple peaks from fft and the output array looks like this;

peak[28] =      [0 0 0 0 0 0 0 0 0 0  14 0 0 0 0 0 0  14 0 0 0  29  74   45 0 0 0 0]
frequency[28] = [0 0 0 0 0 0 0 0 0 0 462 0 0 0 0 0 0 714 0 0 0 924 966 1008 0 0 0 0]

Thus, I need to calculate:

  • average 1 = (14x462)/14 = 462 Hz
  • average 2 = (14x714)/14 = 714 Hz
  • average 3 = (29x924 + 74x966 + 45x1008)/(29+74+45) = 938.8 Hz
2

There are 2 answers

0
Jonathan Leffler On BEST ANSWER

Here's some code that approximates an MCVE (How to create a Minimal, Complete, and Verifiable Example?). Amongst other things, I've compressed the operational/interesting data by leaving one set of zeros before and in between the sets of non-zero data. The calculation for the 3-term weighted average seems to be wrong in the question:

  • average 3 = (29x924 + 74x966 + 45x1008)/(29+74+45) = 938.8 Hz
  • average 3 = (26796 + 71484 + 45360) / 148
  • average 3 = 143640 / 148
  • average 3 = 970.54

This agrees with the program's calculation.

Code:

#include <stdio.h>

int main(void)
{
    enum { NUM_ENTRIES = 28 };

    /* Input data */
    double peak[NUM_ENTRIES] = { 0,  14, 0,  14, 0,  29,  74,   45, 0, };
    double freq[NUM_ENTRIES] = { 0, 462, 0, 714, 0, 924, 966, 1008, 0, };

    /* Output data */
    double bin_av[NUM_ENTRIES];
    int bin_lo[NUM_ENTRIES];
    int bin_hi[NUM_ENTRIES];
    int out = 0;

    int ctr = 0;
    while (ctr < NUM_ENTRIES)
    {
        /* Skip zeroed entries */
        while (ctr < NUM_ENTRIES && (peak[ctr] == 0.0 || freq[ctr] == 0.0))
            ctr++;
        if (ctr < NUM_ENTRIES)
        {
            bin_lo[out] = ctr;
            bin_hi[out] = ctr;
            double w_sum = 0.0;
            double f_sum = 0.0;
            while (ctr < NUM_ENTRIES && (peak[ctr] != 0.0 && freq[ctr] != 0.0))
            {
                bin_hi[out] = ctr;
                w_sum += peak[ctr] * freq[ctr];
                f_sum += peak[ctr];
                ctr++;
            }
            bin_av[out++] = w_sum / f_sum;
        }
    }

    for (int i = 0; i < out; i++)
        printf("%d .. %d: %6.1f\n", bin_lo[i], bin_hi[i], bin_av[i]);

    return 0;
}

Example output:

1 .. 1:  462.0
3 .. 3:  714.0
5 .. 7:  970.5

There's plenty of room to improve the output (it would not be a bad idea to echo the relevant subset of the input data, for example).

With this framework in place, you can go back to complicating the scenario by calculating the values in peak and freq from the values in the FFT outputs, instead of using hard-coded arrays.

3
Joonazan On

You can think of this as a state machine. It reads in a symbol at a time and based on it changes state.

There are two states: the starting state and a state where some data is already read.

In the starting state, if the next symbol is zero, remain in this state. If not, process the symbol and change state.

In the other state, if the next symbol is zero, emit a result and change state. If not, process it. If the end is encountered in this state, emit a result.

Here is a very clean Python implementation:

class Averager:

    def __init__(self):

        self.averages = []

        self.sumOfNumbers = 0
        self.count = 0

    def addToAverage(self, number, weight):
        self.sumOfNumbers += number * weight
        self.count += weight

    def emitAverage(self):
        self.averages.append(self.sumOfNumbers / self.count)
        self.sumOfNumbers = 0
        self.count = 0


def averagesOf(data, weights):

    averager = Averager()
    dataRead = False

    def emitIfData():
        if dataRead:
            averager.emitAverage()

    for number, weight in zip(data, weights):
        if number == 0:
            emitIfData()
            dataRead = False
        else:
            averager.addToAverage(number, weight)
            dataRead = True

    emitIfData()

    return averager.averages

print(averagesOf([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 462, 0, 0, 0, 0, 0, 0, 714, 0, 0, 0, 924, 966, 1008, 0, 0, 0, 0],
                 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  14, 0, 0, 0, 0, 0, 0,  14, 0, 0, 0,  29,  74,   45, 0, 0, 0, 0]))

Output:

[462.0, 714.0, 970.5405405405405]