在MATLAB中找到非线性图解的最长线性部分

时间:2018-07-23 05:39:10

标签: matlab linear-regression

很长的道歉,但这需要一些解释。我正在尝试创建一个脚本,以找到绘图中最长的线性部分。样品数据在csv文件here中,它是用于计算3D打印样品的剪切模量的应力和应变数据。到目前为止,我的代码如下:

for region, df_region in df.groupby('Region'):
    print(df_region)

抱歉,代码没有得到很好的优化,我对MATLAB还是比较陌生。我没有使用导数,因为我的数据相对嘈杂,而导数可能会使情况变得更糟。

通过将数据分成x_data = []; y_data = []; x_data = Data(:,1); y_data = Data(:,2); plot(x_data,y_data); grid on; answer1 = questdlg('Would you like to load last attempt''s numbers?'); switch answer1 case 'Yes' [sim_slopes,reg_data] = regr_and_longest_part(new_x_data,new_y_data,str2num(answer2{3}),str2num(answer2{2}),K); case 'No' disp('Take a look at the plot, find a range estimate, and press any button to continue'); pause; prompt = {'Eliminate values ABOVE this x-value:','Eliminate values BELOW this x-value:','Size of divisions on x-axis:','Factor for similarity of slopes:'}; dlg_title = 'Point elimination'; num_lines = 1; defaultans = {'0','0','0','0.1'}; if isempty(answer2) < 1 defaultans = {answer2{1},answer2{2},answer2{3},answer2{4}}; end answer2 = inputdlg(prompt,dlg_title,num_lines,defaultans); uv_of_x_range = str2num(answer2{1}); lv_of_x_range = str2num(answer2{2}); x_div_size = str2num(answer2{3}); K = str2num(answer2{4}); close all; iB = find(x_data > str2num(answer2{1}),1,'first'); iS = find(x_data > str2num(answer2{2}),1,'first'); new_x_data = x_data(iS:iB); new_y_data = y_data(iS:iB); [sim_slopes, reg_data] = regr_and_longest_part(new_x_data,new_y_data,str2num(answer2{3}),str2num(answer2{2}),K); end [longest_section0, Midx]= max(sim_slopes(:,4)-sim_slopes(:,3)); longest_section=1+longest_section0; long_sec_x_data_start = x_div_size*(sim_slopes(Midx,3)-1)+lv_of_x_range; long_sec_x_data_end = x_div_size*(sim_slopes(Midx,4)-1)+lv_of_x_range; long_sec_x_data_start_idx=find(new_x_data >= long_sec_x_data_start,1,'first'); long_sec_x_data_end_idx=find(new_x_data >= long_sec_x_data_end,1,'first'); long_sec_x_data = new_x_data(long_sec_x_data_start_idx:long_sec_x_data_end_idx); long_sec_y_data = new_y_data(long_sec_x_data_start_idx:long_sec_x_data_end_idx); [b_long_sec, longes_section_reg_data] = robustfit(long_sec_x_data,long_sec_y_data); plot(long_sec_x_data,b_long_sec(1)+b_long_sec(2)*long_sec_x_data,'LineWidth',3,'LineStyle',':','Color','k'); function [sim_slopes,reg_data] = regr_and_longest_part(x_points,y_points,x_div,lv,K) reg_data = cell(1,3); scatter(x_points,y_points,'.'); grid on; hold on; uv = lv+x_div; ii=0; while lv <= x_points(end) if uv > x_points(end) uv = x_points(end); end ii=ii+1; indices = find(x_points>lv & x_points<uv); temp_x_points = x_points((indices)); temp_y_points = y_points((indices)); if length(temp_x_points) <= 2 break; end [b,stats] = robustfit(temp_x_points,temp_y_points); reg_data{ii,1} = b(1); reg_data{ii,2} = b(2); reg_data{ii,3} = length(indices); plot(temp_x_points,b(1)+b(2)*temp_x_points,'LineWidth',2); lv = lv+x_div; uv = lv+x_div; end sim_slopes = NaN(length(reg_data),4); sim_slopes(1,:) = [reg_data{1,1},0,1,1]; idx=1; for ii=2:length(reg_data) coff =sim_slopes(idx,1); if abs(reg_data{ii,1}-coff) <= K*coff C=zeros(ii-sim_slopes(idx,3)+1,1); for kk=sim_slopes(idx,3):ii C(kk)=reg_data{kk,1}; end sim_slopes(idx,1)=mean(C); sim_slopes(idx,2)=std(C); sim_slopes(idx,4)=ii; else idx = idx + 1; sim_slopes(idx,1)=reg_data{ii,1}; sim_slopes(idx,2)=0; sim_slopes(idx,3)=ii; sim_slopes(idx,4)=ii; end end end 个部分,然后对每个部分执行鲁棒拟合,我设法获得了代码,以找到绘图中最长的笔直部分,然后将结果写入其中x_div_size。然后,代码遍历reg_data,并通过计算曲线图某个部分的斜率平均值,找出由reg_data因子确定的斜率最相似的线,并在K。然后,它用sim_slopes找到最长的间隔,并对其进行回归以给出最终答案。

问题是它只会考虑遇到的第一个笔直部分。绘制数据时,它有几个部分看起来最直:

Plot of raw data

作为示例,当我使用max(sim_slopes(:,4)-sim_slopes(:,3))运行脚本时,得到以下信息,其中黑线是代码找到的最直的部分:

Longest straight part

我有以下问题:

  1. 很明显,从大约x = 0.04到x = 0.2,有一个很长的笔直部分,我不确定为什么脚本找不到它。在使用不同值的情况下,脚本似乎总是选择第一个最长的笔直部分,而忽略随后的部分。
  2. MATLAB抱怨answer2 = {'0.2','0','0.0038','0.3'}是因为要执行的回归超过50。有没有办法绕过Warning: Iteration limit reached.的限制?
  3. 在生成robustfit时,图中的某些部分的斜率可能与先前斜率的平均值相差太大,因此将其标记为长截面的结尾。但是该部分有时夹在两侧的其他几个部分之间,它们具有相似的斜率。怎么可能告诉脚本忽略一个任性的部分并继续下去,好像它在K值允许的公差之内?

2 个答案:

答案 0 :(得分:3)

看看the Douglas-Peucker algorithm。如果您将(x,y)值视为(开放)多边形的顶点,则此算法将为您简化它,以使从简化的多边形到原始多边形的最大距离小于您可以选择的某个阈值。简化的多边形将是一组直线。找到距离最远的两个顶点,就可以了。

MATLAB在“映射工具箱”中有一个名为reducem的实现。您可能还会在File Exchange上找到一个实现(但请注意,那里也确实存在错误的代码)。或者,您可以自己滚动,这是一个非常简单的算法。

答案 1 :(得分:1)

您还可以尝试使用ischange function来检测数据的截距和斜率的变化,然后从中提取最长的部分。

使用您提供的示例数据,这是我从基本尝试中看到的结果:

 >> T = readtable('Data.csv');
 >> T = rmmissing(T);  % Remove rows with NaN
 >> T = groupsummary(T,'Var1','mean'); % Average duplicate timestamps
 >> [tf,slopes,intercepts] = ischange(T.mean_Var2, 'linear', 'SamplePoints', T.Var1); % find changes
 >> plot(T.Var1, T.mean_Var2, T.Var1, slopes.*T.Var1 + intercepts)

生成情节

Example plot for ischange usage.

您应该能够根据find(tf)给出的索引提取最长的片段。

您还可以调整ischange的参数以获取更少或更多的细分。例如,添加值为4或5的名称-值对'MaxNumChanges'会产生更多的线性段,使其更紧密地拟合曲线,从而有效地消除了所看到的图中的扭结。