Altair: How to set the color of a plotted line directly/manually?

Created on 3 May 2019  路  5Comments  路  Source: altair-viz/altair

I have found some examples where a color scale is defined and a category variable is chosen based on which the plots are colored. E.g. here:
https://github.com/altair-viz/altair/issues/921

I want the user to be able to plot various lines onto a LayerChart, and select the color manually for each line added (i.e. choose a color from a dropdown list, click plot, and add a new plot to the existing chart with the color chosen). How can I directly tell altair to plot using a certain color?

I tried:

lines = alt.Chart(df).mark_line().encode(
    x=alt.X(...),
    y=alt.Y(...),
    color='rgb(255,184,56)'
)

but this does not work.
Thanks.

Most helpful comment

Two ways to do this:

alt.Chart(df).mark_line(color="#FFAA00").encode(
  x='x',
  y='y'
)

or

alt.Chart(df).mark_line().encode(
  x='x',
  y='y',
  color=alt.value("#FFAA00")
)

The reason your first approach doesn't work is because it indicates you want to encode using a column named 'rgb(255,184,56)'.

See Customizing Visualizations for more information.

All 5 comments

Two ways to do this:

alt.Chart(df).mark_line(color="#FFAA00").encode(
  x='x',
  y='y'
)

or

alt.Chart(df).mark_line().encode(
  x='x',
  y='y',
  color=alt.value("#FFAA00")
)

The reason your first approach doesn't work is because it indicates you want to encode using a column named 'rgb(255,184,56)'.

See Customizing Visualizations for more information.

Thanks, not sure how I missed that.

Hi, I am using multi chart and there is no way to specify the legends directly. So as a hack, introduced a separate legend column to each of the charts. However, still stuck with giving colour of my own choice. Colour has to be consistent for different plots that will be drawn several times for different timeframes on the same page. Here is an example:

dataframe_1['Legend'] = "Speed_GPS"
base = alt.Chart(
            dataframe_1,
            title="Speed plot",
        ).interactive()
udf_chart = (
            base.mark_line(color="steelblue")
            .encode(
                x=alt.X("Timestamp", axis=alt.Axis(title="Timestamp")),
                y=alt.Y("Location Speed", axis=alt.Axis(title="Speed in m/s")),
                color='Legend'
            )
            .properties(width=2000)
        )
dataframe_2['Legend'] = "Speed_others"
sdf = alt.Chart(dataframe_2)
sdf_chart = sdf.mark_line(color="#F4D03F").encode(x="timestamp", y="speed",
                                                             color="Legend")
###(hb_events_df is another dataframe for the same time period)
hb_events_df['Legend'] = "Car Hard Brakes"
hb_dot = (
                alt.Chart(hb_events_df)
                .mark_circle(color="red", size=150)
                .encode(x="Timestamp", y="Location Speed", color="Legend")
            )
###(ra_events_df is another dataframe for the same time period)
ra_events_df['Legend'] = "Car Rapid Acceleration"
ra_dot = (
                alt.Chart(ra_events_df)
                .mark_circle(color="red", size=150)
                .encode(x="Timestamp", y="Location Speed", color="Legend")
            )
udf_chart + sdf_chart + hb_dot + ra_dot

The above plot gives different color each time and would like to know if there is a way to mention alt.value after color column. Thanks in advance.

You can specify a desired color scale using alt.Scale. See https://altair-viz.github.io/user_guide/customization.html#color-domain-and-range for some examples.

Thanks. It fit my purpose. Though I tried alt.Scale as suggested here, the color is not consistent for the plots called several times on the same page. Now it is. Probably I missed a common range/domain definition.

Here is my not so good looking code. Just in case it helps someone introduce legends to multi layer, though I don't recommend taking this path..

       domain = ["Speed 1", "Speed 2", "x", "y"]
        range_ = ["steelblue", "lightsalmon", "green", "red"]
        selector = alt.selection_single(fields=["Legend"], empty="all", bind="legend")
        dataframe_1["Legend"] = domain[0]
        base = alt.Chart(
            title="Some title",
        ).interactive()
        dataframe_2["Legend"] = domain[1]
        sdf = alt.Chart(dataframe_2.reset_index())
        udf_chart = (
            base.mark_line(color="steelblue")
            .encode(
                x=alt.X("timestamp", axis=alt.Axis(title="Timestamp")),
                y=alt.Y("speed", axis=alt.Axis(title="Speed in m/s")),
                color=alt.Color("Legend", scale=alt.Scale(domain=domain, range=range_)),
                opacity=alt.condition(selector, alt.value(1), alt.value(0)),
            )
            .add_selection(selector)
            .properties(width=2000)
        )
        sdf_chart = sdf.mark_line(color="lightsalmon").encode(
            x="timestamp",
            y="speed",
            color=alt.Color("Legend", scale=alt.Scale(domain=domain, range=range_)),
            opacity=alt.condition(selector, alt.value(1), alt.value(0)),
        )

        if x1.empty:
            x_lines = text_hb = None
        else:
            x1.loc[x1.index, "Legend"] = "xline"
            x_lines = (
                alt.Chart(x1.reset_index())
                .mark_rule(color="chartreuse", strokeWidth=1)
                .encode(x="timestamp")
            )
            text_x1 = x_lines.mark_text(align="left", baseline="top", y=ymax).encode(
                text="Legend"
            )

        if y1.empty:
            y_lines = text_ra = None
        else:
            y1.loc[y1.index, "Legend"] = "yline"
            y_lines = (
                alt.Chart(y1.reset_index())
                .mark_rule(color="lightpink", strokeWidth=2)
                .encode(x="timestamp")
            )
            text_y1 = y_lines.mark_text(align="left", baseline="top", y=ymax).encode(
                text="Legend"
            )

        if x.empty:
            x_dot = None
        else:
            x["Legend"] = domain[2]
            x_dot = (
                alt.Chart(x)
                .mark_circle(color="green", size=150)
                .encode(
                    x="timestamp",
                    y="speed",
                    color=alt.Color(
                        "Legend", scale=alt.Scale(domain=domain, range=range_)
                    ),
                )
            )

        if y.empty:
            y_dot = None
        else:
            y["Legend"] = domain[3]
            y_dot = (
                alt.Chart(y)
                .mark_circle(color="red", size=150)
                .encode(
                    x="timestamp",
                    y="speed",
                    color=alt.Color(
                        "Legend", scale=alt.Scale(domain=domain, range=range_)
                    ),
                )
            )

        chart_list = [
            "x_lines",
            "y_lines",
            "text_x1",
            "text_y1",
            "x_dot",
            "y_dot",
        ]
        to_plot = "udf_chart + sdf_chart"
        for item in chart_list:
            if eval(item) is not None:
                to_plot = to_plot + " + " + item
        st.altair_chart(eval(to_plot))

(Of course, I would like to avoid 'eval' .. work in progress)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nielsmde picture nielsmde  路  4Comments

tonylee3399 picture tonylee3399  路  3Comments

SuperShinyEyes picture SuperShinyEyes  路  3Comments

fischcheng picture fischcheng  路  4Comments

LukeMathWalker picture LukeMathWalker  路  3Comments