Heat Maps with Same Scale Dash Plotly

Hello. I learned the basics of plotly dash and am using it to create a dashboard to present data to my research group. However, I am stuck in a problem and would greatly appreciate it if you could assist me.

  1. I have six heat map plots for each of the possible drop-down options, but I need them all to be on the same scale. in the image attached. Some are in the 20-40 range, while others are in the 0-40 range. and the value range varies depending on the metric.

  2. I have a drop-down menu of different metrics. How can I have different colors for each drop-down?

Data and Code
I’ve attached the data and the Python script. I humbly request that you just check it once and give me the way I can get the same scale for the six plots that appear on the screen and the different color of each metric in the drop down

Thanks in Advance
Jaslam

Hello @mJaslam

To control the bounds of the colorscale in color space, use zmin and zmax (plotly.graph_objects.Heatmap — 5.13.0 documentation)

For the dropdown you have to play with the CSS style

Hi @mJaslam welcome to the forums.

What do you mean by

Are you referring to a different colormap such as viridis, cividis…?

With the first question, I think you should find the max value and min value of your data and then pass it to zmin and zmax as Abdel’s suggestions. Something as below:
For second question, I think you should add one more Dropdown, and then use value from other Dropdown to return its value and then pass it to colorscale setting of your fig.

Full code as below:

import dash
import dash_html_components as html
import plotly.graph_objects as go
import dash_core_components as dcc
from dash.dependencies import Input, Output
import pandas as pd
import dash_bootstrap_components as dbc
import numpy as np
import plotly.express as px
import dash_table



#############################################################################################################################################
df1 = pd.read_excel('2022_RGT.xlsx')

#####################
colorscales = px.colors.named_colorscales()

#############################################################################################################################################
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])

PLOTLY_LOGO = "https://ifc.nkn.uidaho.edu/static/img/ifc_logo_official.png"

app.title = 'IFC RGT DASH'
server = app.server


navbar = dbc.Navbar( id = 'navbar', children = [
    dbc.Row([
        dbc.Col(html.Img(src = PLOTLY_LOGO, height = "100px")),
        dbc.Col(
            dbc.NavbarBrand('#UNDER CONSTRUCTION', style = {'color':'RED', 'fontSize':'20px','fontFamily':'Lato','textAlign':'center'}
                            ), align="center"

            )
        ],align = "center"
        # no_gutters = True
        ),


    ], color = '#ffffff')

card_content_dropdwn = [
    dbc.CardBody(
        [
            html.H4('DASHBOARD', style = {'textAlign':'center','color':'#ffffff'}),

            dbc.Row([

                dbc.Col([

                    html.H6('Year',style = {'color':'#ffffff'}),

                    dcc.Dropdown( id = 'dropdown_year',
        options = [
            {'label':i, 'value':i } for i in df1['Year'].unique()

            ],
        value = 2022,

        )


                    ]),

           dbc.Col([

               html.H6('Installation',style = {'color':'#ffffff'}),

               dcc.Dropdown( id = 'dropdown_Installation',
   options = [
       {'label':i, 'value':i } for i in df1['Installation'].unique()

       ],
   value = 'HOODOO',

   )


               ]),

    dbc.Col([

            html.H6('Metric',style = {'color':'#ffffff'}),

            dcc.Dropdown( id = 'dropdown_metric',
options = [
    {'label':i, 'value':i } for i in df1['Metric'].unique()

    ],
value = 'CAL22SP (mm)',

)


            ]),
                    dbc.Col([

            html.H6('Colorscale',style = {'color':'#ffffff'}),

            dcc.Dropdown( id = 'dropdown_colorscale',
options = colorscales,
value = [],

)


            ]),

                ])

            ]
        )



    ]




body_app = dbc.Container([

    #Link
    html.Div(id = 'link_div',children = [html.A(id = 'link', children = 'IFC HOMEPAGE', href = 'https://www.uidaho.edu/cnr/intermountain-forestry-cooperative', style = {'fontSize':15})],\
             style = {'textAlign':'right','font-family':'sans-serif'}),

        html.H2(id = 'H1', children = 'REALIZED GENETIC GAIN TRIALS',style = {'textAlign':'center','fontWeight':'bold','font-family':'sans-serif','width':'100%','color':'#16467a'}),

        html.P('(Relationship of Woods Run vs Improved Douglas-fir to Site Growth Factors)',style = {'textAlign':'center','font-family':'sans-serif','width':'100%','color':'#16467a','fontSize':'20px'}),


    dbc.Row([html.Div(html.P( 'A study on the differential effects of site type on woods run and improved Douglas-fir growth and mortality. The proposed study is designed to meet the needs of IFC members identified in the Spring 2020 Research Survey.  In that survey, IFC members listed Stand Establishment as their primary silvicultural research need, with understanding site effects on growth and mortality as number one priority.  IFC members further wished that the IFC and IETIC could find common research areas to collaborate.  We have developed a study plan to meet these expressed research needs, one which will contain three study components:  1) evaluation of site growth factors on seedling survival and growth, 2) realized gains in survival and growth as a function of first-generation improved seed, and 3) assess seedling performance following transfer from warmer/drier to cooler/moister climes.'),)],style = {'color':'#4F4F4F','font-family':'sans-serif', 'margin':5}),


    dbc.Row([
        dbc.Col([dbc.Card(card_content_dropdwn,style={'height':'100px'},color = '#16467a')],width=6),
        ], justify="center",),


    html.Br(),
    html.Br(),

    dbc.Row([html.Div(html.H4(' Experimental Trial Plots'),
                      style = {'textAlign':'center','fontWeight':'bold','width':'100%','color':'#1C1C1C','font-family':'sans-serif'})]),




    dbc.Row([html.Div(html.H5('Woods Run Seedlot'),
                      style = {'textAlign':'center','fontWeight':'bold','font-family':'sans-serif','width':'100%','color':'#363636'})]),



    dbc.Row([
        dbc.Col([dbc.Card(id = 'card_num1',color='#ffffff',style={'height':'350px'})],xl=3,lg=3,md = 12,sm=12,xs = 12),
        dbc.Col([dbc.Card(id = 'card_num2',color='#ffffff',style={'height':'350px'})],xl=3,lg=3,md = 12,sm=12,xs = 12),
        dbc.Col([dbc.Card(id = 'card_num3',color='#ffffff',style={'height':'350px'})],xl=3,lg=3,md = 12,sm=12,xs = 12),
       

        ]),

    html.Br(),
    dbc.Row([html.Div(html.H5('Improved Seedlot'),
                      style = {'textAlign':'center','fontWeight':'bold','font-family':'sans-serif','width':'100%','color':'#363636'})]),


    dbc.Row([
        dbc.Col([dbc.Card(id = 'card_num4',color='#ffffff',style={'height':'350px'})],xl=3,lg=3,md = 12,sm=12,xs = 12),
        dbc.Col([dbc.Card(id = 'card_num5',color='#ffffff',style={'height':'350px'})],xl=3,lg=3,md = 12,sm=12,xs = 12),
        dbc.Col([dbc.Card(id = 'card_num6',color='#ffffff',style={'height':'350px'})],xl=3,lg=3,md = 12,sm=12,xs = 12),
       

        ]),
    html.Br(),
    html.Br(),



   ],

 style = {'backgroundColor':'#ffffff'},
     fluid = True)



app.layout = html.Div(id = 'parent', children = [navbar,body_app])


@app.callback(Output('dropdown_colorscale', 'value'),
              [Input('dropdown_metric','value')])

def update_color_scales(metric):
    if metric == 'CAL22SP (mm)':
        return 'viridis'
    else:
        return 'agsunset'


@app.callback([Output('card_num1', 'children'),
              Output('card_num2', 'children'),
              Output('card_num3', 'children'),
              Output('card_num4', 'children'),
              Output('card_num5', 'children'),
              Output('card_num6', 'children'),
              

                ],
              [Input('dropdown_year','value'),
               Input('dropdown_Installation','value'),
               Input('dropdown_metric','value'),
              Input('dropdown_colorscale', 'value')])

def update_cards(Year,Installation,Metric,colorscales):
    df1_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==1) ].reset_index()
    df2_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==2)].reset_index()
    df3_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==3)].reset_index()
    df4_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==4)].reset_index()
    df5_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==5)].reset_index()
    df6_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==6)].reset_index()
     
    value_1_max = df1_a['Value'].max()
    value_2_max = df2_a['Value'].max()
    value_3_max = df3_a['Value'].max()
    value_4_max = df4_a['Value'].max()
    value_5_max = df5_a['Value'].max()
    value_6_max = df6_a['Value'].max()
    
    value_1_min = df1_a['Value'].min()
    value_2_min = df2_a['Value'].min()
    value_3_min = df3_a['Value'].min()
    value_4_min = df4_a['Value'].min()
    value_5_min = df5_a['Value'].min()
    value_6_min = df6_a['Value'].min()
    
    max_value=max([value_1_max,value_2_max,value_3_max,value_4_max,value_5_max,value_6_max])
    min_value=max([value_1_min,value_2_min,value_3_min,value_4_min,value_5_min,value_6_min])
    
    fig = go.Figure(data=go.Heatmap(
         z=df1_a['Value'],zmin=min_value, zmax=max_value,
         x=df1_a['Seedlot'],
         y=df1_a['Replication'],
         text=df1_a['Treetag'],
         colorscale=colorscales, hoverongaps = True, hovertemplate='Seedlot: %{x}<br>Tree Tag: %{text}<br>Replication: %{y}<br>Value: %{z}<extra></extra>'))

    fig['layout']['yaxis']['autorange'] = "reversed"

    fig.update_layout(template="plotly_white",
         margin=dict(l = 20, r = 5, t = 10, b = 10),
         yaxis_nticks=10,
     )
   
    fig.update_xaxes(side="top", tickangle = 270)

     
    fig1 = go.Figure(data=go.Heatmap(
         z=df2_a['Value'],zmin=min_value, zmax=max_value,
         x=df2_a['Seedlot'],
         y=df2_a['Replication'],
         text=df2_a['Treetag'],
         colorscale=colorscales, hoverongaps = True, hovertemplate='Seedlot: %{x}<br>Tree Tag: %{text}<br>Replication: %{y}<br>Value: %{z}<extra></extra>'))

    fig1['layout']['yaxis']['autorange'] = "reversed"

    fig1.update_layout(template="plotly_white",
         margin=dict(l = 20, r = 5, t = 10, b = 10),
         yaxis_nticks=10,

    )

    fig1.update_xaxes(side="top", tickangle = 270)


    fig2 = go.Figure(data=go.Heatmap(
         z=df3_a['Value'],zmin=min_value, zmax=max_value,
         x=df3_a['Seedlot'],
         y=df3_a['Replication'],
         text=df3_a['Treetag'],
        colorscale=colorscales, hoverongaps = True, hovertemplate='Seedlot: %{x}<br>Tree Tag: %{text}<br>Replication: %{y}<br>Value: %{z}<extra></extra>'))

    fig2['layout']['yaxis']['autorange'] = "reversed"

    fig2.update_layout(template="plotly_white",
         margin=dict(l = 20, r = 5, t = 10, b = 10),
         yaxis_nticks=10,
      )
    fig2.update_xaxes(side="top", tickangle = 270)


    fig3 = go.Figure(data=go.Heatmap(
         z=df4_a['Value'],zmin=min_value, zmax=max_value,
         x=df4_a['Seedlot'].astype('category'),
         y=df4_a['Replication'],
         text=df4_a['Treetag'],
         colorscale=colorscales, hoverongaps = True, hovertemplate='Seedlot: %{x}<br>Tree Tag: %{text}<br>Replication: %{y}<br>Value: %{z}<extra></extra>'))

    fig3['layout']['yaxis']['autorange'] = "reversed"


    fig3.update_layout(template="plotly_white",
         margin=dict(l = 20, r = 5, t = 10, b = 10),
         yaxis_nticks=10,
     )
    fig3.update_xaxes(side="top", tickangle = 270,type='category')


    fig4 = go.Figure(data=go.Heatmap(
         z=df5_a['Value'],zmin=min_value, zmax=max_value,
         x=df5_a['Seedlot'],
         y=df5_a['Replication'],
         text=df5_a['Treetag'],
        colorscale=colorscales, hoverongaps = True, hovertemplate='Seedlot: %{x}<br>Tree Tag: %{text}<br>Replication: %{y}<br>Value: %{z}<extra></extra>'))

    fig4['layout']['yaxis']['autorange'] = "reversed"

    fig4.update_layout(template="plotly_white",
         margin=dict(l = 20, r = 5, t = 10, b = 10),
         yaxis_nticks=10,
     )
    fig4.update_xaxes(side="top", tickangle = 270,type='category')


    fig5 = go.Figure(data=go.Heatmap(
         z=df6_a['Value'],zmin=min_value, zmax=max_value,
         x=df6_a['Seedlot'],
         y=df6_a['Replication'],
         text=df6_a['Treetag'],
         colorscale=colorscales, hoverongaps = True, hovertemplate='Seedlot: %{x}<br>Tree Tag: %{text}<br>Replication: %{y}<br>Value: %{z}<extra></extra>'))
     
    fig5['layout']['yaxis']['autorange'] = "reversed"

    fig5.update_layout(template="plotly_white",
         margin=dict(l = 20, r = 5, t = 10, b = 10),
         yaxis_nticks=10,
     )
    fig5.update_xaxes(side="top", tickangle = 270,type='category')


    card_content1 = [

         dbc.CardBody(
             [
                 html.H6('Plot 1 ' , style = {'color':'	#4F4F4F','fontWeight':'bold', 'textAlign':'center'}),
                 dcc.Graph(figure = fig, style = {'height':'300px'})


                ]

             )
        ]

    card_content2 = [

         dbc.CardBody(
             [
                 html.H6('Plot 2 ' , style = {'color':'	#4F4F4F','fontWeight':'bold', 'textAlign':'center'}),
                 dcc.Graph(figure = fig1, style = {'height':'300px'})


                ]

            )
        ]
    card_content3 = [

        dbc.CardBody(
            [
                html.H6('Plot 3 ' , style = {'color':'#4F4F4F','fontWeight':'bold', 'textAlign':'center'}),
                dcc.Graph(figure = fig2, style = {'height':'300px'})


                ]

            )
        ]

    card_content4 = [

       dbc.CardBody(
           [
               html.H6('Plot 4 ' , style = {'color':'#4F4F4F','fontWeight':'bold', 'textAlign':'center'}),
               dcc.Graph(figure = fig3, style = {'height':'300px'})


              ]

           )
      ]

    card_content5 = [

       dbc.CardBody(
           [
               html.H6('Plot 5 ' , style = {'color':'#4F4F4F','fontWeight':'bold', 'textAlign':'center'}),
               dcc.Graph(figure = fig4, style = {'height':'300px'})


              ]

          )
      ]
    card_content6 = [

    dbc.CardBody(
          [
              html.H6('Plot 6 ' , style = {'color':'#4F4F4F','fontWeight':'bold', 'textAlign':'center'}),
              dcc.Graph(figure = fig5, style = {'height':'300px'})


              ]

          )
      ]
    
      


    return card_content1,card_content2,card_content3,card_content4,card_content5,card_content6





if __name__ == "__main__":
    app.run_server(port=1120,debug = False)


3 Likes

Just adding to @hoatran’s answer, that if you don’t want to have an additional Drop Down for the colormap, you could tie the colormap to the metric directly.

1 Like

Hi @AIMPED, do you mean that he should add colormap to dataframe?

Hi @hoatran, no, I was thinking in using a colormap depending on the selected metric from the metric drop- dwon.

something like:

if metric == 'metric A':
    colormap = 'viridis'
else:
    colormap = 'plasma'
2 Likes

Hey @AIMPED Thanks for helping out on this!

2 Likes

@hoatran Thank you very much. You made my day. I appreciate how thoroughly you investigated my concern. More importantly, you modified the code for me, which was extremely helpful.

I don’t want an additional Drop Down for the colormap; instead, as @AIMPED said, I want to directly link the colormap to the metric. I tried if else statements, but my lack of programming experience did not help. Could you please assist me in including that instead of the colormap drop down?

As you can see from the modified dash, I have one more request (Modified code and data here - rgt) If you go to the drop-down Installation and select Sherry Transfer, you will not see any heatmaps. This is because, like other installations, a seed lot is not strictly in a column and no replication values.
I have Plotted it in excel and attaching here. can you give me a way we can show this along?

Thank you once more
Jaslam

@mJaslam: As @AIMPED suggestion, you can revise your code as below:

@app.callback([Output('card_num1', 'children'),
              Output('card_num2', 'children'),
              Output('card_num3', 'children'),
              Output('card_num4', 'children'),
              Output('card_num5', 'children'),
              Output('card_num6', 'children'),
              Output('card_num7', 'children'),
              Output('card_num8', 'children'),

                ],
              [Input('dropdown_year','value'),
               Input('dropdown_Installation','value'),
               Input('dropdown_metric','value') ])

def update_cards(Year,Installation,Metric):
    if Metric == 'CALIPER GROWTH (MM)':
        colormap = 'viridis'
    else:
        colormap = 'agsunset'

    df1_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==1) ].reset_index()
    df2_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==2)].reset_index()
    df3_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==3)].reset_index()
    df4_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==4)].reset_index()
    df5_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==5)].reset_index()
    df6_a = df1.loc[(df1['Year']==Year)&(df1['Installation']==Installation)&(df1['Metric']==Metric)&(df1['PLOT']==6)].reset_index()
    df8_a = IMP1.loc[(IMP1['Year']==Year)&(IMP1['Installation']==Installation)&(IMP1['Metric']==Metric) ].reset_index()
    df7_a = WR1.loc[(WR1['Year']==Year)&(WR1['Installation']==Installation)&(WR1['Metric']==Metric) ].reset_index()

    value_1_max = df1_a['Value'].max()
    value_2_max = df2_a['Value'].max()
    value_3_max = df3_a['Value'].max()
    value_4_max = df4_a['Value'].max()
    value_5_max = df5_a['Value'].max()
    value_6_max = df6_a['Value'].max()
    
    value_1_min = df1_a['Value'].min()
    value_2_min = df2_a['Value'].min()
    value_3_min = df3_a['Value'].min()
    value_4_min = df4_a['Value'].min()
    value_5_min = df5_a['Value'].min()
    value_6_min = df6_a['Value'].min()
    
    max_value=max([value_1_max,value_2_max,value_3_max,value_4_max,value_5_max,value_6_max])
    min_value=max([value_1_min,value_2_min,value_3_min,value_4_min,value_5_min,value_6_min])





    fig = go.Figure(data=go.Heatmap(
         z=df1_a['Value'],zmin=min_value, zmax=max_value,
         x=df1_a['Seedlot'],
         y=df1_a['Replication'],
         text=df1_a['Defect'],
         hovertext=df1_a['Management'],
         colorscale=colormap, hoverongaps = True, hovertemplate='Seedlot: %{x}<br>Value: %{z}<br>Defect: %{text}<br>Management: %{hovertext}<extra></extra>'))

    fig['layout']['yaxis']['autorange'] = "reversed"

    fig.update_layout(template="plotly_white",
         margin=dict(l = 20, r = 5, t = 10, b = 10),
         yaxis_nticks=10,
     )
   
    fig.update_xaxes(side="top", tickangle = 270)

So you will map colorscale with your Metric and then pass it to fig setting.
For Sherry Transfer, because Replication is blank so that it did not show. So you want to show picture, not chart?


2 Likes

Thanks @hoatran

I wish to display the graphs. I’ve heard of annotated heat maps. So, in case of a sherry transfer, along with the replication problem determining the x axis is difficult because the seed lot is not strictly 1 column. z=value and the seedlot name are displayed on the cells or hovertext… Can we do something similar? Any helpful advice on how to make the plot in the same way as image, which also varies with metrics?

Respectfully
Jaslam

Thanks @hoatran

I wish to display the graphs. I’ve heard of annotated heat maps. So, in case of a sherry transfer, along with the replication problem determining the x axis is difficult because the seed lot is not strictly in 1 column. z=value and the seedlot name are displayed on the cells or hovertext… Can we do something similar? Any helpful advice on how to make the plot in the same way as the above given image, which also varies with metrics?

Respectfully
Jaslam