Search code examples
pythondataframealtair

Make only one value to be a unique shape in Altair point graph?


I'm using Altair to make a point graph where all the shapes are the same (circle) for each stock value except for one (let's say diamond).

In this dataset I want the stock symbol for "V" to be a unique shape.

ratio_df = pd.DataFrame(data={
  "symbol": {
    "0": "V",
    "1": "MA",
    "2": "FI"
  },
  "shortName": {
    "0": "Visa Inc.",
    "1": "Mastercard Incorporated",
    "2": "Fiserv, Inc."
  },
  "value": {
    "0": 31.446135,
    "1": 38.91105,
    "2": 30.7025
  }
})

chart = (
    alt.Chart(ratio_df)
        .mark_point()
        .encode(
            row=alt.Row("metric:N"),
            x=alt.X("value:Q"),
            y=alt.Y("symbol:N"),
            tooltip=alt.Tooltip(["shortName:N", "metric:N", "value:Q"]),
            color=alt.Color("shortName:N"),
            shape=alt.condition("datum.symbol == V", "diamond", "circle"),
        )
    )

I've tried:

  • encode(..., shape=alt.condition("datum.symbol == V", alt.value("diamond"), alt.value("circle")))
    • Cannot use alt.condition here
  • encode(..., shape=alt.Shape("symbol:N"))
    • Different shapes for each stock symbol; I need all the same except one
  • mark_point(..., shape=alt.Shape("symbol:N"))
    • Different shapes for each stock symbol
  • mark_point(..., shape=alt.condition("datum.symbol == V", alt.value("diamond"), alt.value("circle")))
    • Shape requires a string
  • https://github.com/altair-viz/altair/issues/1135#issuecomment-421415074
    • Only two bins; I could have 2 or more

Solution: ordering list as range

base_symbol: str = "V"  
marker_shape: list[str] = []
for distinct_symbol in ratio_df["symbol"].drop_duplicates().sort_values():
        if distinct_symbol == base_symbol:
            marker_shape.append("cross")
        else:
            marker_shape.append("circle")

chart = (
    alt.Chart(ratio_df)
        .mark_point()
        .encode(
            row=alt.Row("metric:N"),
            x=alt.X("value:Q"),
            y=alt.Y("symbol:N"),
            tooltip=alt.Tooltip(["shortName:N", "metric:N", "value:Q"]),
            color=alt.Color("shortName:N"),
            shape=alt.Shape("shortName", scale=alt.Scale(range=marker_shape)),
        )
    )
chart

Solution

  • I am not experienced with Altair, but I applied the examples on github to your question. Here for the stocks, each marker type is given as a scale range. I followed this and specified a list of squares and circles.

    chart = (
        alt.Chart(ratio_df)
            .mark_point()
            .encode(
                row=alt.Row("metric:N"),
                x=alt.X("value:Q"),
                y=alt.Y("symbol:N"),
                tooltip=alt.Tooltip(["shortName:N", "metric:N", "value:Q"]),
                color=alt.Color("shortName:N"),
                shape=alt.Shape("shortName", scale=alt.Scale(range=["circle","circle","diamond"])),
            )
        )
    chart
    

    enter image description here