Remove colorscale from last row and column of a plotly imshow / heatmap

Viewed 342

I have a matrix with which i am plotting the following plot.

enter image description here

As one could see the total row and column part of the plot dominates the colorscale which in turn diminishes the visual effect of other values. My goal is to remove the color scale from last row and column but i am unsure how to do it.

Here is my current plot code

def annotate_matrix_values(df: pd.DataFrame, fig: go.Figure) -> go.Figure:
    df_matrix = df.to_records(index=False)
    for i, r in enumerate(df_matrix):
        for k, c in enumerate(r):
            fig.add_annotation(
                x=k,
                y=i,
                text=f"<b>{str(int(c))}</b>" if not math.isnan(c) else "",
                showarrow=False,
            )
    fig.update_xaxes(side="top")
    fig.update_coloraxes(showscale=False)
    return fig
  

df_final = pd.crosstab(pt[y_axis_name], pt[x_axis_name], margins=True)

fig = px.imshow(
            df_final,
            labels=dict(x=x_title, y=y_title, color="Value"),
            x=x_label,
            y=y_label,
            color_continuous_scale="Purples",
            aspect="auto",
        )
fig = annotate_matrix_values(df_final, fig)

Of course i can set margin false here and get the following plot. But then I cant show the totals.

df_final = pd.crosstab(pt[y_axis_name], pt[x_axis_name], margins=False)

enter image description here

So my goal is to show the totals but without any color scale on it. Thanks for the help

2 Answers

I solved the problem with this example:

import pandas as pd
import plotly.graph_objects as go
import math

df = pd.DataFrame({'animal' : ['fish','fish','fish','monkey','tiger'], 
                   'zoo':['Paris','Grenoble','Paris','Toulose','Paris']})
df_cross = pd.crosstab(df.animal,df.zoo, margins=True)

enter image description here

df_matrix = df_cross.to_records(index=False)

fig = px.imshow(df_cross, color_continuous_scale="Purples")

for i, r in enumerate(df_matrix):
    for k, c in enumerate(r):
        fig.add_annotation(
                x=k,
                y=i,
                text=f"<b>{str(int(c))}</b>" if not math.isnan(c) else "",
                font_color = "white",
                showarrow=False,
            )

fig.show()

enter image description here

Add these line to solve the problem:

fig.layout['coloraxis']['cauto']=False 
fig.layout['coloraxis']['cmax'] = fig.data[0]['z'][:-1,:-1].max()
fig.show()

enter image description here

The color scale is adjusted by the maximum value of rows and columns, excluding the values of the total row and column. Values in the total row and column, which are greater than the maximum, are colored with the same color of the maximum value.

With the understanding that the purpose of the question was to exclude totals from the color scale, I tried various approaches with subplots and overlaying a blank graph and heatmap, but did not get good results. The simplest approach was to add a quadrangle and annotation to the EXPRESS heatmap. The data was created in the form of x,y with categorical variables. The heatmap was created with data not including totals, and the annotations were created with the totals as strings.

import plotly.graph_objects as go
import plotly.express as px
import numpy as np
import pandas as pd

data = np.random.randint(0,10,60).reshape(12,5)

cols = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
rows = ['Jan.','Feb.','Mar.','Apr.','May','June','July','Aug.','Sept.','Oct.','Nov.','Dec.']

df = pd.DataFrame(data, columns=cols, index=rows)
df['month_sum'] = df.sum(axis=1)
df.loc['week_sum'] = df.sum(axis=0)

fig = px.imshow(
    df.iloc[:-1,:-1].values,
    labels=dict(x="Day of Week", y="Year of Month", color="Value"),
    x=cols,
    y=rows,
    color_continuous_scale="Purples",
    aspect="auto",
    text_auto='auto'
        )

for i,r in enumerate(df['month_sum']):
    fig.add_shape(type='rect',
                  x0=4.5, y0=-0.5+i, x1=5.5, y1=0.5+i,
                  line=dict(
                      color='rgb(188,189,220)',
                      width=1,
                  ),
                  fillcolor='white',
                 )
    fig.add_annotation(
        x=5,
        y=i,
        text=str(r),
        showarrow=False
    )

for i,c in enumerate(df.loc['week_sum'].tolist()):
    if i == 5:
        break
    fig.add_shape(type='rect',
                  x0=-0.5+i, y0=12.5, x1=0.5+i, y1=11.5,
                  line=dict(
                      color='rgb(188,189,220)',
                      width=1,
                  ),
                  fillcolor='white',
                 )
    fig.add_annotation(
        x=i,
        y=12.0,
        text=str(c),
        showarrow=False
    )

fig.add_annotation(
    x=1.0,
    y=1.04,
    xref='paper',
    yref='paper',
    text='Month Total',
    showarrow=False,
)

fig.add_annotation(
    x=-0.16,
    y=0.02,
    xref='paper',
    yref='paper',
    text='Week Total',
    showarrow=False,
)

fig.update_xaxes(side="top")
fig.update_layout(
    autosize=False,
    width=600,
    height=600,
    coloraxis=dict(showscale=False)
                 )

fig.show()

enter image description here

Related