Как выбрать значения Y в позиции X в Groovy? - PullRequest
1 голос
/ 30 сентября 2011

это своего рода математический вопрос ...

До этого у меня был вопрос о нормализации месячных данных: Как получить значения 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 днями) в течение первой недели вы пробегаете одну милю в день.В течение второй недели вы пробегаете две мили каждый день и т. Д.

В марте (с 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)

Если я переключаю «march» в объявлении переменной oв (1..31), скрипт выполняется успешно.Когда я пытаюсь использовать «march», я получаю «java.lang.NullPointerException: невозможно вызвать метод getAt () для нулевого объекта»

Также: я стараюсь не копировать код только потому, что это плохая практика, поэтомуодна из функций, которые я изменил, в основном делает то же самое, это просто моя версия.В конце концов, я тоже перейду к рефакторингу всего остального.Но вот почему это немного отличается.

1 Ответ

2 голосов
/ 30 сентября 2011

Хорошо ... мы идем ... это может быть не самый чистый бит кода когда-либо ...

Давайте сначала сгенерируем два распределения, оба от 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]

соответственно (до 2дп). Теперь, используя код из предыдущего вопроса, мы можем нормализовать их в том же диапазоне х:

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 )

сейчас значения final1 и final2:

[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. скрывает тот факт, что они не точно одинаковы)

Фу ... Должно быть после этого домой; -)

EDIT

Как указывалось в редактировании вопроса, в моем методе 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 точку (в o) и новый график из 28 точек (в v), вы получите:

enter image description here

Что выглядит не так уж и плохо.

Я понятия не имею, что должен делать метод change, поэтому я пропустил его в этом коде

...