Skip to content

Inset axis formats parent axis #483

Closed

Description

@tamnnm

Dear teams,

First thank you so much for your work. While using Ultraplot, I notice that the syntax leftlabels_kw = {'size': xx} doesn't have any effect on changing the fontsize of the leftlabels. Could you take a quick look on that?

Sincerely, Tam

Activity

  1. cvanelteren commented on Jan 19, 2026

    @cvanelteren
    Collaborator

    Any particular code you have issues with? This seems to work fine for me

    fig, ax = uplt.subplots(ncols=2, nrows=2)
    ax.format(leftlabels=["hello", "there"], leftlabels_kw=dict(size=3))
    fig.save("test.png")
    uplt.show(block=1)
    Image

    UltraPlot: 1.71.0
    Matplotlib: 3.10.8
    Python: 3.14.2

  2. tamnnm commented on Jan 20, 2026

    @tamnnm
    Author

    Here given my code. I don't think that my configuration or my code has any other updates/moderation compared to you.

    I attached my code and link to my data if you want to try it.

    DataLink: https://drive.google.com/drive/folders/1p7vNZMVFiFJQAwR4NClucdH48eJQLgPo?usp=sharing
    `def doubling(resample_mode, ind='Tx', gev_fit=False, doubling: Optional[Literal['linear', 'log', 'both']] = 'linear'):
    data_dict = {}
    with h5py.File(f'OG_{resample_mode}.h5', 'r') as f:
    for key,item in f.items():
    data_dict[key] = item[:]
    if os.path.exists(f"OG_{resample_mode}.csv"):
    data_fitted_df = pd.read_csv(f"OG_{resample_mode}.csv")
    if resample_mode == 0:
    data_fitted_df.rename(columns = {'Unnamed: 0': 'name_station'}, inplace=True)
    else:
    data_fitted_df.rename(columns = {'station': 'name_station'}, inplace=True)
    else:
    data_fitted_df = fit_gev(ind, og=True, resample_mode=0)

    num_station = len(data_fitted_df.index)
    ri = RI(resample_mode = resample_mode, monthly = False)
    TR = 50
    mu_T_threshold = 3 # Threshold for fit of sigma 2x
    
    # Initialize the variables
    mu_T = np.linspace(0,10,100) #local temperature rise
    TR_rise = np.zeros(shape=(mu_T.size, num_station)) # return period
    E_rise = np.zeros(shape=(mu_T.size, num_station)) # exceedence probability
    ODD_rise = np.zeros(shape=(mu_T.size, num_station)) # odd of exceeding
    sigma_2x = np.zeros(mu_T.size) # slope of the line
    sigma_2x_fit = np.zeros(mu_T.size) # slope of the line
    
    
    fig, axes = uplt.subplots(ncols = 2, nrows = 2,  sharey = False)
    axes.format(
    abc="a.",
    abcloc="l",
    grid=False,
    # toplabels=("Column 1", "Column 2"),
    leftlabels=("Empirical", " GEV"),
    leftlabels_kw = {'size': '60'}
    )
    
    ix = axes[0].inset(
            [1, 2000, 2, 5000],
            transform="data",
            zoom=True,
            zoom_kw={"ec": "blush", "ls": "--", "lw": 2},
            yscale = 'log', yscale_kw = {'base': 2})
    
    ix.format(xlim=(0, 3), ylim=(0, 512), xlocator = np.arange(0,4,1), fontsize = 7,  color="red8", linewidth=1)
    
    #! Use the daily data to calculate the exceedence probability
    for i,name_station in enumerate(data_dict.keys()):
    
        # Get input values
        station_df = data_fitted_df[data_fitted_df['name_station'] == name_station]
        data = data_dict[name_station]
        mu_org = station_df['loc'].values[0]
        sigma_org = station_df['scale'].values[0]
        k_org = station_df['shape'].values[0]
        clus = station_df['cluster'].values[0]
    
        x0_GEV = x_Tr(TR, mu_org, sigma_org, k_org, ri) # Reverse of cdf at TR
        E_base = ri/TR
    
        if not gev_fit:
            # ALL DATA
            if resample_mode == 0:
            # Value of x that has return period: 50 years (x_50)
    
            # Calculate the exceedence probability emperically
    
                # Find the exceedance value of all values
                E_list = EV(data, mu_org, sigma_org, k_org) # TODO: Think about the exceedance of ri = 1/3 or just all data
    
                # Find the last index of each exceedance value
                # Find the corresponding real value of the exceedance value
                last_index_E = last_index(E_list)
                x_tmp, E_tmp = data[last_index_E], E_list[last_index_E]
    
                # Develop the relationship between x and E
                # Use x_50 to find E_50 (exceedence probability of x_50)(percent)
                E_base = f_interp(x0_GEV,x_tmp,E_tmp) # or E0 if you want original use linear but it's kinda outdated
    
                # Rising odd
                # E(x_50 - mu_T) - right shift compared to original
                for j, mu in enumerate(mu_T):
                    x_rise = x_tmp + mu
                    E_rise[j,i] = f_interp(x0_GEV, x_rise, E_tmp) # Eceedance of rise
            # EXTREME DATA (n events/year)
            else:
                #?? Why doesn't they use the same way?
                #! They define Exceedence first here??? Why???
                x_tmp = data[last_index(data)][::-1] # Descending
                len_x = x_tmp.size
                E_tmp = np.linspace(1/len_x,len_x/(len_x+1),len_x)
    
                #? x_temp is descending <= we try to match the CDF => larger temp = smaller exceedance
                x0_emp = f_interp(E_base, E_tmp, x_tmp)
    
                for j, mu in enumerate(mu_T):
                    x_rise = x_tmp + mu
                    E_rise[j,i] = f_interp(x0_emp, x_rise, E_tmp)
                    TR_rise[j,i] = ri/E_rise[j,i]
    
        # Use GEV distribution to calculate the exceedence probability
        else:
            #? Use GEV distribution to calculate the exceedence probability
            for j, mu in enumerate(mu_T):
                x_rise = x0_GEV + mu
                E_rise[j,i] = EV(x0_GEV, mu_org+mu, sigma_org, k_org)
                TR_rise[j,i] = ri/E_rise[j,i]
    
        ODD_rise[:,i] = E_rise[:,i]/(1-E_rise[:,i])
        ODD_base = E_base/(1-E_base)
        ODD_ratio = ODD_rise[:,i]/ODD_base
        # ODD_ratio_log = np.log2(ODD_ratio)
        ODD_ratio_log = np.log(np.log(ODD_ratio))
    
        if ODD_base in (np.nan, 0):
            sigma_2x[i] = np.nan
            sigma_2x_fit[i] = np.nan
        else:
            # Index for mu_T < mu_T_threshold
            index_sel = np.where(mu_T<mu_T_threshold)
            x_sel = mu_T[index_sel]
    
            # Choose ODD_ratio_log where mu_t < mu_T_threshold
            ODD_ratio_log_sel = ODD_ratio_log[index_sel]
            log_index = ~np.isnan(ODD_ratio_log_sel) # index of non-NaN values
    
            #? Explaination
            #? Slope of the line = doubling rate of growth
            #? We find the slope of this line (m): log_2(ODD_ratio) = mu_T/sigma_2x = m.mu_T
            #? => sigma_2x = 1/m
    
            # Method 1: Linear
            if doubling in ('linear', 'both'):
                sigma_2x[i] = 1/np.nanmean(np.diff(ODD_ratio_log_sel)/np.diff(x_sel))
            # Method 2: Polynomial
            if doubling in ('log', 'both'):
                p = np.polyfit(x_sel[log_index], ODD_ratio_log_sel[log_index], 1)
                sigma_2x[i] = 1/p[0] # Use polynomial relationship
    
    
        axes[0].plot(mu_T, ODD_ratio, color='blue9', alpha=0.4, linewidth = 1)
        ix.plot(mu_T, ODD_ratio, color='blue9', alpha=0.4, linewidth = 1)
        axes[1].plot(mu_T/sigma_2x[i], ODD_ratio_log, color='blue9', alpha=0.5, linewidth = 1)
    
    axes[0].format(xlim = (0,8), ylim = (0,8000), xlabel = r'$\mu_T$', ylabel = r'$\frac{\mathrm{Odds}}{\mathrm{Odds_0}}$')
    
    # if resample_mode==0: ax.plot(np.linspace(0,40,1000), 2.5*(np.log2(np.linspace(0,40,1000)+1)), color='gray', linestyle='-.', linewidth=10)
    axes[1].plot(mu_T,mu_T, color = 'r', linestyle = '--')
    
    # Draw lines at x=0 and y=0
    axes[1].format(xlabel = r'$\frac{\mu_T}{\tilde{\sigma}}$',
                   ylabel = r'$\log\log\left(\frac{\mathrm{Odds}}{\mathrm{Odds_0}}\right)$',
                   ylim = (0,4),xlim = (0,4),ylocator = (np.arange(0,5,1)), xlocator = (np.arange(0, 18, 2)))
    
    # The equation of odds of exceedance
    axes[1].text(2, 0.75, r'$\log\log\left(\frac{\mathrm{Odds}}{\mathrm{Odds}_0}\right) = \frac{\mu_T}{\tilde{\sigma}}$',
                 fontsize = 10, fontweight = 'bold',
                 bbox=dict(boxstyle="round",
                          alpha = 0.8,
                            ec=(1., 0.5, 0.5),
                            fc=(0.9,0.9,0.9),
                            ))
    axes[1].annotate("",xy=(0.55, 0.28), xycoords = 'axes fraction', xytext = (0.45, 0.4),
                     textcoords = 'axes fraction', arrowprops = {'arrowstyle':'simple, head_width=0.5, head_length=1', 'facecolor': 'black','linewidth': 0.25, 'connectionstyle':"arc3,rad=-0.2"})
    
    
    fig.savefig(os.getenv('img')+f"/GEV/Doubling_rate_{resample_mode}_{"gev" if gev_fit else "emp"}.png")
    

    `

  3. cvanelteren commented on Jan 20, 2026

    @cvanelteren
    Collaborator

    Could you please provide a minimal and verifiable example?

  4. cvanelteren commented on Jan 20, 2026

    @cvanelteren
    Collaborator

    I see the issue. Fix incoming

  5. changed the title [-]Update leftlabels has no effect[/-] [+]Inset axis formats parent axis[/+] on Jan 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions