2

我已将数值数据及其坐标(xValues,yValues)存储在列表中,如果我想将该组数据与另一组数据进行比较(加、减、除...),我必须知道我无法比较如果 xValues 不匹配(因为没有什么可比较的)。所以我需要在“缺失的”xValues 之间进行线性插值,这些xValues 实际存在于另一个集合中并生成新点。请检查这张图片:

在此处输入图像描述

红线上的青色方块代表存储的点(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 或 lambda 表达式,但我不习惯使用它们,而且我对这些主题缺乏了解。请注意,此操作将经常进行,因此我必须使其不太重。由于这个原因,我认为生成一个新列表而不是在原始列表的中间插入点是个好主意。

请如果有人可以指导我一点或告诉我是否有一个数学库实际上这样做会很棒。谢谢你。


编辑:对不起,如果我没有正确解释我。

这里我有一个例子(在 Excel 中完成):

在此处输入图像描述

请注意,我不能直接将 Series1 和 Series2 (+) 或任何其他操作相加,因为它们中的 X 间距不同。所以我想要的是在需要时在 Series1 中生成一个新点。

为此,我想简单地使用线性插值。假设我在 series1 中有 P1(0,40) 和 P2(0,60),但在 series2 中有一个点 (1,10)。我需要使用(1,50)坐标在 P1 和 P2 之间生成一个点 P3。

在此处输入图像描述

在此处输入图像描述

我试图用 SkipWhile 执行此操作并比较两个系列的下一个 X 值,如果 series1 的 XValue 较低,则在 newSeries 中添加该 XValue 和相应的 YValue。否则使用 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 方法来做这件事真是太棒了。但我不知道如何在谓词中编写该条件。

4

2 回答 2

2

首先,我可能会使用包含xy坐标的适当“点”类,而不是每个坐标的两个单独列表。然后您可以使用该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)));

这使得从输入数据计算一组新点变得容易。如果您只关心单个 y 值,您仍然可以使用当前数据执行此操作,它看起来会很奇怪:

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

如果在编写此答案的过程中我以某种方式破坏了您的数学,我深表歉意。


更新

现在我对你想要做的事情有了更好的了解,我认为任何 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();
    }
}
于 2013-06-14T23:20:40.750 回答
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

    }
}
于 2013-06-17T21:21:07.077 回答