执行一个“在线”线性插值。

huangapple go评论76阅读模式
英文:

Performing an "online" linear interpolation

问题

我有一个问题,需要对从传感器获取的一些数据进行线性插值(它实际上是位置数据,但数据的性质并不重要)。我现在在Matlab中进行这个操作,但由于最终将将此代码迁移到其他语言,我希望尽可能保持代码简单,不使用任何复杂的Matlab特定/内置函数。

我的实现初始看起来还可以,但是当我将我的工作与Matlab的内置interp1函数进行对比时,似乎我的实现并不完美,我不知道为什么。以下是我在已经完全收集的数据集上使用的代码,但当我循环遍历数据时,我表现得好像只有当前样本和上一个样本,这反映了我最终将面临的问题。

%创建一些虚拟数据
np = 109; % x和y的数据点数
x_data = linspace(3, 98, np) + (normrnd(0.4, 0.2, [1, np]));
y_data = normrnd(2.5, 1.5, [1, np]);

%定义数据将进行插值的查询点
qp = [1:100];

kk = 2; %索引数据
cc = 1; %索引查询点

qpi = qp(cc); %qpi是循环中的当前查询点
y_interp = qp * nan; %这将保存我们的解决方案

while kk <= length(x_data)
    kk = kk + 1; %更新数据计数器
    
    %执行在线插值
    if cc < length(qp) - 1
        if qpi >= y_data(kk - 1) %查询点当然必须位于x_data的当前值和下一个值之间
            y_interp(cc) = myInterp(x_data(kk - 1), x_data(kk), y_data(kk - 1), y_data(kk), qpi);
        end
        
        if qpi > x_data(kk) %如果当前查询点已经大于当前样本,请更新样本
            kk = kk + 1;
        else %否则,更新查询点以确保它在下一次迭代的样本之间
            cc = cc + 1;
            qpi = qp(cc);
            
            %如果x_data的变化大于查询点的分辨率,类似上面的更新将无法工作。在这种情况下,我们必须滞后数据
            if qpi < x_data(kk)
                kk = kk - 1;
            end
        end
    end
end

%获取正确的插值
y_interp_correct = interp1(x_data, y_data, qp);

%绘制两种解决方案以显示差异
figure;
plot(y_interp, 'displayname', 'manual-solution'); hold on;
plot(y_interp_correct, 'k--', 'displayname', 'matlab solution');
leg1 = legend('show');
set(leg1, 'Location', 'Best');
ylabel('interpolated points');
xlabel('query points');

请注意,“myInterp”函数如下所示:

function yi = myInterp(x1, x2, y1, y2, qp)
%线性插值函数y(x)在查询点qp上
yi = y1 + (qp - x1) * ((y2 - y1) / (x2 - x1));
end

这是显示我的实现不正确的图表:

执行一个“在线”线性插值。

有人能帮助我找到错误在哪里吗?为什么?我怀疑与确保查询点位于前一个和当前x样本之间有关,但我不确定。

英文:

I have a problem where I need to do a linear interpolation on some data as it is acquired from a sensor (it's technically position data, but the nature of the data doesn't really matter). I'm doing this now in matlab, but since I will eventually migrate this code to other languages, I want to keep the code as simple as possible and not use any complicated matlab-specific/built-in functions.

My implementation initially seems OK, but when checking my work against matlab's built-in interp1 function, it seems my implementation isn't perfect, and I have no idea why. Below is the code I'm using on a dataset already fully collected, but as I loop through the data, I act as if I only have the current sample and the previous sample, which mirrors the problem I will eventually face.

%make some dummy data
np = 109; %number of data points for x and y
x_data = linspace(3,98,np) + (normrnd(0.4,0.2,[1,np]));
y_data = normrnd(2.5, 1.5, [1,np]);

 %define the query points the data will be interpolated over
qp = [1:100];

kk=2; %indexes through the data
cc = 1; %indexes through the query points

qpi = qp(cc); %qpi is the current query point in the loop
y_interp = qp*nan; %this will hold our solution

while kk&lt;=length(x_data)
    kk = kk+1; %update the data counter
    
    %perform online interpolation
    if cc&lt;length(qp)-1
        if qpi&gt;=y_data(kk-1) %the query point, of course, has to be in-between the current value and the next value of x_data
            y_interp(cc) = myInterp(x_data(kk-1), x_data(kk), y_data(kk-1), y_data(kk), qpi);
        end
        
        if qpi&gt;x_data(kk), %if the current query point is already larger than the current sample, update the sample
            kk = kk+1;
        else %otherwise, update the query point to ensure its in between the samples for the next iteration
            cc = cc + 1;
            qpi = qp(cc);
            
            %It is possible that if the change in x_data is greater than the resolution of the query
            %points, an update like the above wont work. In this case, we must lag the data
            if qpi&lt;x_data(kk),
                kk=kk-1;
            end
        end
    end
end

%get the correct interpolation
y_interp_correct = interp1(x_data, y_data, qp);

%plot both solutions to show the difference
figure;
plot(y_interp,&#39;displayname&#39;,&#39;manual-solution&#39;); hold on;
plot(y_interp_correct,&#39;k--&#39;,&#39;displayname&#39;,&#39;matlab solution&#39;);
leg1 = legend(&#39;show&#39;);
set(leg1,&#39;Location&#39;,&#39;Best&#39;);
ylabel(&#39;interpolated points&#39;);
xlabel(&#39;query points&#39;);

Note that the "myInterp" function is as follows:

function yi = myInterp(x1, x2, y1, y2, qp)
%linearly interpolate the function value y(x) over the query point qp
yi = y1 + (qp-x1) * ( (y2-y1)/(x2-x1) );
end

And here is the plot showing that my implementation isn't correct 执行一个“在线”线性插值。

执行一个“在线”线性插值。

Can anyone help me find where the mistake is? And why? I suspect it has something to do with ensuring that the query point is in-between the previous and current x-samples, but I'm not sure.

答案1

得分: 2

您的代码问题在于有时使用超出界限 x_data(kk-1)x_data(kk) 的值来调用 myInterp,这会导致无效的外推结果。

您循环遍历 kk 而不是 cc 的逻辑对我来说非常令人困惑。我会使用一个简单的 for 循环来遍历 cc,这些是您要插值的点。对于这些点中的每一个,如果必要,前进 kk,以使 qp(cc) 位于 x_data(kk)x_data(kk+1) 之间(如果您喜欢,也可以使用 kk-1kk,只需初始化 kk=2 以确保 kk-1 存在,我只是觉得从 kk=1 开始更直观)。

为了简化这里的逻辑,我限制了 qp 中的值在 x_data 的限制内,这样我们就不需要测试以确保 x_data(kk+1) 存在,也不需要测试 x_data(1)<qp(cc)。如果您愿意,可以添加这些测试。

这是我的代码:

qp = [ceil(x_data(1)+0.1):floor(x_data(end)-0.1)];
y_interp = qp*nan; % 这将保存我们的解决方案

kk=1; % 索引数据
for cc=1:numel(qp)
   % 将 kk 前进到我们可以进行插值的位置
   % (这个循环保证不会越界,因为 x_data(end)>qp(end),
   %    但如果在循环之前不确保这一点,则需要进行调整)
   while x_data(kk+1) < qp(cc)
      kk = kk + 1;
   end
   % 执行在线插值
   y_interp(cc) = myInterp(x_data(kk), x_data(kk+1), y_data(kk), y_data(kk+1), qp(cc));
end

正如您所看到的,这种方式的逻辑要简单得多。结果与 y_interp_correct 完全相同。内部的 while x_data... 循环具有与您的外部 while 循环相同的目的,是您从数据源处读取数据的地方。

英文:

The problem in your code is that you at times call myInterp with a value of qpi that is outside of the bounds x_data(kk-1) and x_data(kk). This leads to invalid extrapolation results.

Your logic of looping over kk rather than cc is very confusing to me. I would write a simple for loop over cc, which are the points at which you want to interpolate. For each of these points, advance kk, if necessary, such that qp(cc) is in between x_data(kk) and x_data(kk+1) (you can use kk-1 and kk instead if you prefer, just initialize kk=2 to ensure that kk-1 exists, I just find starting at kk=1 more intuitive).

To simplify the logic here, I'm limiting the values in qp to be inside the limits of x_data, so that we don't need to test to ensure that x_data(kk+1) exists, nor that x_data(1)&lt;pq(cc). You can add those tests in if you wish.

Here's my code:

qp = [ceil(x_data(1)+0.1):floor(x_data(end)-0.1)];
y_interp = qp*nan; % this will hold our solution

kk=1; % indexes through the data
for cc=1:numel(qp)
   % advance kk to where we can interpolate
   % (this loop is guaranteed to not index out of bounds because x_data(end)&gt;qp(end),
   %    but needs to be adjusted if this is not ensured prior to the loop)
   while x_data(kk+1) &lt; qp(cc)
      kk = kk + 1;
   end
   % perform online interpolation
   y_interp(cc) = myInterp(x_data(kk), x_data(kk+1), y_data(kk), y_data(kk+1), qp(cc));
end

As you can see, the logic is a lot simpler this way. The result is identical to y_interp_correct. The inner while x_data... loop serves the same purpose as your outer while loop, and would be the place where you read your data from wherever it's coming from.

huangapple
  • 本文由 发表于 2020年1月4日 01:57:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/59583179.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定