Search code examples
pythonvegavega-litealtair

How to force Altair to order heatmap(rect) on a specific axis?


I'm trying to create a clustered heatmap in Altair. Creating the heatmap works just fine, but I cannot reorder the rows based on the value of another column.

Here is the code snippet I'm using to create the heatmap using seaborn:

import pandas as pd
import numpy as np
import seaborn as sns
import altair as alt
import random

iris = sns.load_dataset("iris")
species = iris.pop("species")

# Clustermap for rows only
g = sns.clustermap(iris, col_cluster=False, cmap="magma")

# Get the reodered indices
reordered_indices = g.dendrogram_row.reordered_ind

# Create a dictionary to add this information to the longform dataframe later
reordering_dict = pd.Series(reordered_indices, index=iris.index.values).to_dict()

# Converting iris to tidyform
iris.reset_index(level=0, inplace=True)
iris_tidy = pd.melt(iris, id_vars=["index"], var_name="Paramaeter", value_name="value")

# Adding the ordering information
iris_tidy['order'] = iris_tidy['index'].map(reordering_dict)

and this results in this: Clustermap using seaborn

Now while trying the same using Altair:

# Plotting using Altair
alt.Chart(iris_tidy, width=500, height=500).mark_rect().encode(
    alt.X("Paramaeter:N", bin=False, sort=None),
    alt.Y("order:O", bin=False),
    alt.Color("value:Q", scale=alt.Scale(scheme="magma")),
    order=alt.Order("order:Q", sort="ascending"),
).configure_scale(bandPaddingInner=0).configure_view(strokeOpacity=0, stroke="transparent").configure_axisY(
    labels=False, ticks=False
).configure_axisX(
    labelAngle=0, ticks=False
)

gives me this: Heatmap using altair

I believe I'm using alt.order() incorrectly. One way I assume to do it would be to use the ordinal column order itself for defining Y axis - but I will loose the labels associated with index.


Solution

  • If you want to control the sort order of the Y-axis categories, you can use the sort property of the y encoding. For example:

    # Plotting using Altair
    alt.Chart(iris_tidy, width=500, height=500).mark_rect().encode(
        alt.X("Paramaeter:N", bin=False, sort=None),
        alt.Y("index:O", sort=alt.EncodingSortField(field='order', order='ascending')),
        alt.Color("value:Q", scale=alt.Scale(scheme="magma")),
    ).configure_scale(bandPaddingInner=0).configure_view(strokeOpacity=0, stroke="transparent")
    

    enter image description here

    This does not match the seaborn output, but I believe that is because the computed order column does not reflect the order of the data in the seaborn chart.