0

I am plotting this figure but I would like to play with the intervals. However, I don't want to have to modify the legend, DataFrame column names and other variables every single time manually. ideally I would send the ranges "<", "<=", ">=" as input arguments. Is this possible in Python?

The code:

def plotHistDistances(pat_name, lesion_id, rootdir, distanceMap, num_voxels, title, ablation_date):
    # PLOT THE HISTOGRAM FOR THE MAUERER EUCLIDIAN DISTANCES
    lesion_id_str = str(lesion_id)
    lesion_id = lesion_id_str.split('.')[0]
    figName_hist = 'Pat_' + str(pat_name) + '_Lesion' + str(lesion_id) + '_AblationDate_' + ablation_date + '_histogram'
    min_val = int(np.floor(min(distanceMap)))
    max_val = int(np.ceil(max(distanceMap)))
    fig, ax = plt.subplots(figsize=(18, 16))

    col_height, bins, patches = ax.hist(distanceMap, ec='darkgrey', bins=range(min_val - 1, max_val + 1))

    voxels_nonablated = []
    voxels_insuffablated = []
    voxels_ablated = []

    for b, p, col_val in zip(bins, patches, col_height):
        if b < 0:
            voxels_nonablated.append(col_val)
        elif 0 <= b <= 5:
            voxels_insuffablated.append(col_val)
        elif b > 5:
            voxels_ablated.append(col_val)
    # %%
    '''calculate the total percentage of surface for ablated, non-ablated, insufficiently ablated'''

    voxels_nonablated = np.asarray(voxels_nonablated)
    voxels_insuffablated = np.asarray(voxels_insuffablated)
    voxels_ablated = np.asarray(voxels_ablated)

    sum_perc_nonablated = ((voxels_nonablated / num_voxels) * 100).sum()
    sum_perc_insuffablated = ((voxels_insuffablated / num_voxels) * 100).sum()
    sum_perc_ablated = ((voxels_ablated / num_voxels) * 100).sum()
    # %%
    '''iterate through the bins to change the colors of the patches bases on the range [mm]'''
    for b, p, col_val in zip(bins, patches, col_height):
        if b < 0:
            plt.setp(p, label='Ablation Surface Margin ' + r'$x < 0$' + 'mm :' + " %.2f" % sum_perc_nonablated + '%')
        elif 0 <= b <= 5:
            plt.setp(p, 'facecolor', 'orange',
                     label='Ablation Surface Margin ' + r'$0 \leq  x \leq 5$' + 'mm: ' + "%.2f" % sum_perc_insuffablated + '%')
        elif b > 5:
            plt.setp(p, 'facecolor', 'darkgreen',
                     label='Ablation Surface Margin ' + r'$x > 5$' + 'mm: ' + " %.2f" % sum_perc_ablated + '%')
    # %%
    '''edit the axes limits and labels'''
    plt.xlabel('Euclidean Distances [mm]', fontsize=30, color='black')
    plt.tick_params(labelsize=28, color='black')
    ax.tick_params(colors='black', labelsize=28)
    plt.grid(True)
    # TODO: set equal axis limits
    ax.set_xlim([-15, 15])

    # edit the y-ticks: change to percentage of surface
    yticks, locs = plt.yticks()
    percent = (yticks / num_voxels) * 100
    percentage_surface_rounded = np.round(percent)
    yticks_percent = [str(x) + '%' for x in percentage_surface_rounded]
    new_yticks = (percentage_surface_rounded * yticks) / percent
    new_yticks[0] = 0
    plt.yticks(new_yticks, yticks_percent)
    #    plt.yticks(yticks,yticks_percent)
    plt.ylabel('Percentage of tumor surface voxels', fontsize=30, color='black')

    handles, labels = plt.gca().get_legend_handles_labels()
    by_label = OrderedDict(zip(labels, handles))
    plt.legend(by_label.values(), by_label.keys(), fontsize=30, loc='best')

    plt.title(title + '. Patient ' + str(pat_name) + '. Lesion ' + str(lesion_id), fontsize=30)

The figure: histogram with labels interval limits

So I would like to send the intervals you see in legend as input here:

def plotHistDistances(pat_name, lesion_id, rootdir, distanceMap,
                      num_voxels, title, ablation_date, interval_limits):
2
  • that's a lot of code for a seemingly simple histogram. Commented Sep 25, 2019 at 16:51
  • @QuangHoang yes. It's very important that my histogram is colored this way and that I also save the bins for later processing. You are free to suggest ways to improve the code :) I want to learn and do better Commented Sep 25, 2019 at 18:38

1 Answer 1

1

The idea is to parameterize the range element (i.e. 0 and 5 in your sample code) into interval_limits. To do so I have assumed that the parameter interval_limits will be a list of 2 values in the following form: [min_value, max_value] or concretely given your sample, interval_limits should be a list of 0, 5 like the following:

interval_limits = [0, 5]

Based on the assumption, I have modified your code a little bit. Pay attention to the new block where I assign the first element of interval_limits into a new variable min_limit and the 2nd element of interval_limits into another new variable max_limit and then I have modified the label string using the '%.2f format (feel free to change into whatever format you want)

Here's the code:

def plotHistDistances(pat_name, lesion_id, rootdir, distanceMap, num_voxels, title, ablation_date, interval_limits):
    ##########################################
    # NEW COODE SECTION
    ##########################################
    # Check if interval_limits contains all the limits
    if len(interval_limits) != 2:
        raise ValueError("2 limits are expected, got {} instead.".format(len(interval_limits)))
    # Assign the limits
    min_limit = interval_limits[0]
    max_limit = interval_limits[1]
    ##########################################
    # END OF NEW CODE SECTION
    ##########################################

    # PLOT THE HISTOGRAM FOR THE MAUERER EUCLIDIAN DISTANCES
    lesion_id_str = str(lesion_id)
    lesion_id = lesion_id_str.split('.')[0]
    figName_hist = 'Pat_' + str(pat_name) + '_Lesion' + str(lesion_id) + '_AblationDate_' + ablation_date + '_histogram'
    min_val = int(np.floor(min(distanceMap)))
    max_val = int(np.ceil(max(distanceMap)))
    fig, ax = plt.subplots(figsize=(18, 16))

    col_height, bins, patches = ax.hist(distanceMap, ec='darkgrey', bins=range(min_val - 1, max_val + 1))

    voxels_nonablated = []
    voxels_insuffablated = []
    voxels_ablated = []

    for b, p, col_val in zip(bins, patches, col_height):
        if b < min_limit:
            voxels_nonablated.append(col_val)
        elif min_limit <= b <= max_limit:
            voxels_insuffablated.append(col_val)
        elif b > max_limit:
            voxels_ablated.append(col_val)
    # %%
    '''calculate the total percentage of surface for ablated, non-ablated, insufficiently ablated'''

    voxels_nonablated = np.asarray(voxels_nonablated)
    voxels_insuffablated = np.asarray(voxels_insuffablated)
    voxels_ablated = np.asarray(voxels_ablated)

    sum_perc_nonablated = ((voxels_nonablated / num_voxels) * 100).sum()
    sum_perc_insuffablated = ((voxels_insuffablated / num_voxels) * 100).sum()
    sum_perc_ablated = ((voxels_ablated / num_voxels) * 100).sum()
    # %%
    '''iterate through the bins to change the colors of the patches bases on the range [mm]'''
    for b, p, col_val in zip(bins, patches, col_height):
        if b < min_limit:
            plt.setp(p, label='Ablation Surface Margin ' + r'$x < %.2f$' % min_limit + 'mm :' + " %.2f" % sum_perc_nonablated + '%')
        elif min_limit <= b <= max_limit:
            plt.setp(p, 'facecolor', 'orange',
                     label='Ablation Surface Margin ' + r'$%.2f \leq  x \leq %.2f$' % (min_limit, max_limit) + 'mm: ' + "%.2f" % sum_perc_insuffablated + '%')
        elif b > max_limit:
            plt.setp(p, 'facecolor', 'darkgreen',
                     label='Ablation Surface Margin ' + r'$x > %.2f$' % max_limit + 'mm: ' + " %.2f" % sum_perc_ablated + '%')
    # %%
    '''edit the axes limits and labels'''
    plt.xlabel('Euclidean Distances [mm]', fontsize=30, color='black')
    plt.tick_params(labelsize=28, color='black')
    ax.tick_params(colors='black', labelsize=28)
    plt.grid(True)
    # TODO: set equal axis limits
    ax.set_xlim([-15, 15])

    # edit the y-ticks: change to percentage of surface
    yticks, locs = plt.yticks()
    percent = (yticks / num_voxels) * 100
    percentage_surface_rounded = np.round(percent)
    yticks_percent = [str(x) + '%' for x in percentage_surface_rounded]
    new_yticks = (percentage_surface_rounded * yticks) / percent
    new_yticks[0] = 0
    plt.yticks(new_yticks, yticks_percent)
    #    plt.yticks(yticks,yticks_percent)
    plt.ylabel('Percentage of tumor surface voxels', fontsize=30, color='black')

    handles, labels = plt.gca().get_legend_handles_labels()
    by_label = OrderedDict(zip(labels, handles))
    plt.legend(by_label.values(), by_label.keys(), fontsize=30, loc='best')

    plt.title(title + '. Patient ' + str(pat_name) + '. Lesion ' + str(lesion_id), fontsize=30)

Disclaimer: I have not tested this code since I do not have the full set of parameter to reproduce the result, but that should work. If it doesn't feel free to provide me the set of parameters you use and I will see how I can rectify the issue

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.