Skip to content

Commit

Permalink
Merge pull request #875 from PCMDI/ljw_portrait
Browse files Browse the repository at this point in the history
Portrait plotting code upgrade
  • Loading branch information
lee1043 authored Oct 11, 2022
2 parents 5cefcb9 + 9bb5d81 commit 02921b6
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 237 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
116 changes: 64 additions & 52 deletions pcmdi_metrics/graphics/portrait_plot/portrait_plot_example.ipynb

Large diffs are not rendered by default.

130 changes: 98 additions & 32 deletions pcmdi_metrics/graphics/portrait_plot/portrait_plot_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def portrait_plot(
vrange=None,
xaxis_fontsize=15,
yaxis_fontsize=15,
xaxis_tick_labels_top_and_bottom=False,
xticklabel_rotation=45,
inner_line_color="k",
inner_line_width=0.5,
cmap="RdBu_r",
cmap_bounds=None,
cbar_label=None,
Expand Down Expand Up @@ -61,29 +65,33 @@ def portrait_plot(
- `annotate_format`: format for annotate value, default="{x:.2f}"
- `figsize`: tuple of two numbers (width, height), default=(12, 10), figure size in inches
- `vrange`: tuple of two numbers, range of value for colorbar. Optional.
- `xaxis_fontsize`: number, default=15, font size for xaxis tick labels
- `yaxis_fontsize`: number, default=15, font size for yaxis tick labels
- `cmap`: string, default="RdBu_r", name of matplotlib colormap
- `xaxis_fontsize`: number, default=15, font size for xaxis tick labels. Optional.
- `yaxis_fontsize`: number, default=15, font size for yaxis tick labels. Optional.
- `xaxis_tick_labels_top_and_bottom`: bool, default=False, if true duplicate xaxis tick label to the other side. Optional.
- `xticklabel_rotation`: int or float, default=45, degree of angle to rotate x-axis tick label. Optional
- `inner_line_color`: string, default="k" (black), color for inner lines (triangle edge lines). Optional.
- `inner_line_width`: float, default=0.5, line width for inner lines (triangle edge lines). Optional.
- `cmap`: string, default="RdBu_r", name of matplotlib colormap. Optional.
- `cmap_bounds`: list of numbers. If given, discrete colors are applied. Optional.
- `cbar_label`: string, default=None, label for colorbar
- `cbar_label_fontsize`: number, default=15, font size for colorbar labels
- `cbar_tick_fontsize`: number, default=12, font size for colorbar tick labels
- `cbar_label`: string, default=None, label for colorbar. Optional.
- `cbar_label_fontsize`: number, default=15, font size for colorbar labels. Optional.
- `cbar_tick_fontsize`: number, default=12, font size for colorbar tick labels. Optional.
- `cbar_kw`: A dictionary with arguments to `matplotlib.Figure.colorbar`. Optional.
- `colorbar_off`: Trun off colorbar if True. Optional.
- `missing_color`: color, default="grey", `matplotlib.axes.Axes.set_facecolor` parameter
- `invert_yaxis`: bool, default=True, place y=0 at top on the plot
- `box_as_square`: bool, default=False, make each box as square
- `legend_on`: bool, default=False, show legend (only for 2 or 4 triangles portrait plot)
- `legend_labels`: list of strings, legend labels for triangls
- `legend_box_xy`: tuple of numbers, position of legend box's upper-left corner
(lower-left if `invert_yaxis=False`), in `axes` coordinate
- `legend_box_size`: number, size of legend box
- `legend_lw`: number, line width of legend, default=1
- `legend_fontsize`: number, font size for legend, default=14
- `logo_rect`: sequence of float. The dimensions [left, bottom, width, height] of the the PMP logo.
- `missing_color`: color, default="grey", `matplotlib.axes.Axes.set_facecolor` parameter. Optional.
- `invert_yaxis`: bool, default=True, place y=0 at top on the plot. Optional.
- `box_as_square`: bool, default=False, make each box as square. Optional.
- `legend_on`: bool, default=False, show legend (only for 2 or 4 triangles portrait plot). Optional.
- `legend_labels`: list of strings, legend labels for triangls. Optional.
- `legend_box_xy`: tuple of numbers, position of legend box's upper-left corner. Optional.
(lower-left if `invert_yaxis=False`), in `axes` coordinate. Optional.
- `legend_box_size`: number, size of legend box. Optional.
- `legend_lw`: number, line width of legend, default=1. Optional.
- `legend_fontsize`: number, font size for legend, default=14. Optional.
- `logo_rect`: sequence of float. The dimensions [left, bottom, width, height] of the the PMP logo. Optional.
All quantities are in fractions of figure width and height. Optional
- `logo_off`: bool, default=False, turn off PMP logo
- `debug`: bool, default=False, if true print more message when running that help debugging
- `logo_off`: bool, default=False, turn off PMP logo. Optional.
- `debug`: bool, default=False, if true print more message when running that help debugging. Optional.
Return
------
Expand All @@ -92,6 +100,7 @@ def portrait_plot(
- `cbar`: matplotlib component for colorbar (not returned if colorbar_off=True)
Author: Jiwoo Lee @ LLNL (2021. 7)
Last update: 2022. 10
"""

# ----------------
Expand All @@ -116,8 +125,10 @@ def portrait_plot(
# ----------------
# Ready to plot!!
# ----------------
if fig is None and ax is None:
fig, ax = plt.subplots(figsize=figsize)
if fig is None:
fig = plt.figure(figsize=figsize)
if ax is None:
ax = fig.add_subplot(111)

ax.set_facecolor(missing_color)

Expand All @@ -133,7 +144,11 @@ def portrait_plot(
norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax)
else:
cmap = plt.get_cmap(cmap)
norm = matplotlib.colors.BoundaryNorm(cmap_bounds, cmap.N, **cbar_kw)
if 'extend' in list(cbar_kw.keys()):
extend = cbar_kw['extend']
else:
extend = 'neither'
norm = matplotlib.colors.BoundaryNorm(cmap_bounds, cmap.N, extend=extend)

# [1] Heatmap-style portrait plot (no triangles)
if num_divide == 1:
Expand Down Expand Up @@ -178,6 +193,8 @@ def portrait_plot(
cmap=cmap,
invert_yaxis=invert_yaxis,
norm=norm,
inner_line_color=inner_line_color,
inner_line_width=inner_line_width,
)

# [4] Four triangle portrait plot
Expand All @@ -196,17 +213,23 @@ def portrait_plot(
tripcolorkw={
"cmap": cmap,
"norm": norm,
"edgecolors": "k",
"linewidth": 0.5,
"edgecolors": inner_line_color,
"linewidth": inner_line_width,
},
xaxis_labels=xaxis_labels,
yaxis_labels=yaxis_labels,
invert_yaxis=invert_yaxis,
)

# Let the horizontal axes labeling appear on top.
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False)
# X-axis tick labels
if xaxis_tick_labels_top_and_bottom:
# additional x-axis tick labels
ax.tick_params(axis="x", bottom=True, top=True, labelbottom=True, labeltop=True)
else:
# Let the horizontal axes labeling appear on top.
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False)

"""
# Rotate the tick labels and set their alignment.
plt.setp(
ax.get_xticklabels(),
Expand All @@ -215,6 +238,17 @@ def portrait_plot(
ha="right",
rotation_mode="anchor",
)
"""
# Rotate and align top ticklabels
plt.setp(
[tick.label2 for tick in ax.xaxis.get_major_ticks()], rotation=xticklabel_rotation,
ha="left", va="center", rotation_mode="anchor", fontsize=xaxis_fontsize)

if xaxis_tick_labels_top_and_bottom:
# Rotate and align bottom ticklabels
plt.setp(
[tick.label1 for tick in ax.xaxis.get_major_ticks()], rotation=xticklabel_rotation,
ha="right", va="center", rotation_mode="anchor", fontsize=xaxis_fontsize)

# Set font size for yaxis tick labels
plt.setp(ax.get_yticklabels(), fontsize=yaxis_fontsize)
Expand Down Expand Up @@ -248,11 +282,41 @@ def portrait_plot(

# Label for colorbar
if cbar_label is not None:
cbar.ax.set_ylabel(
cbar_label, rotation=-90, va="bottom", fontsize=cbar_label_fontsize
)
if "orientation" in list(cbar_kw.keys()):
if cbar_kw["orientation"] == "horizontal":
rotation = 0
ha = "center"
va = "top"
cbar.ax.set_xlabel(
cbar_label,
rotation=rotation,
ha=ha,
va=va,
fontsize=cbar_label_fontsize
)
else:
rotation = -90
ha = "center"
va = "bottom"
cbar.ax.set_ylabel(
cbar_label,
rotation=rotation,
ha=ha,
va=va,
fontsize=cbar_label_fontsize
)
else:
rotation = -90
ha = "center"
va = "bottom"
cbar.ax.set_ylabel(
cbar_label,
rotation=rotation,
ha=ha,
va=va,
fontsize=cbar_label_fontsize
)
cbar.ax.tick_params(labelsize=cbar_tick_fontsize)

return fig, ax, cbar
else:
return fig, ax
Expand Down Expand Up @@ -443,15 +507,17 @@ def triamatrix_wrap_up(
vmax=3,
norm=None,
invert_yaxis=True,
inner_line_color="k",
inner_line_width=0.5
):

# Colorbar range
if norm is None:
norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax)

# Triangles
im = triamatrix(upper, ax, rot=270, cmap=cmap, norm=norm, edgecolors="k", lw=0.5)
im = triamatrix(lower, ax, rot=90, cmap=cmap, norm=norm, edgecolors="k", lw=0.5)
im = triamatrix(upper, ax, rot=270, cmap=cmap, norm=norm, edgecolors=inner_line_color, lw=inner_line_width)
im = triamatrix(lower, ax, rot=90, cmap=cmap, norm=norm, edgecolors=inner_line_color, lw=inner_line_width)
ax.set_xlim(-0.5, upper.shape[1] - 0.5)
ax.set_ylim(-0.5, upper.shape[0] - 0.5)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"\n",
"- Generate a static image of Portrait plot (with or without triangles) using Matplotlib.\n",
"- Author: Jiwoo Lee (2021.07)\n",
"- Last update: 2022.09"
"- Last update: 2022.10"
]
},
{
Expand Down
Loading

0 comments on commit 02921b6

Please sign in to comment.