How do I plot a sine wave with markers?
Show older comments
Answers (2)
Hi @Jia
Teach you a trick. When data points are densely packed, individual markers can overlap. You can use MarkerIndices to display markers at fewer points rather than at every data point.
x = linspace(0, 2, 2001);
y = sinpi(x);
plot(x, y, '-o', 'LineWidth', 1.5, 'MarkerIndices', 1:50:length(y), 'MarkerSize', 6, 'MarkerFaceColor', 'red')
grid on
xlabel('x')
ylabel('y')
5 Comments
%plot(x,y,'-o','LineWidth',1.5,'MarkerIndices',1:50:length(y),...
is useful, indeed. I've wondered why, while they were at it didn't introduce 'MarkerSpacing' or some other way to just supply the spacing between instead of having to build another vector explicitly.
@Sam Chak - I think your comment is deserving to be an Answer so I moved it... dpb :>)
Hi @dpb
At first, I also wondered like you, why not introduce a MarkerSpacing property like Python’s markevery, where you provide a single integer, n, to draw a marker at every nth point. Then I asked myself, “What if I want to use the markers to indicate the neighborhoods of local extrema?” A vector is useful in that case because it specifies the exact data-point locations where the markers should appear.
Here’s the code snippet (not so perfect at the valley) I came up with after studying the example here:
% a dense data
x = linspace(0, 2, 2001);
y = sinpi(x);
% define a neighborhood radius around the extrema (xmax and xmin)
idxmax = find(y == max(y));
xmax = x(idxmax); % location of local maximum, x = 0.5
idxmin = find(y == min(y));
xmin = x(idxmin); % location of local minimum, x = 1.5
radius = 0.2; % radius around the extrema, R = 0.2
idxRmax = find(abs(x - xmax) <= radius);
idxRmin = find(abs(x - xmin) <= radius);
% combine indices
markEvery = 50; % Marker spacing
marker_idx = [idxRmax, idxRmin];
marker_idx = marker_idx(1:markEvery:end);
% plot using MarkerIndices
plot(x, y, '-o', ...
'LineWidth', 1.5, ...
'MarkerIndices', marker_idx, ...
'MarkerSize', 6, ...
'MarkerFaceColor', 'red');
grid on;
xlabel('x');
ylabel('sin(\pi x)');
title('Markers Concentrated around Local Extrema');
ylim([-1.2, 1.2]);
I've wondered why, while they were at it didn't introduce 'MarkerSpacing'
What would you expect MarkerSpacing to do if one or both of the axes rulers were in log space rather than linear space? Use distance on the screen or distance in data space?
What would you expect it to do if you plotted using a datetime array for the X coordinates and double for the Y coordinates? What's the linear distance between two points that are 3 days (aka 72 hours, 4320 minutes, or 259200 seconds) apart on the X axis and 10 apart on the Y axis?
Would you expect MarkerSpacing to measure along one or both of the axes rulers or along the arc length of the curve? If the latter (which I suspect would be your expectation) how would this handle gaps in the line created by plotting missing data (or the aforementioned datetime X data, double Y data)?
h = plot(datetime('today') + days(0:9), [1:5 NaN 7:10].^2);
ax = ancestor(h, 'axes');
ax.YScale = 'log';
yticks((1:10).^2)
If I used MarkerSpacing = 10 for the above plot call, what should it do? Where should the markers be? What if I used MarkerSpacing = days(1)?
Sometimes one of the answers to "Why didn't MathWorks do <something simple / straightforward>?" is "It's easy to define/specify the behavior in the simple/straightforward cases, but quite difficult (or perhaps even impossible) to generalize."
I'd probably treat it like or similar to the Python markevery that includes some other options/interpretations to deal with such oddball cases. If it doesn't do what pleases the user in some cases, so be it; they can revert to specifying each individual point desired.
Python also allows a fractional value that serves in the log space case at least for "normal" axes. The one kicker with it I don't think right is that the single N serves to do all so afaict with it there is no way to put just one marker. That surely isn't right, but why I thought would be the second single purpose input. CORRECTION: Python can distinguish N as an integer and [N] as a list so this covers that case, but aren't distinct in MATLAB as simply returning a 1x1 array.
Agree it's no real big deal and if one is going to carry the implementation to extremes for every edge case can dream up, then it's undoubtedly better off to not bother at all.
For the example, I'd expect it to do the same thing as--
t=datetime('today') + days(0:9);
h = plot(t, [1:5 NaN 7:10].^2,'-x','MarkerIndices',[1:10:numel(t)]);
ax = ancestor(h, 'axes');
ax.YScale = 'log';
yticks((1:10).^2)
which only generates one marker. That's on the user since "every 10" --> [1 11 ...] and there are only 10 points to begin with. I'd say in these cases it's probably rarely possible to know a priori just where the mrkers ought to be and it'll take inspection and adjustments to create something appropriate for the given figure. Here, 10 obviously isn't a good choice; with only ten points to start with, 2, 3 or maybe 4 would be a better guess--
figure
h = plot(t, [1:5 NaN 7:10].^2,'-x','MarkerIndices',[1:2:numel(t)]);
ax = ancestor(h, 'axes');
ax.YScale = 'log';
yticks((1:10).^2)
ADDENDUM
On reflection, if one were to implement the alternate form, the Python-like option using a starting index and difference would make it much more flexible.
Again, one can always produce the same result by explicit calculation of course, just a shorthand syntax.
% Define parameters
f = 2; % Frequency in Hz
t = linspace(0, 1, 30); % Time vector (30 points from 0 to 1 second)
% Calculate sine wave
y = sin(2 * pi * f * t);
% Create plot with line style and markers
% 'o-' specifies a solid line with circle markers
figure;
plot(t, y, 'o-', 'LineWidth', 1.5, 'MarkerSize', 6, 'MarkerFaceColor', 'red');
% Customizing the plot
title('Sine Wave with Markers at Every Point');
xlabel('Time (s)');
ylabel('Amplitude');
grid on;
Categories
Find more on Annotations in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!




