我已经将数据存储在列表中,其坐标(xValues,yValues),如果我想比较(加,减,除......)那组数据到另一个,我必须知道我可以不比较xValues是否匹配(因为没有什么可比较的)。所以我需要在“缺失的”xValues之间线性插值,这些xValues实际存在于另一组中并生成新的点。请查看此图片:
红线上的青色方块表示存储的点(xValues2),并且(通常)它们与其他的xValues(xValues1)不匹配。绿线上的两个正方形是所需生成点的示例。有了它们,我可以毫无问题地使用这两张图。
对于线性插值这很简单:如果我有两个点(x0,y0)和(x1,y1)并且我想在它们之间添加一个给定“x2”的新点:
y2=y0+(x2-x0)*(y1-y0)/(x1-x0)
为了完成这项工作,我想我必须实现这样的事情:
我整天都在这,试图找到一个简单的解决方案,可能使用Linq或lambda表达式,但我不习惯使用它们,而且我对这些主题缺乏了解。请注意,此操作将经常进行,因此我必须使其不太重。我认为生成一个新列表而不是在原始中间插入点是个好主意。
如果有人可以指导我一点,或者告诉我是否有数学库实际上这样做会很好。谢谢。
这里我有一个例子(在Excel中完成):
请注意,我不能直接将Series1和Series2(+)或任何其他操作添加在一起,因为它们中的X间距不同。所以我想要的是在需要时在Series1中生成一个新点。
为此,我想简单地使用线性插值。假设我在系列1中有P1(0,40)和P2(0,60),但在连续2中我有一个点(1,10)。我需要在P1和P2之间用(1,50)坐标生成一个点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方法执行此操作真是太棒了。但我不知道如何在谓词中写出这个条件。
答案 0 :(得分:2)
首先,我可能会使用一个合适的'point'类,它包含 x 和 y 坐标,而不是每个坐标的两个单独列表。然后,您可以使用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();
}
}
答案 1 :(得分: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
}
}