2013-06-14 3 views
2

Я сохранил численные данные в списках с его координатами (xValues, yValues), и если я хочу сравнить (добавить, вычесть, делить ...), что набор данных для другой я должен знать, что я не могу сравнивать, если xValues ​​не совпадают (потому что с чем сравнивать нечего). Поэтому мне нужно интерполировать линейно между «отсутствующими» xValues, которые фактически существуют в другом наборе и генерируют новые точки. Пожалуйста, проверьте эту картину:Сравнение двух наборов данных (x, y)

enter image description here

бирюзового квадраты на красной линии представляют собой сохранённые точки (xValues2), и (обычно) они не будут соответствовать другим Установим xValues ​​(xValues1). Два квадрата на зеленой линии являются примерами желаемых сгенерированных точек. С ними я могу работать с этими двумя графиками без проблем.

Для линейной интерполяции Это довольно просто: если у меня есть две точки (x0, y0) и (x1, y1), и я хочу, чтобы добавить новую точку между ними дали «x2»:

y2=y0+(x2-x0)*(y1-y0)/(x1-x0) 

Чтобы сделать эту работу, я думаю, что мне нужно реализовать что-то вроде этого:

  1. Создать новые списки (xValuesNew, yValuesNew).
  2. Сделайте соединение между xValues1 и xValues2 (xValuesNew).
  3. Проверьте, какие различия между исходными xValues1 и xValuesNew.
  4. Для каждого найденного нового значения генерируйте «y», используя формулу, написанную выше.
  5. Поместите это 4 шага в метод и используйте его снова, но теперь с помощью set2.

Я был на этом весь день, пытаясь найти легкое решение, возможно, используя выражения Linq или лямбда-выражения, но я не привык к работе с ними, а также отсутствие знаний по этим темам. Обратите внимание, что эта операция будет выполняться довольно часто, поэтому я должен сделать ее не слишком тяжелой. Я думал, что будет хорошей идеей генерировать новый список, вместо этого вставляя точки в середине оригинала по этой причине.

Пожалуйста, если кто-нибудь может мне немного поправить или рассказать мне, есть ли математическая библиотека, на самом деле это будет здорово. Спасибо.


EDIT: Извините, если я не объяснил мне должным образом.

Здесь у меня есть пример (сделано в Excel):

enter image description here

Обратите внимание, что я не могу напрямую добавлять вместе Series1 и Series2 (+) или любой другой операции, так как расстояние между X в них другой. Поэтому я хочу создать новую точку в Series1, когда это необходимо.

Для этого я хотел бы просто использовать линейную интерполяцию. Скажем, что у меня есть P1 (0,40) и P2 (0,60) в серии 1, но в серии 2 у меня есть точка (1,10). Мне нужно создать точку P3 между P1 и P2 с (1,50) координатами.

enter image description here

enter image description here

Я пытался сделать это с SkipWhile и сравнивая следующее значение X из обеих серий, если XValue из Series1 ниже, а затем добавить, что XValue и соответствующие YValue в newSeries. Else использовать XValue2 для генерации Y и добавить его в newSeries. Вот одна из моих попыток (не работает):

List<double> series1X = new List<double> { 0, 2, 4, 6, 8 }; 
     List<double> series1Y = new List<double> { 120, 100, 110, 105, 70 }; 
     List<double> series2X = new List<double> { 0, 1, 7, 8,9 }; 

     List<double> newSeries1X = new List<double>(); 
     List<double> newSeries1Y = new List<double>(); 

    double lastX1 = series1X[series1X.Count()-1]; 
     int i = 0; 

     while (next1X <= lastX1) 
     { 

      next2X = series2X.SkipWhile(p => p <= next1X).First(); 
      Console.WriteLine(next2X.ToString()); 

      if (next1X <= next2X) 
      { 

       newSeries1X.Add(series1X[i]); 
       newSeries1Y.Add(series1Y[i]); 
      } 

      if (next2X < next1X) 
      { 

       while (next2X < next1X) 
       { 
        newSeries1X.Add(next2X); 
        newY = series1Y[i] + (next2X - series1X[i]) * (series1Y[i + 1] - series1Y[i])/(series1X[i + 1] - series1X[i]); 
        newSeries1Y.Add(newY); 

        next2X = series2X.SkipWhile(p => p <= next2X).First(); 
       } 
      } 


      next1X = series1X.SkipWhile(p => p <= next2X).First(); 
      Console.WriteLine(next1X.ToString()); 
      i++; 

     } 

Было бы здорово сделать это с помощью метода Zip. Но я не знаю, как написать это условие в предикате.

ответ

2

Во-первых, я бы, вероятно, использовать соответствующий класс «точки», который содержит как х и у координаты вместо двух отдельных списков для каждой координаты. После этого вы можете использовать метод Zip быстро перебирать их:

IEnumerable<PointF> points0 = ... 
IEnumerable<PointF> points0 = ... 
float x2 = ... 
IEnumerable<PointF> newPoints = point0.Zip(points1, 
    (p0, p1) => new PointF(p0.X, p0.Y + (x2-p0.X) * (p1.Y-p0.Y)/(p1.X-p0.X))); 

Это позволяет легко вычислить новый набор точек из входных данных. Если вы просто заботитесь об одном у-значении, вы можете сделать это с текущими данными, она будет просто выглядеть странно:

IEnumerable<double> y2values = 
    xValues1.Zip(yValues1, (x, y) => new { x, y }).Zip(
     xValues2.Zip(yValues2, (x, y) => new { x, y }), 
     (p0, p1) => p0.y + (x2-p0.x) * (p1.y-p0.y)/(p1.x-p0.x)); 

Я извинить, если в процессе кодирования этого ответа я как-то исковерканные математику ,


Update

Теперь, когда у меня есть лучшее понимание о том, что вы пытаетесь сделать, я не думаю, что любой метод Linq будет работать совсем правильно. Вот то, что я придумал с использованием индексов:

List<double> series1X = new List<double> { 0, 2, 4, 6, 8 }; 
List<double> series1Y = new List<double> { 120, 100, 110, 105, 70 }; 
List<double> series2X = new List<double> { 0, 1, 7, 8, 9 }; 

// in the worst case there are n + m new points 
List<double> newSeries1X = new List<double>(series1X.Count + series2X.Count); 
List<double> newSeries1Y = new List<double>(series1X.Count + series2X.Count); 

int i = 0, j = 0; 
for (; i < series1X.Count && j < series2X.Count;) 
{ 
    if (series1X[i] <= series2X[j]) 
    { 
     newSeries1X.Add(series1X[i]); 
     newSeries1Y.Add(series1Y[i]); 
     if (series1X[i] == series2X[j]) 
     { 
      j++; 
     } 
     i++; 
    } 
    else 
    { 
     int k = (i == 0) ? i : i - 1; 
     // interpolate 
     double y0 = series1Y[k]; 
     double y1 = series1Y[k + 1]; 
     double x0 = series1X[k]; 
     double x1 = series1X[k + 1]; 
     double y = y0 + (y1 - y0) * (series2X[j] - x0)/(x1 - x0); 
     newSeries1X.Add(series2X[j]); 
     newSeries1Y.Add(y); 
     j++; 
    } 
} 
for (; i < series1X.Count; i++) 
{ 
    newSeries1X.Add(series1X[i]); 
    newSeries1Y.Add(series1Y[i]); 
} 
for (; j < series2X.Count; j++) 
{ 
    // interpolate 
    double y0 = series1Y[i - 2]; 
    double y1 = series1Y[i - 1]; 
    double x0 = series1X[i - 2]; 
    double x1 = series1X[i - 1]; 
    double y = y0 + (y1 - y0) * (series2X[j] - x0)/(x1 - x0); 
    newSeries1X.Add(series2X[j]); 
    newSeries1Y.Add(y); 
} 

Выход

newSeries1X = { 0, 1, 2, 4, 6, 7, 8, 0 } 
newSeries1Y = { 120, 110, 100, 110, 105, 87.5, 70, 52.5 } 

Это решение обрабатывает случаи, когда первый series2X[0] < series1X[0] и когда series2X[n] > series1X[m] линейно «проецировании» данные наружу от первого/последняя пара точек.

Вот еще одно решение с использованием счетчиков (в основном), но это не так элегантно, как я надеялся. Это, вероятно, может быть улучшен немного:

bool hasS1 = true, hasS2 = true, preinterp = true; 
double x0 = 0, y0 = 0, x1 = 0, y1 = 0, x = 0, y = 0; 
using(var s1xEnumerator = series1X.GetEnumerator()) 
using(var s1yEnumerator = series1Y.GetEnumerator()) 
using(var s2xEnumerator = series2X.GetEnumerator()) 
{ 
    hasS1 = s1xEnumerator.MoveNext(); 
    hasS2 = s2xEnumerator.MoveNext(); 
    s1yEnumerator.MoveNext(); 
    while(hasS1 && hasS2) 
    { 
     x1 = s1xEnumerator.Current; 
     y1 = s1yEnumerator.Current; 
     x = s2xEnumerator.Current; 

     if (x1 <= x) 
     { 
      newSeries1X.Add(x1); 
      newSeries1Y.Add(y1); 
      hasS1 = s1xEnumerator.MoveNext(); 
      s1yEnumerator.MoveNext(); 
      preinterp = false; 
      if (hasS1) 
      { 
       x0 = x1; 
       y0 = y1; 
      } 
      if (x1 == x) 
      { 
       hasS2 = s2xEnumerator.MoveNext(); 
      } 
     } 
     else 
     { 
      // we have to look ahead to get the next interval to interpolate before x0 
      if (preinterp) 
      { 
       x0 = x1; 
       y0 = y1; 
       x1 = series1X[1]; // can't peek with enumerator 
       y1 = series1Y[1]; 
       preinterp = false; 
      } 

      y = y0 + (y1 - y0) * (x - x0)/(x1 - x0); 
      newSeries1X.Add(x); 
      newSeries1Y.Add(y); 
      hasS2 = s2xEnumerator.MoveNext(); 
     } 
    } 

    while(hasS1) 
    { 
     newSeries1X.Add(s1xEnumerator.Current); 
     newSeries1Y.Add(s1yEnumerator.Current); 
     hasS1 = s1xEnumerator.MoveNext(); 
     s1yEnumerator.MoveNext(); 
    } 

    while(hasS2) 
    { 
     x = s2xEnumerator.Current; 
     y = y0 + (y1 - y0) * (x - x0)/(x1 - x0); 
     newSeries1X.Add(x); 
     newSeries1Y.Add(y); 
     hasS2 = s2xEnumerator.MoveNext(); 
    } 
} 
+0

Извините, если я не понимаю ваш ответ, но x2 не является постоянной величиной. Он задается точками, которые существуют в одном наборе, но не в другом. Поэтому сначала я думаю, что мне приходится сравнивать оба ряда пунктов ... Кстати, это очень интересный метод. Благодарю. – Sturm

+0

@Sturm Боюсь, я не совсем понимаю ваш вопрос. Но вы можете захотеть изучить метод 'Except' вместо' Union' для вычисления 'x2'.На самом деле это проще с 'doubleles', чем с точками (вам нужен пользовательский' IEqualityComparer') –

+0

Здравствуйте, еще раз, я обновил вопрос с дополнительной информацией. Хотела бы вам взглянуть. Спасибо – Sturm

0

Для работы с двумя рядами с разным шагом я сначала нужно создавать точки в первом наборе, а затем во втором (с тем же самым способом) и, наконец, точка суммы, чтобы указать ,

Вот код для этого метода:

using OxyPlot.Series; 
using OxyPlot; 

namespace Algorithm1 
{ 

    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      List<DataPoint> S1 = new List<DataPoint>(); 
      List<DataPoint> S2 = new List<DataPoint>(); 
      List<DataPoint> NS1 = new List<DataPoint>(); 

      S1.Add(new DataPoint(4, 10)); 
      S1.Add(new DataPoint(6, 20)); 
      S1.Add(new DataPoint(8, 15)); 
      S1.Add(new DataPoint(9, 70)); 
      S1.Add(new DataPoint(10, 5)); 

      S2.Add(new DataPoint(1, 0)); 
      S2.Add(new DataPoint(2, 0)); 
      S2.Add(new DataPoint(3, 0)); 
      S2.Add(new DataPoint(6, 0)); 
      S2.Add(new DataPoint(7, 0)); 
      S2.Add(new DataPoint(8.1, 0)); 
      S2.Add(new DataPoint(8.2, 0)); 
      S2.Add(new DataPoint(8.3, 0)); 
      S2.Add(new DataPoint(8.4, 0)); 
      S2.Add(new DataPoint(9, 0)); 
      S2.Add(new DataPoint(9.75, 0)); 
      S2.Add(new DataPoint(11, 0)); 
      S2.Add(new DataPoint(12, 0)); 
      S2.Add(new DataPoint(16, 0)); 

      NS1 = GetMiddlePoints(S1, S2); 
      foreach (DataPoint pointin NS1) 
      { 
       MessageBox.Show(point.X.ToString()+" : "+ point.Y.ToString()); 
      } 


     } 

     #region GetMiddlePoints 
     private List<DataPoint> GetMiddlePoints(List<DataPoint> S1, List<DataPoint> S2) 
     { 
      List<DataPoint> NS1 = new List<DataPoint>(); 

      int i = 0; 
      int j = S2.TakeWhile(p => p.X < S1[0].X).Count(); 


      int PointsInS1 = S1.Count; 
      int PointsInS2 = S2.Count; 

      DataPoint newPoint = new DataPoint(); 


      while (i < PointsInS1) 
      { 
       if (j < PointsInS2) 
       { 
        if (S1[i].X < S2[j].X) 
        { 
         newPoint = S1[i]; 
         NS1.Add(newPoint); 
         i++; 
        } 

        else if (S1[i].X == S2[j].X) 
        { 
         newPoint = S1[i]; 
         NS1.Add(newPoint); 
         i++; 
         j++; 
        } 

        else if (S1[i].X > S2[j].X) 
        { 
         newPoint .X = S2[j].X; 
         newPoint .Y = S1[i-1].Y + (S2[j].X - S1[i-1].X) * (S1[i].Y - S1[i-1].Y)/(S1[i].X - S1[i-1].X); 
         NS1.Add(newPoint); 
         j++; 

        } 

       } 

       if (j == PointsInS2) 
       { 
        newPoint = S1[i]; 
        NS1.Add(newPoint); 
        i++; 
       } 


      } 


      return NS1; 

     } 

     #endregion 

    } 
} 
Смежные вопросы