Marker color not displaying correctly in legend

26 views (last 30 days)
I am plotting from a data set using for loops and multiple conditionals to control the shape, fill, and color of the markers. However when I try to add in a legend the colors do not transfer over correctly. The marker shape and fill are displayed correctly in my legend, just not the color. The bottom four elements in my legend should be red. Here is my code along with the graphical output. I'm sure there's a more elegant/efficient way to code this without all of these if statements but I'm only concerned with getting my legend working correctly. Many thanks. For reference, my code concerning the legend is at the very bottom.
massdata = xlsread('massdata.xlsx');
x = massdata(:,12);
y = massdata(:,15);
gamma = massdata(:,16);
ra = massdata(:,17);
gammadm = massdata(:,4);
s = 40;
for i = 1:1:32
if gamma(i) == 0.1 && ra(i) == 1 && gammadm(i) == 0
scatter(x(i), y(i), s, 'blue', 'd', 'MarkerFaceColor', 'blue');
hold on
end
if gamma(i) == 0.1 && ra(i) == 1 && gammadm(i) == 1
scatter(x(i), y(i), s, 'blue', 'd');
hold on
end
if gamma(i) == 0.1 && ra(i) == 0 && gammadm(i) == 0
scatter(x(i), y(i), s, 'blue', 's', 'MarkerFaceColor', 'blue');
hold on
end
if gamma(i) == 0.1 && ra(i) == 0 && gammadm(i) == 1
scatter(x(i), y(i), s, 'blue', 's');
hold on
end
if gamma(i) == 1 && ra(i) == 1 && gammadm(i) == 0
scatter(x(i), y(i), s, 'red', 'd', 'MarkerFaceColor', 'red');
hold on
end
if gamma(i) == 1 && ra(i) == 1 && gammadm(i) == 1
scatter(x(i), y(i), s, 'red', 'd');
hold on
end
if gamma(i) == 1 && ra(i) == 0 && gammadm(i) == 0
scatter(x(i), y(i), s, 'red', 's', 'MarkerFaceColor', 'red');
hold on
end
if gamma(i) == 1 && ra(i) == 0 && gammadm(i) == 1
scatter(x(i), y(i), s, 'red', 's');
hold on
end
hleg1 = legend('\gamma_s = 0.1, r_a = 1, \gamma_{DM} = 0',...
'\gamma_s = 0.1, r_a = 1, \gamma_{DM} = 1', ...
'\gamma_s = 0.1, r_a = 0, \gamma_{DM} = 0', ...
'\gamma_s = 0.1, r_a = 0, \gamma_{DM} = 1', ...
'\gamma_s = 1, r_a = 1, \gamma_{DM} = 0', ...
'\gamma_s = 1, r_a = 1, \gamma_{DM} = 1', ...
'\gamma_s = 1, r_a = 0, \gamma_{DM} = 0', ...
'\gamma_s = 1, r_a = 0, \gamma_{DM} = 1');
end
grid on;
  1 Comment
Walter Roberson
Walter Roberson on 19 Apr 2014
You should almost never compare floating point numbers for equality. 0.1 cannot be exactly represented in binary floating point, different routes of calculation might round up or down in storing the algebraic 1/10

Sign in to comment.

Answers (3)

Image Analyst
Image Analyst on 19 Apr 2014
I had that happen too in this question I answered I never did figure it out. It just seemed to pick one color and use that for everything.
  2 Comments
Colin
Colin on 19 Apr 2014
Is there no way to correct this short of photoshopping the correct colors into the legend?? It seems I shouldn't need to do that with a program with as many features as matlab
Walter Roberson
Walter Roberson on 19 Apr 2014
You could possibly find the legend axis and bash the marker colors within it.

Sign in to comment.


Geoff Hayes
Geoff Hayes on 19 Apr 2014
Hi Colin,
From your code, there are 32 iterations in the for loop. Do you expect that for each i, will at least one of the eight if conditions be satisfied with scatter being called? If yes, then scatter will be called 32 times. Note that I said "at least" but it appears that if one condition is satisfied then no other can be satisfied. If that is the case, then you may want to replace your code with if {…} elseif {…} blocks instead of separate if blocks to make that clear:
if gamma(i) == 0.1 && ra(i) == 1 && gammadm(i) == 0
scatter(x(i), y(i), s, 'blue', 'd', 'MarkerFaceColor', 'blue');
hold on
elseif gamma(i) == 0.1 && ra(i) == 1 && gammadm(i) == 1
scatter(x(i), y(i), s, 'blue', 'd');
hold on
elseif gamma(i) == 0.1 && ra(i) == 0 && gammadm(i) == 0
% etc. for the remaining 5 conditions too
end
Now the legend is within the for loop so it is being displayed/created 32 times and so should be moved outside the for loop so that it is only "instantiated" once when all scatters have been called.
Now my understanding of how the legend operation works is that if the call is something like
legend('text1','text2','text3');
then the text messages will be assigned according to the order of the updates (plots,lines,scatters, whatever) to the figure. Your legend has 8 strings for each of the eight different types of scatters that can be drawn on the figure. But the for loop iterates 32 times - so what guarantee is there that the first 8 scatters drawn correspond to the ordered 8 strings in the legend? Or maybe since up to 32 scatters are drawn, then the last 8 are considered for the legend. I modified your code to just iterate eight times and have each if block called once only (for each i) and then drew the legend and it came out okay with respect to shape AND colour. So it is possible to get the right assignment of colour and shape to the text string in the legend.
There may be two solutions:
1. Order all the data according the conditions that correspond to each of the eight if blocks and then execute each if block the once, calling scatter 8 times only. This would require some preprocessing which may be time-consuming but should guarantee that the 8 scatters correspond to the 8 strings in your legend. OR
2. Grab the handle for each scatter and then pass a vector of handles to the legend:
hdles = -1*ones(1,8); % set all eight handles to -1
for i = 1:1:32
if gamma(i) == 0.1 && ra(i) == 1 && gammadm(i) == 0
% save the handle for this scatter to the vector
hdles(1) = scatter(x(i), y(i), s, 'blue', 'd', 'MarkerFaceColor', 'blue');
hold on
end
if gamma(i) == 0.1 && ra(i) == 1 && gammadm(i) == 1
% save the handle for this scatter to the vector
hdles(2) = scatter(x(i), y(i), s, 'blue', 'd');
hold on
end
% etc.
So now each time we have called the scatter function we save its handle to the vector, perhaps overwriting previous handles but who cares since the scatter drawn will have the same shape and colour. Now when it comes time to showing the legend just do the following:
% pass the vector of handles where each one corresponds to the text to write out
legend(hdles, '\gamma_s = 0.1, r_a = 1, \g…..
And that should work. The only problem may be for the case where you don't have any data to display for one of the eight possible scatters. In that case, there will be a -1 within the hdles vector and so it and its corresponding text message should be removed before calling legend .
Hope that this helps!
Geoff

Walter Roberson
Walter Roberson on 19 Apr 2014
Edited: Walter Roberson on 19 Apr 2014
Following on from Geoff's analysis of how you could use if/elseif: there is a way to rewrite those kinds of "if" statements into a switch statement. The method is not immediately obvious. It works because switch evaluates each "case" rather than requiring them to be constant values. switch will stop looking as soon as it finds a match.
Rewrite:
if CONDITION1
code1
end
if CONDITION2
code2
end
and so on, to
switch true %yes, switch on a constant!
case CONDITION1:
code1
case CONDITION2:
code2
end
I am not fond of writing a switch this way, but sometimes it cleans up the code quite a bit.
I find that the majority of time that I would consider using this kind of switch statement, that the code can be transformed to use lookup tables or state tables. For example if you have three variables each of which has two states, then you have 8 possible situations: [F,F,F], [F,F,T], [F,T,F] and so on to [T,T,T]. Those can be transformed into a 3 bit number that can be converted to an integer: (C1)*4+(C2)*2+(C3) . Now you have 0 to 7 encoding which combination you are dealing with. Add 1 and you have 1 to 8, suitable for indexing arrays... e.g.,
FaceColors = {'blue', 'blue', 'green', 'red', 'blue', 'yellow', 'red', 'green'};
scatter(...., 'MarkerFaceColor', FaceColors{condition_index})

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!