c# - calculate average function of several functions -
i have several ordered list of x/y pairs , want calculate ordered list of x/y pairs representing average of these lists.
all these lists (including "average list") drawn onto chart (see example picture below).
i have several problems:
- the different lists don't have same amount of values
- the x , y values can increase , decrease , increase (and on) (see example picture below)
i need implement in c#, altought guess that's not important algorithm itself.
sorry, can't explain problem in more formal or mathematical way.
edit: replaced term "function" "list of x/y pairs" less confusing.
i use method justin proposes, 1 adjustment. suggests using mappingtable fractional indices, though suggest integer indices. might sound little mathematical, it's no shame have read following twice(i'd have too). suppose point @ index in list of pairs has searched closest points in list b, , closest point @ index j. find closest point in b a[i+1] should consider points in b index equal or larger j. j + 1, j or j + 2, j + 3 etc, never below j. if point closest a[i+1] has index smaller j, still shouldn't use point interpolate with, since result in unexpected average , graph. i'll take moment create sample code you. hope see optimalization makes sense.
edit: while implementing this, realised j not bounded below(by method described above), bounded above. when try distance a[i+1] b[j], b[j+1], b[j+2] etc, can stop comparing when distance a[i+1] b[j+...] stops decreasing. there's no point in searching further in b. same reasoning applies when j bounded below: if point elsewhere in b closer, that's not point want interpolate with. doing result in unexpected graph, less smooth you'd expect. , bonus of second bound improved performance. i've created following code:
ienumerable<tuple<double, double>> average(list<tuple<double, double>> a, list<tuple<double, double>> b) { if (a == null || b == null || a.any(p => p == null) || b.any(p => p == null)) throw new argumentexception(); func<double, double> square = d => d * d;//squares argument func<int, int, double> euclidiandistance = (a, b) => math.sqrt(square(a[a].item1 - b[b].item1) + square(a[a].item2 - b[b].item2));//computes distance a[first argument] b[second argument] int previousindexinb = 0; (int = 0; < a.count; i++) { double distance = euclidiandistance(i, previousindexinb);//distance between a[i] , b[j - 1], (int j = previousindexinb + 1; j < b.count; j++) { var distance2 = euclidiandistance(i, j);//distance between a[i] , b[j] if (distance2 < distance)//if it's closer checked point, keep searching. otherwise stop search , return interpolated point. { distance = distance2; previousindexinb = j; } else { break;//don't place yield return statement here, because go wrong @ end of b. } } yield return linearinterpolation(a[i], b[previousindexinb]); } } tuple<double, double> linearinterpolation(tuple<double, double> a, tuple<double, double> b) { return new tuple<double, double>((a.item1 + b.item1) / 2, (a.item2 + b.item2) / 2); }
for information, function average returns same amount of interpolated points list contains, fine, should think specific application. i've added comments in clarify details, , i've described aspects of code in text above. hope it's clear, , otherwise feel free ask questions.
second edit: misread , thought had 2 lists of points. have created generalised function of above accepting multiple lists. still uses principles explained above.
ienumerable<tuple<double, double>> average(list<list<tuple<double, double>>> data) { if (data == null || data.count < 2 || data.any(list => list == null || list.any(p => p == null))) throw new argumentexception(); func<double, double> square = d => d * d; func<tuple<double, double>, tuple<double, double>, double> euclidiandistance = (a, b) => math.sqrt(square(a.item1 - b.item1) + square(a.item2 - b.item2)); var firstlist = data[0]; (int = 0; < firstlist.count; i++) { int[] previousindices = new int[data.count];//the indices of points closest previous point firstlist[i - 1]. //(or 0 if == 0). kept track of per list, except first list. var closests = new tuple<double, double>[data.count];//an array of points used caching, of average yielded. closests[0] = firstlist[i]; (int listindex = 1; listindex < data.count; listindex++) { var list = data[listindex]; double distance = euclidiandistance(firstlist[i], list[previousindices[listindex]]); (int j = previousindices[listindex] + 1; j < list.count; j++) { var distance2 = euclidiandistance(firstlist[i], list[j]); if (distance2 < distance)//if it's closer checked point, keep searching. otherwise stop search , return interpolated point. { distance = distance2; previousindices[listindex] = j; } else { break; } } closests[listindex] = list[previousindices[listindex]]; } yield return new tuple<double, double>(closests.select(p => p.item1).average(), closests.select(p => p.item2).average()); } }
actually did specific case 2 lists separately might have been thing: explained , offers step before understanding generalised version. furthermore, square root taken out, since doesn't change order of distances when sorted, lengths.
third edit: in comments became clear there might bug. think there none, aside mentioned small bug, shouldn't make difference except @ end of graphs. proof works, result of it(the dotted line average):
Comments
Post a Comment