Color and Legend Help in Plot

Hello,

I have a bunch of line graphs that you can sort by drug name. I want 8th grade to be green, 10th grade to be blue, and 12th grade to be red.

Initially I just did this

    color: {
      domain: ["8th Grade", "10th Grade", "12th Grade"],
      range: ["green", "blue", "red"],
      legend: true
    },

But for some graphs where I only have 12th grade data the legend would show 8th and 10th grade as well. I just wanted to show 12th grade in the legend. Is there an easy way to do this?

So I tried using the filtered data as the domain and I left the range the same.

    color: {
      domain: radio.grade,
      range: ["green", "blue", "red"],
      legend: true
    },

The problem here is that it makes 12th grade green instead of red. How would I go about keeping my same color scheme even when the domain changes from three grades to just one grade?

Here is the example notebook with both implementations.

Iā€™ve sent you a suggestion.

1 Like

Thank you!

Iā€™ve updated the suggestion with (I think) a more readable/editable format.

@Fil I am not able to see your suggestion. Would you be able to retrace what you did? This is the exact problem Iā€™m having.

Two years later, and @frscott has deleted their notebook, Iā€™m sorry I donā€™t remember what I suggested.

A possibility is to create the mapping like so:

colors = new Map([["8th Grade", "green"], ["10th Grade", "blue"], ["12th Grade", "red"]]);

and then use:

    color: {
      domain,
      range: Array.from(domain, (c) => colors.get(c)),
      legend: true
    },

Thanks for getting back to me on this. Your snippet has been a good help. Could you expand on the domain a little bit more in the color: section? I have a single data file that I filter in differently depending on the figure weā€™re trying to show. How would I declare the domain?

Iā€™ve written a minimum example of the type of problem Iā€™m dealing with:

// Define the color scheme
colors = new Map([
    ["8th Grade", "green"], 
    ["10th Grade", "blue"], 
    ["12th Grade", "red"]
]);

// Plot a filtered version of the data
Plot.plot({
    marks: [
        Plot.barY(
            data,
            Plot.groupX(
                {
                    y: "sum"
                },
                {
                    filter: d => d.grade != "10th Grade",
                    x: "x",
                    y: "y",
                    fill: "grade"
                }
            )
        ),
        Plot.ruleY([0])
    ],

    // Legend
    color: {
        domain,
        range: Array.from(domain, (c) => colors.get(c)),
        legend: true
    }
})

Which would return domain is not defined

Sure, you need to define the domain to the exact values you want to support in this chart (e.g. domain = [ā€œ8th gradeā€, ā€œ12th gradeā€]).

That makes sense. Is there a programmatic way to export the domain from the fill only after itā€™s been filtered?

I want to avoid hard coding domains because this process will be repeated frequently.

you can create a chart with the default color scale, then read that scaleā€™s domain:

Plot.dot(data, {fill: ā€œgradeā€}).plot().scale(ā€œcolorā€).domain

Note that the filter option does not change the colorā€™s domain; you will have to filter the data instead: Plot.dot(data.filter(ā€¦)), {fill: ā€œgradeā€}) etc.

I see, so it seems that my issue centers around filtering as an option instead of using a full on data filter. I think this is plenty to go off for now. Thanks for taking the time to contribute.

1 Like

Note that if you filter on the value (as in the pseudo example above), thereā€™s no reason to create a fake chart and throw it away, since you could filter on the domain directly; my first suggestion would be more performant in that case:

    color: {
        domain: domain.filter(d => d !== "8th grade"),
        range: Array.from(domain.filter(d => d !== "8th grade"), (c) => colors.get(c)),
        legend: true
    }