Normalizing array data

I have an array of values ​​(-1.0 to 1.0) that represent the intensity (black to white). I need a way to match double values ​​from -1.0 to 1.0 to 0 to 255 and back.

More generalized, I have an array of data and I need to map from the minimum and maximum data values ​​to the supplied min and max. The basic structure should be like this:

private static int[] NormalizeData(double[] data, int min, int max)
{
    var sorted = data.OrderBy(d => d);
    double dataMax = sorted.First();
    double dataMin = sorted.Last();
    int[] ret = new int[data.Length];

    for (int i = 0; i < data.Length; i++)
    {
        ret[i] = (int)data[i];  // Normalization here
    }

    return ret;
}

      

+2


source to share


6 answers


It works:

private static int[] NormalizeData(IEnumerable<double> data, int min, int max)
{
    double dataMax = data.Max();
    double dataMin = data.Min();
    double range = dataMax - dataMin;

    return data
        .Select(d => (d - dataMin) / range)
        .Select(n => (int)((1 - n) * min + n * max))
        .ToArray();
}

      

The first choice normalizes the input from 0 to 1 (0 is the minimum, 1 is the maximum). The second choice takes this normalized number and maps it to the new minimum and maximum.



Note that using LINQ Min()

and Max()

functions is faster than sorting input for larger datasets: O (n) versus O (n * lg (n)).

Also, if you want to go the other way, then you'll want it to return doubles instead of ints.

+7


source


public static double Scale(this double elementToScale,
              double rangeMin, double rangeMax, 
              double scaledRangeMin, double scaledRangeMax)
{
    var scaled = scaledRangeMin + ((elementToScale - rangeMin) * (scaledRangeMax - scaledRangeMin) / (rangeMax - rangeMin));
    return scaled;
}

      

Using:



// double [-1,1] to int [0-255]
int[] integers = doubles.Select(x => x.Scale(-1,1,0,255)).ToArray();

//  int [0-255] to double [-1,1]
double[] doubles = integers.Select(x => ((double)x).Scale(0,255,-1,1)).ToArray();

      

If you don't know in advance min and max ( [0-255]

and [-1,1]

in the example), you can use LINQ Min()

andMax()

+2


source


EDIT: How about this:

private static int[] NormalizeData(double[] data, int min, int max)
{
    var sorted = data.OrderBy(d => d);
    double dataMax = sorted.First();
    double dataMin = sorted.Last();
    int[] ret = new int[data.Length];

    double avgIn = (double)((min + max) / 2.0);
    double avgOut = (dataMax + dataMin) / 2.0);

    for (int i = 0; i < data.Length; i++)
    {
        ret[i] = (int) Math.Round(avgOut * (data[i] + avgIn) / 2);
    }

    return ret;
}

      

0


source


private static int[] NormalizeData(double[] data, int min, int max) {
    int[] ret = new int[data.Length];
    for (int i = 0; i < data.Length; i++) {
        ret[i] = (int)((max * (data[i] + 1)) / 2);
    }
    return ret;
}

static void Main(string[] args) {
    double[] data = { 1.0, -1, 0, -.5, .5 };
    int[] normalized = NormalizeData(data, 0, 255);
    foreach (var v in normalized) {
        Console.WriteLine(v);
    }
}

      

0


source


Assuming a strictly linear transformation and want to dataMin

map to min

and dataMax

to map to max

:

double dataRange = dataMax - dataMin;
int newRange = max - min;

double pct = (data[i] - dataMin) / dataRange;

int newValue = Math.Round(min + (pct * newRange));

      

This can certainly be optimized, but it shows the main idea. Basically, you determine the position (as a percentage) of the value in the source range and then map that percentage to the target range.

Note that if dataMin

equal to -0.5 and dataMax

equal to 0.5, this may not produce the results you are looking for, because -0.5 will map to 0 and 0.5 will map to 255. If you want things exactly the same, as stated, you also need to define a range of sources.

As an aside, there is no particular reason for sorting items to get min and max. You can write:

double dataMax = data.Max();
double dataMin = data.Min();

      

0


source


To be able to normalize your array, which in this example acts mathematically in a vector fashion, you need to determine how long the vector is (how many dimensions). This is not entirely clear from the example if you want to normalize the entire array taking into account all the elements of the array. If so, you are computing the dot product of the array, store the dot-square root as the length of the array. then you divide each term with that length to normalize the array to 1.0 length.

In the example above, you didn't actually describe data normalization, but transformation. To fix this problem, you can use something like the following:

private static double[] convertToScale(double[] data, double oldMin, double oldMax,double min, double max)
{
    double oldDiff = 0 - oldMin;
    double oldScale = oldMax - oldMin;
    double diff = 0 - min;
    double scale = max - min;
    int[] ret = new double[data.Length];

    for (int i = 0; i < data.Length; i++)
    {
        double scaledFromZeroToOne = (oldDiff+data[i])/oldScale; // Normalization here [0,1]
        double value = (scaledFromZeroToOne*scale)-diff;
        ret[i] = value;  
    }

    return ret;
}

      

This function, I believe, will solve the problem described above. You can call it with the following line:

double[] result = convertToScale(input,-1.0,1.0,0,255);

      

And then cast everything to int if you prefer values ​​represented as ints.

Hope it helps.

0


source







All Articles