1

这是一个数学问题...

在此之前我有一个关于标准化每月数据的问题: 如何生成拉伸图的 X 值?

我得到了一个很好的答案,而且效果很好,唯一的问题是现在我需要检查一个月的 X 值,其中 31 天与一个月的 X 值,其中 28 天。

所以我的问题是:如果我有两组这样的参数:

x    |    y           x2    |     y2

1    |    10        1.0    |     10
2    |    9         1.81    |     9.2
3    |    8         2.63    |     8.6
4    |    7         3.45    |     7.8
5    |    6         4.27    |     7
6    |    5         5.09    |     6.2
7    |    4         5.91    |     5.4
8    |    3         6.73    |     4.2
9    |    2         7.55    |     3.4
10   |    1         8.36    |     2.6
                    9.18    |     1.8
                    10.0    |     1.0

如您所见,这两个数据集的总体趋势是相同的。但是,如果我通过互相关函数(一般目标)运行这些值,我会得到一些不反映这一点的东西,因为数据集有两种不同的大小。

例如,如果您正在跟踪您每天跑了多少英里,那么现实世界的例子是:

在二月(28 天)的第一周,你每天跑一英里。在第二周,你每天跑两英里,等等。

在 3 月(31 天),你做同样的事情,但是跑一英里八天,跑两英里八天,跑三英里八天,跑四英里七天。

根据以下函数,相关系数应该几乎正好为 1:

class CrossCorrelator {

    def variance = { x->
        def v = 0
        x.each{ v += it**2}
        v/(x.size()) - (mean(x)**2)
    }

    def covariance = {x, y->
        def z = 0
        [x, y].transpose().each{ z += it[0] * it[1] }
        (z / (x.size())) - (mean(x) * mean(y))
    }
    def coefficient = {x, y->
        covariance(x,y) / (Math.sqrt(variance(x) * variance(y)))
    }
}
def i = new CrossCorrelator()
i.coefficient(y values, y2 values)

仅查看数据集,如果我要获取 1、2、3、4、5、6、7、8、9 和 10 的值,图表似乎完全相同,并且函数将产生更准确的结果。

但是,由于长度不同,因此它是倾斜的。

有没有办法定位十二值数据集中整数的值是什么?我还没有找到一种简单的方法来做到这一点,但这将非常有帮助。

提前致谢,

5

编辑:根据请求,这里是生成图形 X 值的代码:

def x  = (1..12) 
def y = 10

change = {l, size ->
    v = [1]
    l.each{
        v << ((((size-1)/(x.size() - 1)) * it) + 1)
    }
    v -= v.last()
    return v
}


change(x, y)

编辑:根据另一个请求不工作代码:

def normalize( xylist, days ) {
  xylist.collect { x, y -> [ x * ( days / xylist.size() ), y ] }
}

def change = {l, size ->
    def v = [1]
    l.each{
        v << ((((size-1)/(l.size() - 1)) * it) + 1)
    }
    v -= v.last()
    return v
}

def resample( list, min, max ) {
   // We want a graph with integer points from min to max on the x axis
  (min..max).collect { i ->
    // find the values above and below this point
    bounds = list.inject( [ a:null, b:null ] ) { r, p ->
      // if the value is less than i, set it in r.a
      if( p[ 0 ] < i )
        r.a = p
      // if it's bigger (and we don't already have a bigger point)
      // then set it into r.b
      if( !r.b && p[ 0 ] >= i )
        r.b = p
      r
    }
    // so now, bounds.a is the point below our required point, and bounds.b
    // Deal with the first case (where a is null, because we are at the start)
    if( !bounds.a )
      [ i, list[ 0 ][ 1 ] ]
    else {
      // so work out the distance from bounds.a to bounds.b
      dist = ( bounds.b[0] - bounds.a[0] )
      // And how far the point i is along this line
      r = ( i - bounds.a[0] ) / dist
      // and recalculate the y figure for this point
      y = ( ( bounds.b[1] - bounds.a[1] ) * r ) + bounds.a[1]
      [ i, y ]
    }
  }
}

def feb = [9, 3, 7, 23, 15, 16, 17, 18, 19, 13, 14, 8, 13, 12, 15, 6, 7, 13, 19, 12, 7, 3, 4, 15, 6, 17, 8, 19]
def march = [8, 12, 4, 17, 11, 15, 12, 8, 9, 13, 12, 7, 3, 4, 8, 2, 17, 19, 21, 12, 12, 13, 14, 15, 16, 7, 8, 19, 21, 14, 16]

//X and Y Values for February
z = [(1..28), change(feb, 28)].transpose()

//X and Y Values for March stretched to 28 entries
o = [(1..31), change(march, 28)].transpose()

o1 = normalize(o, 28)

resample(o1, 1, 28)

如果我将 o 变量声明中的“march”切换为 (1..31),则脚本运行成功。当我尝试使用“march”时,我得到“java.lang.NullPointerException: Cannot invoke method getAt() on null object”

另外:我尽量不直接复制代码,因为这是不好的做法,所以我更改的功能之一基本上做同样的事情,它只是我的版本。我最终也会着手重构其余部分。但这就是为什么它略有不同。

4

1 回答 1

2

好的......我们开始......这可能不是最干净的代码......

让我们首先生成两个分布,都从 1 到 10(在 y 轴上)

def generate( range, max ) {
  range.collect { i ->
    [ i, max * ( i / ( range.to - range.from + 1 ) ) ]
  }
}

// A distribution 10 elements long from 1 to 10
def e1 = generate( 1..10, 10 )
// A distribution 14 elements long from 1 to 10
def e2 = generate( 1..14, 10 )

所以现在,e1 和 e2 是:

[1.00,1.00], [2.00,2.00], [3.00,3.00], [4.00,4.00], [5.00,5.00], [6.00,6.00], [7.00,7.00], [8.00,8.00], [9.00,9.00], [10.00,10.00]
[1.00,0.71], [2.00,1.43], [3.00,2.14], [4.00,2.86], [5.00,3.57], [6.00,4.29], [7.00,5.00], [8.00,5.71], [9.00,6.43], [10.00,7.14], [11.00,7.86], [12.00,8.57], [13.00,9.29], [14.00,10.00]

分别(到2dp)。现在,使用上一个问题中的代码,我们可以将它们标准化为相同的 x 范围:

def normalize( xylist, days ) {
  xylist.collect { x, y -> [ x * ( days / xylist.size() ), y ] }
}

n1 = normalize( e1, 10 )
n2 = normalize( e2, 10 )

这意味着 n1 和 n2 是:

[1.00,1.00], [2.00,2.00], [3.00,3.00], [4.00,4.00], [5.00,5.00], [6.00,6.00], [7.00,7.00], [8.00,8.00], [9.00,9.00], [10.00,10.00]
[0.71,0.71], [1.43,1.43], [2.14,2.14], [2.86,2.86], [3.57,3.57], [4.29,4.29], [5.00,5.00], [5.71,5.71], [6.43,6.43], [7.14,7.14], [7.86,7.86], [8.57,8.57], [9.29,9.29], [10.00,10.00]

但是,正如您正确指出的那样,它们具有不同数量的样本点,因此无法轻松进行比较。

但是我们可以编写一个方法来遍历图中我们想要的每个点,喜欢两个最近的点,然后从这两个点的值中插入一个y值,如下所示:

def resample( list, min, max ) {
   // We want a graph with integer points from min to max on the x axis
  (min..max).collect { i ->
    // find the values above and below this point
    bounds = list.inject( [ a:null, b:null ] ) { r, p ->
      // if the value is less than i, set it in r.a
      if( p[ 0 ] < i )
        r.a = p
      // if it's bigger (and we don't already have a bigger point)
      // then set it into r.b
      if( !r.b && p[ 0 ] >= i )
        r.b = p
      r
    }
    // so now, bounds.a is the point below our required point, and bounds.b
    if( !bounds.a )             // no lower bound...take the first element
      [ i, list[ 0 ][ 1 ] ]
    else if( !bounds.b )        // no upper bound... take the last element
      [ i, list[ -1 ][ 1 ] ]
    else {
      // so work out the distance from bounds.a to bounds.b
      dist = ( bounds.b[0] - bounds.a[0] )
      // And how far the point i is along this line
      r = ( i - bounds.a[0] ) / dist
      // and recalculate the y figure for this point
      y = ( ( bounds.b[1] - bounds.a[1] ) * r ) + bounds.a[1]
      [ i, y ]
    }
  }
}    
final1 = resample( n1, 1, 10 )
final2 = resample( n2, 1, 10 )

现在,值final1final2是:

[1.00,1.00], [2.00,2.00], [3.00,3.00], [4.00,4.00], [5.00,5.00], [6.00,6.00], [7.00,7.00], [8.00,8.00], [9.00,9.00], [10.00,10.00]
[1.00,1.00], [2.00,2.00], [3.00,3.00], [4.00,4.00], [5.00,5.00], [6.00,6.00], [7.00,7.00], [8.00,8.00], [9.00,9.00], [10.00,10.00]

(显然,这里有一些舍入,所以 2d.p. 隐藏了它们不完全相同的事实)

呼……在那之后一定是在家的时间;-)

编辑

正如问题编辑中指出的那样,我的resample方法中有一个错误导致它在某些情况下失败......

我相信这已经在上面的代码中得到了修复,并且从给定的例子中:

def march = [8, 12, 4, 17, 11, 15, 12, 8, 9, 13, 12, 7, 3, 4, 8, 2, 17, 19, 21, 12, 12, 13, 14, 15, 16, 7, 8, 19, 21, 14, 16]
o = [ (1..31), march ].transpose()

// X values squeezed to be between 1 and 28 (instead of 1 to 31)
o1 = normalize(o, 28)

// Then, resample this graph so there are only 28 points
v = resample(o1, 1, 28)

如果您绘制原始的 31 个点 (in o) 和 28 个点的新图形 (in v),您会得到:

在此处输入图像描述

这看起来还不错。

我不知道该change方法应该做什么,所以我从这段代码中省略了它

于 2011-09-30T16:11:50.777 回答