【问题标题】:MATLAB/Octave plot markers above the line rather than on the lineMATLAB/Octave 绘图标记在线上方而不是在线
【发布时间】:2016-04-07 08:05:43
【问题描述】:

我想可视化一个函数的峰值,并且我想让它的标记出现在与它们关联的线的上方

我制作了一个已经有峰值的最小示例,问题是如何正确可视化标记:

y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2]
x = (1:length(y))
plot(x,y);

hold on;

peaks = [3 7];
plot(x(peaks), y(peaks), 'v', 'MarkerSize', 24);

print('-dpng', 'example.png', '-S640,480');

因此,标记显示在该行的中心,如下所示:

像这样仔细调整参数OFFSET可以达到我想要的结果:

plot(x(peaks), y(peaks)+OFFSET, 'v', 'MarkerSize', 24);

如下图,对于这个精确的例子OFFSET=2.56对导出的png有效,但是用交互式绘图和导出矢量图,又错了。

谁能推荐一种无需手动尝试/错误即可获得此结果的方法?

目前我正在使用带有 gnuplot 的 Octave 来导出到 latex+tikz,如果该解决方案可以在那里工作会很好。

在我的实际(更复杂)用例中,我将多条线一个接一个地绘制到同一个图中,并且 y 限制发生变化,因此不能轻易计算偏移量,因为标记大小不会随y 限制。

编辑:此外,我使用的是 semilogx 图,因此在图表内以 x/y 轴比例绘制线条会看起来失真。

【问题讨论】:

    标签: matlab plot gnuplot octave


    【解决方案1】:

    一种方法是使用annotations,但有一些缺点(见下文)。

    注释使您能够将各种图形对象放入图形中。关于它们的一件非常烦人的事情是它们在所谓的规范化坐标中工作, 它跨越整个图形窗口(不仅仅是绘图区域)并从 [0,0] 到 [1,1],迫使您首先转换为这些坐标。我写了一个简单的函数来做到这一点,前提是你的绘图比例是线性的(如果你想要对数,你必须修改这个函数):

    ## Convert from data coordinates to normalized figure coordinates.
    function [xf yf] = figcoords(xa, ya)
        axp = get(gca, "position");
        lf = axp(1);
        bf = axp(2);
        rf = lf + axp(3);
        tf = bf + axp(4);
    
        xl = xlim();
        yl = ylim();
        la = xl(1);
        ra = xl(2);
        ba = yl(1);
        ta = yl(2);
    
        xf = lf + (xa-la).*(rf-lf)./(ra-la);
        yf = bf + (ya-ba).*(tf-bf)./(ta-ba);
    endfunction
    

    解决了这个问题,您可以继续使用annotation 函数对绘图进行注释:

    y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2];
    x = (1:length(y));
    peaks = [3 7];
    
    ## Plot the data as you would normally
    plot(x,y);
    
    ## Plot peak markers (no `hold on` needed)
    [xp yp] = figcoords(peaks, y(peaks));    # Transform to figure coordinates
    for coords = [xp; yp]
        xpi = coords(1);
        ypi = coords(2);
        annotation("arrow", [xpi xpi], [ypi+eps ypi]);
    endfor
    

    Plot with annotated peaks

    在这里,我们实际上绘制了从顶部指向山峰的小箭头。 由于它们的高度非常小,我们只能看到箭头。 annotation 函数的参数是 x 和 y 坐标 箭头的端点。请注意,我们添加了一个小数字 (eps) 到起点的 y 值,使箭头指向下方。

    如果需要,您可以调整标记的外观,使其更具视觉吸引力:

    y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2];
    x = (1:length(y));
    peaks = [3 7];
    
    coloridx = get(gca, "ColorOrderIndex")
    peakcolor = get(gca, "ColorOrder")(coloridx,:);    # Save current plot colour
    
    plot(x,y);
    
    ## Plot peak markers
    [xp yp] = figcoords(peaks, y(peaks));
    for coords = [xp; yp]
        xpi = coords(1);
        ypi = coords(2);
        annotation("arrow", [xpi xpi], [ypi+eps ypi], "headstyle", "plain",...
            "color", peakcolor);
    endfor
    

    Plot with annotated peaks in the same color

    缺点

    尽管无论标记或绘图的大小如何,这种方法都可以正常工作,但也有一些缺点:

    • 首先,注释相对于图形窗口 是固定的,而不是绘图。 当您第一次显示绘图时这很好,但是一旦您缩放 或平移,对齐丢失:当情节移动时,标记保持在原位。 如果您不需要交互式绘图(例如,您只想将其导出为图像), 只需确保在添加注释之前设置绘图限制,您应该 没事。
    • 其次,与使用 plot 函数。例如,在我的电脑上,当用 七个带注释的峰,在标记出现之前大约需要一秒钟。 绘制具有数千个峰值的信号几乎是不可能的。

    【讨论】:

      【解决方案2】:

      关于 Matlab 部分,您可以自己绘制峰值标记。沿着这些方向(扩展你的例子):

      y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2]
      x = (1:length(y))
      figure, plot(x,y);
      leglengthx=0.2;
      leglengthy=0.5;
      hold on;
      
      peaks = [3 7];
      peaks_max=[10 24];
      
      for ii=1:2
         line([peaks(ii) peaks(ii)+leglengthx],[peaks_max(ii) peaks_max(ii)+leglengthy]);
         line([peaks(ii) peaks(ii)-leglengthx],[peaks_max(ii) peaks_max(ii)+leglengthy]);
         line([peaks(ii)-leglengthx peaks(ii)+leglengthx],[peaks_max(ii)+leglengthy peaks_max(ii)+leglengthy]);
      end
      
      plot(x(peaks), y(peaks), 'v', 'MarkerSize', 24);
      

      我已经添加了峰值的最大值,这不应该是自动提取的问题,以及控制标记三角形大小的两个变量。然后它只是为每个峰值绘制三条线。

      我不知道这将如何转换为 Octave。

      【讨论】:

      • 如注:peaks_max = y(peaks)
      【解决方案3】:

      画小三角形怎么样?

      y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2];
      x = (1:length(y));
      peaks = [3 7];
      plot(x,y);
      
      hold on; line([peaks(1) peaks(1)+0.2], [y(x==peaks(1)) y(x==peaks(1))+1], 'color','b')
      hold on; line([peaks(1) peaks(1)-0.2], [y(x==peaks(1)) y(x==peaks(1))+1], 'color','b')
      hold on; line([peaks(1)+0.2 peaks(1)-0.2], [y(x==peaks(1))+1 y(x==peaks(1))+1], 'color','b')
      
      hold on; line([peaks(2) peaks(2)+0.2], [y(x==peaks(2)) y(x==peaks(2))+1], 'color','b')
      hold on; line([peaks(2) peaks(2)-0.2], [y(x==peaks(2)) y(x==peaks(2))+1], 'color','b')
      hold on; line([peaks(2)+0.2 peaks(2)-0.2], [y(x==peaks(2))+1 y(x==peaks(2))+1], 'color','b')
      

      如果峰值的 y 值存在于向量上的其他位置,则可能会出现问题。如果是这样,您可以为 find 函数指定 first 或其他匹配规范。

      【讨论】:

      • 你的速度有点快;)
      • 一点点... :)
      • 这适用于 octave+gnuplot 并且已经很有帮助,但是现在三角形的纵横比会根据 y 和 x 限制的变化而变化。在我的真实用例中,我什至使用对数图,所以它完全扭曲了。还有其他想法吗? ...编辑:好的,我想我也可以使标记以指数方式绘制并根据轴进行缩放,只是有点复杂...
      • 好的,指数/缩放不是解决方案,因为我将独立绘制多个图
      • 嗯..您可以尝试按三角形尺寸进行操作。通过abs(diff(y)) 的高值获取基本长度,通过y(x==peaks(n)) 获取高度。因此,您可以将小三角形的长度和高度定义为这些长度的 1/5、1/10 或类似的东西..
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-11
      相关资源
      最近更新 更多