Home > front end >  How do I add labels to subplot rows and columns?
How do I add labels to subplot rows and columns?

Time:09-17

I've made the following function which creates scalar profile plots from a scalar field.

import numpy as np 
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error



dummy_data_A = {'A': np.random.uniform(low=-3.5, high=3.5, size=(98,501)), 
                'B': np.random.uniform(low=-3.5, high=3.5, size=(98,501)), 'C': np.random.uniform(low=-3.5, high=3.5, size=(98,501))}
dummy_data_B = {'A': np.random.uniform(low=-3.5, high=3.5, size=(98,501)), 
                'B': np.random.uniform(low=-3.5, high=3.5, size=(98,501)), 'C': np.random.uniform(low=-3.5, high=3.5, size=(98,501))}


def plot_scalar_profiles(true_var, pred_var,  
                         coordinates = ['x','y'], 
                         x_position = None, 
                         bounds_var = None, 
                         norm = False ):
    
    fig = plt.figure(figsize=(12,10))
    st = plt.suptitle("Scalar fields", fontsize="x-large")
 
    nr_plots = len(list(true_var.keys())) # not plotting x or y
    plot_index = 1
   
    for key in true_var.keys():
        rmse_list = []
      
        for profile_pos in x_position:  

            
            true_field_star = true_var[key]
            pred_field_star = pred_var[key]
            
                
            axes = plt.subplot(nr_plots, len(x_position), plot_index)
            axes.set_title(f"scalar: {key},  X_pos: {profile_pos}")   # ,RMSE: {rms:.2f}
            
            
            true_profile = true_field_star[...,profile_pos]   # for i in range(probe positions)
            pred_profile = pred_field_star[...,profile_pos] 

            if norm:
              #true_profile = true_field_star[...,profile_pos]   # for i in range(probe positions)
              #pred_profile = pred_field_star[...,profile_pos] 
              true_profile = normalize_list(true_field_star[...,profile_pos])
              pred_profile = normalize_list(pred_field_star[...,profile_pos])
    
            rms = mean_squared_error(true_profile, pred_profile, squared=False)
            

            true_profile_y = range(len(true_profile))
            axes.plot(true_profile, true_profile_y, label='True')
            
            #
            pred_profile_y = range(len(pred_profile))
            axes.plot(pred_profile, pred_profile_y, label='Pred')
            
            rmse_list.append(rms) 
            plot_index  = 1
        
        RMSE = sum(rmse_list)
        print(f"RMSE {key}: {RMSE}")
        
    lines, labels = fig.axes[-1].get_legend_handles_labels()    
    fig.legend(lines, labels, loc = "lower right") 
    fig.supxlabel('u Velocity (m/s)')
    fig.supylabel('y Distance (cm)')
    fig.tight_layout()
  
    #plt.savefig(save_name   '.png', facecolor='white', transparent=False)
    plt.show()
    plt.close('all')
    
plot_scalar_profiles(dummy_data_A, dummy_data_B, 
                     x_position = [1, 100, 200, 400], 
                     bounds_var=None, 
                     norm=False) 

im.show

It also computes the total root mean squared error of the profiles extracted along x_position for scalar fields 'A','B', 'C'.

Console output:
RMSE A: 11.815624240063709
RMSE B: 11.623509385371737
RMSE C: 11.435156749416366

Theres 2 things I have questions about:

  1. How do I label each row and column? Labelling each subplot looks cluttered.

  2. How to I annotate the RMSE outputs to the right of each row?

Here's a rough drawing of what I mean with the 2 points.

enter image description here

Also any other suggestions for improvements are welcome. I'm still trying to figure out whats the cleanest/best way to represent this data.

CodePudding user response:

Assumption

I base my answer on the hypothesis you have always 12 plots, so I focus my attention to some plots with fixed positions.

Answer

In order to label each row, you can set the y label of the plots on the left side with fig.axes[n].set_ylabel, optionally you can add additional parameter to customize the label:

fig.axes[0].set_ylabel('A', rotation = 0, weight = 'bold', fontsize = 12)
fig.axes[4].set_ylabel('B', rotation = 0, weight = 'bold', fontsize = 12)
fig.axes[8].set_ylabel('C', rotation = 0, weight = 'bold', fontsize = 12)

In order to report RMSE for each row, you need to save those values in a list, then you can exploit the same concept as above: set the y label of the plots on the right side and move the y label to the right:

    RMSE = sum(rmse_list)
    RMSE_values.append(RMSE)
    print(f"RMSE {key}: {RMSE}")

...

fig.axes[3].set_ylabel(f'RMSE = {RMSE_values[0]:.2f}', rotation = 0, weight = 'bold', fontsize = 12, labelpad = 50)
fig.axes[7].set_ylabel(f'RMSE = {RMSE_values[1]:.2f}', rotation = 0, weight = 'bold', fontsize = 12, labelpad = 50)
fig.axes[11].set_ylabel(f'RMSE = {RMSE_values[2]:.2f}', rotation = 0, weight = 'bold', fontsize = 12, labelpad = 50)
fig.axes[3].yaxis.set_label_position('right')
fig.axes[7].yaxis.set_label_position('right')
fig.axes[11].yaxis.set_label_position('right')

Complete Code

import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error

dummy_data_A = {'A': np.random.uniform(low = -3.5, high = 3.5, size = (98, 501)),
                'B': np.random.uniform(low = -3.5, high = 3.5, size = (98, 501)),
                'C': np.random.uniform(low = -3.5, high = 3.5, size = (98, 501))}
dummy_data_B = {'A': np.random.uniform(low = -3.5, high = 3.5, size = (98, 501)),
                'B': np.random.uniform(low = -3.5, high = 3.5, size = (98, 501)),
                'C': np.random.uniform(low = -3.5, high = 3.5, size = (98, 501))}


def plot_scalar_profiles(true_var, pred_var,
                         coordinates = ['x', 'y'],
                         x_position = None,
                         bounds_var = None,
                         norm = False):
    fig = plt.figure(figsize = (12, 10))
    st = plt.suptitle("Scalar fields", fontsize = "x-large")

    nr_plots = len(list(true_var.keys()))  # not plotting x or y
    plot_index = 1
    RMSE_values = []

    for key in true_var.keys():
        rmse_list = []

        for profile_pos in x_position:

            true_field_star = true_var[key]
            pred_field_star = pred_var[key]

            axes = plt.subplot(nr_plots, len(x_position), plot_index)
            axes.set_title(f"scalar: {key},  X_pos: {profile_pos}")  # ,RMSE: {rms:.2f}

            true_profile = true_field_star[..., profile_pos]  # for i in range(probe positions)
            pred_profile = pred_field_star[..., profile_pos]

            if norm:
                # true_profile = true_field_star[...,profile_pos]   # for i in range(probe positions)
                # pred_profile = pred_field_star[...,profile_pos]
                true_profile = normalize_list(true_field_star[..., profile_pos])
                pred_profile = normalize_list(pred_field_star[..., profile_pos])

            rms = mean_squared_error(true_profile, pred_profile, squared = False)

            true_profile_y = range(len(true_profile))
            axes.plot(true_profile, true_profile_y, label = 'True')

            #
            pred_profile_y = range(len(pred_profile))
            axes.plot(pred_profile, pred_profile_y, label = 'Pred')

            rmse_list.append(rms)
            plot_index  = 1

        RMSE = sum(rmse_list)
        RMSE_values.append(RMSE)
        print(f"RMSE {key}: {RMSE}")

    lines, labels = fig.axes[-1].get_legend_handles_labels()
    fig.legend(lines, labels, loc = "lower right")
    fig.supxlabel('u Velocity (m/s)')
    fig.supylabel('y Distance (cm)')

    fig.axes[0].set_ylabel('A', rotation = 0, weight = 'bold', fontsize = 12)
    fig.axes[4].set_ylabel('B', rotation = 0, weight = 'bold', fontsize = 12)
    fig.axes[8].set_ylabel('C', rotation = 0, weight = 'bold', fontsize = 12)

    fig.axes[3].set_ylabel(f'RMSE = {RMSE_values[0]:.2f}', rotation = 0, weight = 'bold', fontsize = 12, labelpad = 50)
    fig.axes[7].set_ylabel(f'RMSE = {RMSE_values[1]:.2f}', rotation = 0, weight = 'bold', fontsize = 12, labelpad = 50)
    fig.axes[11].set_ylabel(f'RMSE = {RMSE_values[2]:.2f}', rotation = 0, weight = 'bold', fontsize = 12, labelpad = 50)
    fig.axes[3].yaxis.set_label_position('right')
    fig.axes[7].yaxis.set_label_position('right')
    fig.axes[11].yaxis.set_label_position('right')

    fig.tight_layout()

    # plt.savefig(save_name   '.png', facecolor='white', transparent=False)
    plt.show()
    plt.close('all')


plot_scalar_profiles(dummy_data_A, dummy_data_B,
                     x_position = [1, 100, 200, 400],
                     bounds_var = None,
                     norm = False)

Plot

enter image description here

  • Related