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.

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.

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
    }