阵列数据规范化

时间:2011-03-21 21:26:55

标签: c# arrays

我有一个表示强度(黑到白)的值数组(介于-1.0和1.0之间)。我需要一种方法将双值从-1.0映射到1.0到0到25​​5并返回。

更一般化,我有一个数据数组,我需要从数据的最小值和最大值映射到提供的最小值和最大值。基本结构应该是:

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;
}

6 个答案:

答案 0 :(得分:7)

这有效:

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();
}

第一个选择将输入标准化为0到1(0表示最小值,1表示最大值)。第二个选择采用该标准化数字,并将其映射到新的最小值和最大值。

请注意,使用LINQ Min()Max()函数比为较大数据集排序输入要快:O(n)与O(n * lg(n))。

另外,如果你想走另一条路,那么你会希望它返回双打而不是整数。

答案 1 :(得分:2)

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;
}

用法:

// 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();

如果您事先不知道最小值和最大值(示例中为[0-255][-1,1]),则可以使用LINQ Min()Max()

答案 2 :(得分:0)

修改 怎么样:

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;
}

答案 3 :(得分:0)

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);
    }
}

答案 4 :(得分:0)

假设进行了严格的线性转换,并希望dataMin映射到mindataMax以映射到max

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

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

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

这肯定可以优化,但它显示了基本的想法。基本上,您可以计算出原始范围内值的位置(百分比),然后将该百分比映射到目标范围。

请注意,如果dataMin为-0.5且dataMax为0.5,则可能无法生成您正在查找的结果,因为-0.5将映射为0而0.5将映射到255。如果您希望事物完全按照规定进行映射,您还必须定义源范围。

顺便说一下,没有特别的理由对项目进行排序只是为了获得最小值和最大值。你可以写:

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

答案 5 :(得分:0)

为了能够规范化您的数组,在此示例中以数学方式处理向量,您需要定义向量的长度(多少维度)。 从示例中可以清楚地了解是否要将整个数组规范化,同时考虑数组中的所有元素。如果是,则计算数组的点积,将点积平方根存储为数组的长度。然后将每个术语除以该长度,以将数组规范化为1.0的长度。

在上面的情况中,您实际上没有描述数据的规范化,而是转换。要解决这个问题,您可以使用以下内容:

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;
}

我认为这个功能可以解决上述问题。 您可以将其称为以下行:

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

如果您希望将值表示为整数,则将所有内容都转换为int。

希望它有所帮助。