Procyon
Procyon

Reputation: 11

Select most unique markers, linestyles and colours

i would like to automatise the selection of line styles, markers and colours. I would like to have a function like this:

function [selected_lineStyles, selected_markers, selected_colours] = plot_lineStyle_markers_and_colours(i)
    linestyles = {'-', '--', ':', '-.'};
    colours = {'red', [0.0, 220.0, 0.0]./255.0, 'blue', 'black', [255.0, 180.0, 0.0]./255.0, [0.0, 128.0, 0.0]./255.0, [0.0, 255.0, 255.0]./255.0, [1.0, 0.0, 1.0]};
    markers = {'d', '*', '+', 'x', 'o', 's', '^', 'v', '.', '>', '<', 'p', 'h'};
    num_different_lineStyles = numel(linestyles);
    num_different_markers = numel(markers);
    num_different_colours = numel(colours);
    num_different_combinations = num_different_markers*num_different_lineStyles*num_different_colours;
    if any(i(:) > num_different_combinations)
        warning('Not enough unique combinations of line styles, markers and colours available!');
    end
    i = 1 + mod(i - 1, num_different_combinations);
    [lineStyle_i, marker_i, colour_i] = ind2sub([num_different_lineStyles, num_different_markers, num_different_colours], i);
    selected_lineStyles = linestyles(lineStyle_i);
    selected_markers = markers(marker_i);
    selected_colours = colours(colour_i);
end

For

i = 1:4

this function returns different linestyles, but the same markers and colours.

But I would like to have it like this: For i = 1, it should return the first linestyle, the first marker and the first colour. For i = 2, it should return the second linestyle, the second marker and the second colour. Same with i = 3 and i = 4. For i = 5, the linestyle has to be repeated for the first time, because there are only 4 of them. However, the function should return the fifth colour and the fifth marker.

In general, the "most unique" combinations of linestyle, marker and colour should be returned for low i, so that in plots they are easy to distinguish.

I have no idea how to phrase, or even solve, this problem mathematically. I guess one has to walk along the diagonals of an n-dimensional, non-square array.

Can somebody help me with this or point me into the right direction?

Upvotes: 1

Views: 50

Answers (2)

Procyon
Procyon

Reputation: 11

I noticed that there is something called the distance of an array element to the main array diagonal, and that I want to have the indices in ascending order of this distance. For a 3D subscript index A, this distance is defined as sum(abs([A(1) - A(2), A(2) - A(3), A(3) - A(1)])). Right now I use this function:

function subs_sorted_by_dist_from_main_diagonal = subs_sorted_by_dist_from_main_diagonal(siz)
    d = numel(siz);
    n = prod(siz);
    i = arrayfun(@(n)1:n, siz, 'UniformOutput', false);
    sub = cell(1, d);
    [sub{:}] = ndgrid(i{:});

    sub2 = zeros(n, d);
    for j = 1:d
        sub2(:, j) = sub{j}(:);
    end

    dist_from_main_diagonal = sum(abs(sub2 - sub2(:, [2:end, 1])), 2);

    [~, sort_index] = sort(dist_from_main_diagonal, 'ascend');

    subs_sorted_by_dist_from_main_diagonal = sub2(sort_index, :);
end

which is not optimal, because it sorts all subscript indices, instead of generating them without sorting.

Upvotes: 0

craigim
craigim

Reputation: 3914

I misunderstood the question the first time, and I had to think about it a bit.

If you're willing to change your lists by adding or subtracting a color or linestyle, then you can do it by taking advantage of the fact that the number of elements in each of your three lists is coprime. If you have two coprime numbers N and M, then the least common multiple will be N×M. If you have a third number P that is coprime with each other two, then the least common multiple is N×M×P, etc.

What that means for your lists is that if you increment each index at each step over and over, you won't repeat a combination for a very long time. You've got 4 line styles, 8 colors, and 13 markers. If you add a color (e.g. neutral grey [0.5 0.5 0.5]), you have 4, 9, and 13, which are all coprime.

% Define your combinations, with neutral grey added to the end of the color choices.
linestyles = {'-', '--', ':', '-.'};
colours = {'red', [0.0, 220.0, 0.0]./255.0, 'blue', 'black', [255.0, 180.0, 0.0]./255.0, [0.0, 128.0, 0.0]./255.0, [0.0, 255.0, 255.0]./255.0, [1.0, 0.0, 1.0], [0.5 0.5 0.5]};
markers = {'d', '*', '+', 'x', 'o', 's', '^', 'v', '.', '>', '<', 'p', 'h'};

% Count the values in each cell array 
ldx = numel(linestyles); % = 4
cdx = numel(colours); % = 9
mdx = numel(markers); % = 13

% Make each cycle the same length
LDX = repmat(1:ldx, [1 cdx*mdx]);
CDX = repmat(1:cdx, [1 ldx*mdx]);
MDX = repmat(1:mdx, [1 ldx*cdx]);

x = 1:10
y = x;
for idx = 1:numel(LDX)
    % Pick out the current combination
    currentLineStyle = LDX(idx);
    currentMarker = MDX(idx);
    currentColour = CDX(idx);

    % Plot your data here...
    line(x, y, 'Color', currentColour, 'LineStyle', currentLineStyle, 'Marker', currentMarker)
end

Each property will now cycle, and you won't get, say, a second set of red solid lines until 36 lines later, and it will have a different line style than the first. Without the extra color in there, that number would be 8.

I don't have MATLAB at home, so I can't print out a figure this time, but I think it will do what you want it to.

Upvotes: 2

Related Questions