Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to set the color of a plotted line directly/manually? #1466

Closed
trllrgn opened this issue May 3, 2019 · 5 comments
Closed

How to set the color of a plotted line directly/manually? #1466

trllrgn opened this issue May 3, 2019 · 5 comments

Comments

@trllrgn
Copy link

trllrgn commented May 3, 2019

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:
#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.

@jakevdp
Copy link
Collaborator

jakevdp commented May 3, 2019

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.

@trllrgn
Copy link
Author

trllrgn commented May 16, 2019

Thanks, not sure how I missed that.

@trllrgn trllrgn closed this as completed May 16, 2019
@RajUnikie
Copy link

RajUnikie commented Jun 10, 2020

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.

@jakevdp
Copy link
Collaborator

jakevdp commented Jun 10, 2020

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.

@RajUnikie
Copy link

RajUnikie commented Jun 11, 2020

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants