Skip to content

Instantly share code, notes, and snippets.

@sunt05
Last active December 11, 2021 21:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sunt05/e6b0f23e47e492777cd83cfbef707903 to your computer and use it in GitHub Desktop.
Save sunt05/e6b0f23e47e492777cd83cfbef707903 to your computer and use it in GitHub Desktop.
generate metric wedge plot; inspired by https://www.nature.com/articles/s41597-021-01079-3/figures/3
generate metric wedge plot; inspired by https://www.nature.com/articles/s41597-021-01079-3/figures/3
metric HR HR HR HR HR HR HR HR MAE MAE MAE MAE MAE MAE MAE MAE MBE MBE MBE MBE MBE MBE MBE MBE N N N N N N N N
site KCL KCL KCL KCL SWD SWD SWD SWD KCL KCL KCL KCL SWD SWD SWD SWD KCL KCL KCL KCL SWD SWD SWD SWD KCL KCL KCL KCL SWD SWD SWD SWD
month 1 4 7 10 1 4 7 10 1 4 7 10 1 4 7 10 1 4 7 10 1 4 7 10 1 4 7 10 1 4 7 10
var cat
KDOWN Offline 0.9196428571428571 0.46726190476190477 0.6369047619047619 0.8280254777070064 0.8898809523809523 0.6130952380952381 0.6190476190476191 0.554140127388535 2.4739124503967256 11.791444846189082 10.497472718254079 4.989246284501065 8.023086350859634 31.10475384424591 11.551452752976186 60.41359032200984 -0.00011904761904776917 -0.8419022612488614 -0.011071428571427615 -0.012643312101910517 -2.2597321428571444 21.9940476190476 0.8287797619047604 -30.542515923566903 336 242 336 314 336 336 336 314
KDOWN Online 0.6875 0.3333333333333333 0.38392857142857145 0.589171974522293 0.6785714285714286 0.44345238095238093 0.40476190476190477 0.5254777070063694 16.19266370135049 78.84640012428042 69.97099075617056 33.46999990918547 18.63777568581203 83.48717266950216 75.04115269436727 69.25910773128459 5.092046535039942 19.2210056970755 14.701420248489088 3.3765259012779247 -1.187608636018064 27.878771447138426 -23.04704398027488 -44.97338696239863 336 242 336 314 336 336 336 314
KUP Offline 0.9553571428571429 0.6398809523809523 0.8005952380952381 1.0 0.9791666666666666 0.9166666666666666 0.9732142857142857 0.7484076433121019 2.5908458940541044 4.611406812913313 5.075014855779802 2.1287651481361256 1.3776504269865357 4.550503371657739 2.153100961061528 9.008821172184211 -2.059271819173097 3.793457439755912 3.9994516091533887 -0.2525361991633501 -0.10268839766850495 3.270277943278073 0.9318420833332934 -4.846844736491472 336 251 336 314 336 336 336 314
KUP Online 0.8809523809523809 0.48214285714285715 0.6279761904761905 0.802547770700637 0.8809523809523809 0.6309523809523809 0.6994047619047619 0.6815286624203821 3.9861375468490365 12.678222993362947 12.677270748178774 5.823491690054438 3.0471978036369674 14.023841550254176 11.216790034244111 10.957801051553652 -0.9442485318953796 8.25954084823094 8.523450518562772 1.262955887429654 0.8468410151254472 7.802935766365063 2.093189583805701 -5.218202938346346 336 251 336 314 336 336 336 314
LDOWN Offline 0.09523809523809523 0.18154761904761904 0.22023809523809523 0.20382165605095542 0.16964285714285715 0.15476190476190477 0.24404761904761904 0.11464968152866242 28.80548586307266 21.78252145884973 18.561839653859753 21.54420820363628 19.91418794831636 22.958469361842408 17.293573190097952 36.812156594595876 10.328926423402967 -8.079857620218412 2.2231875919786646 3.4497159011454945 2.298449478970949 -5.698775362894823 -5.324533937304787 -4.464329128620719 336 240 336 314 336 336 336 314
LDOWN Online 0.4166666666666667 0.22321428571428573 0.2857142857142857 0.410828025477707 0.2113095238095238 0.19642857142857142 0.13988095238095238 0.18789808917197454 21.343624282110287 25.2425304031372 25.96566813514346 21.875614108917805 29.829018514724005 32.395522529965376 33.89948244004019 38.50602943371817 -5.77322275797526 -19.03613387807211 -9.720736574445459 -10.539442358320684 -21.71153317042758 -26.320700432913625 -21.714666148594432 -14.239254717978717 336 240 336 314 336 336 336 314
LUP Offline 0.9821428571428571 0.6755952380952381 0.8898809523809523 0.9140127388535032 0.7113095238095238 0.7529761904761905 0.7976190476190477 0.3184713375796178 3.938866958133416 3.6848481677469427 4.609314568146286 4.001741127175416 10.082654094461175 8.817043552986267 5.850434989438742 26.056598536825174 -3.0956064229969025 0.9713405428237102 -1.345857295703294 2.55018751351955 10.082654094461175 8.413097958791314 4.912141804880463 2.3567695948187626 336 243 336 314 336 336 336 314
LUP Online 0.8452380952380952 0.5684523809523809 0.5773809523809523 0.8343949044585988 0.5803571428571429 0.6517857142857143 0.5505952380952381 0.31210191082802546 5.657558477492559 7.05495836046007 11.041619001116072 5.629943839881071 10.114204036167695 9.793943321591334 12.585875919887 25.685162135810604 2.9224694460914256 -1.6438440041875628 -3.2471743193126863 -0.09712199411574451 9.155629643031528 2.524099055698946 -2.1275536673409583 -0.33153517024532386 336 243 336 314 336 336 336 314
QE Offline 0.15476190476190477 0.22321428571428573 0.42857142857142855 0.2356687898089172 0.44047619047619047 0.43154761904761907 0.5327380952380952 0.3248407643312102 16.11352670616155 19.636056168653038 22.182636788542556 20.432225530772584 11.199279237605918 16.309328367642696 20.12458563429576 23.380659578592702 10.618043732362038 3.931076794364584 1.8390798366387922 12.254358082144547 -5.233177743156702 -4.036724688444019 9.54232370836601 -2.2888425094055997 307 189 302 248 249 235 299 240
QE Online 0.17857142857142858 0.19047619047619047 0.3244047619047619 0.23248407643312102 0.4880952380952381 0.37202380952380953 0.5714285714285714 0.31528662420382164 18.52063727014229 25.495804963477692 32.92714311649468 23.84773616806152 10.105633993515054 18.21227364039168 18.736801991765706 22.925545032376125 5.837182450694448 13.697368397649637 22.553881432275897 17.681449188570813 -4.1026095572269545 -5.034057231129484 -3.64757907821183 -8.582996270075743 307 189 302 248 249 235 299 240
QH Offline 0.07738095238095238 0.16666666666666666 0.22023809523809523 0.09872611464968153 0.07142857142857142 0.25 0.19047619047619047 0.28343949044585987 46.54473126754155 36.47865394165103 47.35639126042056 48.24331553491404 35.32910412794247 33.936081980475585 47.93534532957108 32.93644213899186 43.81626096021461 24.618028596391532 30.617859076240915 33.353190592479464 34.86328999028555 7.647908360448059 -29.4136863956245 3.479179032701086 317 210 321 272 325 325 331 313
QH Online 0.07142857142857142 0.11607142857142858 0.17559523809523808 0.1942675159235669 0.13988095238095238 0.3005952380952381 0.25595238095238093 0.24203821656050956 42.98693972754552 40.56709290631612 49.482302302482346 31.10166182854597 26.168893764517865 32.45166730941628 47.09659550722272 32.116652526978264 20.854003426389365 10.235599965504248 11.436583062941404 12.847053978583375 24.822679550699096 -1.3428357767325145 -31.37753043793482 -2.630958407769758 317 210 321 272 325 325 331 313
QN Offline 0.11607142857142858 0.16964285714285715 0.25297619047619047 0.21656050955414013 0.20535714285714285 0.16964285714285715 0.22916666666666666 0.07006369426751592 29.486217887742253 25.782102534501746 20.935350725282774 19.686017600865494 21.95887313983705 37.82599910304292 24.269404403897475 64.37267489023705 15.452330106593573 -13.332585205207273 -0.4660058081321757 1.1252837540198088 -9.940593598774111 4.733533258845305 -10.05051187313759 -32.43992277675689 336 240 336 314 336 336 336 314
QN Online 0.4255952380952381 0.13392857142857142 0.19642857142857142 0.321656050955414 0.1488095238095238 0.21726190476190477 0.17559523809523808 0.20063694267515925 21.118226206132338 63.7570957308213 59.551800596061206 33.77153054162196 37.169424460562205 65.44595852787295 71.14475856193472 73.784612323247 -2.659893314100445 -7.4790124561389275 -0.2677712331215525 -8.303995938718694 -32.9009647831498 -8.647339826502977 -44.43813299550892 -53.586066994883424 336 240 336 314 336 336 336 314
"""
Author: Ting Sun (ting.sun@reading.ac.uk)
wedge_quadrant.py (c) 2021
Desc: generate metric plot; inspired by https://www.nature.com/articles/s41597-021-01079-3/figures/3
Created: 2021-12-11T09:31:26.685Z
"""
# %%
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.collections import PatchCollection
from matplotlib.patches import Circle, Polygon, Wedge
# %%
# dict for labelling annotations
dict_lbl_var = {
"KDOWN": r"$K_\downarrow$",
"LDOWN": r"$L_\downarrow$",
"KUP": r"$K_\uparrow$",
"LUP": r"$L_\uparrow$",
"QN": r"$Q^*$",
"QF": r"$Q_F$",
"QH": r"$Q_H$",
"QE": r"$Q_E$",
"Bo": r"$Bo$",
}
dict_lbl_var = {
var: lbl + r" (W m$^{-2}$)" if var != "Bo" else lbl
for var, lbl in dict_lbl_var.items()
}
dict_lbl_metric = {
"MBE": r"(W m$^{-2}$)",
"MAE": r"(W m$^{-2}$)",
"HR": r"",
}
df_metric_inset = pd.read_csv(
"./df_metric_inset.csv",
index_col=[0, 1],
header=[0, 1, 2],
)
df_metric_inset
# %%
# helper function to create a quadrant wedge
def plot_wedge(ax, ar_val, cmap_func):
p = [
Wedge(
(0.5, 0.5),
1,
-45 + 90 * s,
45 + 90 * s,
facecolor=cmap_func.to_rgba(v),
edgecolor="grey",
)
for s, v in enumerate(ar_val)
]
p = PatchCollection(p, match_original=True)
_ = ax.add_collection(p)
_ = ax.xaxis.set_ticks([])
_ = ax.yaxis.set_ticks([])
# list for looping
list_var = df_metric_inset.index.get_level_values("var").unique()
list_cat = df_metric_inset.index.get_level_values("cat").unique()
list_site = df_metric_inset.columns.get_level_values("site").unique()
list_metric = df_metric_inset.columns.get_level_values("metric").unique().drop("N")
# set up figure container
n_row, n_col = len(list_var) * len(list_cat), len(list_metric) * len(list_site)
size_base = 1.0
fig, axes = plt.subplots(
n_row,
n_col,
figsize=(n_col * 1 * size_base, n_row * 0.6 * size_base),
sharey=True,
sharex=True,
constrained_layout=True,
)
dict_cmap = {
"MAE": "Reds",
"MBE": "RdBu_r",
"HR": "Purples",
# "HR": "Blues",
}
dict_norm = {}
for metric in dict_cmap.keys():
ar_var = df_metric_inset[metric].values
dict_norm[metric] = mpl.colors.Normalize(vmin=ar_var.min(), vmax=ar_var.max())
# =============================================================================
# colour patches
for r0, var in enumerate(list_var):
ar_var = df_metric_inset.loc[var].values
for r1, cat in enumerate(list_cat):
r = r0 * len(list_cat) + r1
for c0, metric in enumerate(list_metric):
norm = dict_norm[metric]
cmap = mpl.cm.get_cmap(dict_cmap[metric])
cmap_func = mpl.cm.ScalarMappable(norm=norm, cmap=cmap)
for c1, site in enumerate(list_site):
c = c0 * len(list_site) + c1
ax = axes[r, c]
# draw wedge into quadrant
df_plot = df_metric_inset.loc[(var, cat), (metric, site)]
plot_wedge(ax, df_plot.values, cmap_func)
# label: site
if r == 0:
_ = ax.set_xlabel(site)
_ = ax.xaxis.set_label_position("top")
# label: category
if c == 0:
_ = ax.set_ylabel(cat)
# draw colorbar
list_cax = []
for c, metric in enumerate(list_metric[-1::-1]):
norm = dict_norm[metric]
cmap = mpl.cm.get_cmap(dict_cmap[metric])
cmap_func = mpl.cm.ScalarMappable(norm=norm, cmap=cmap)
cbar = fig.colorbar(
cmap_func,
ax=axes,
orientation="horizontal",
fraction=0.04,
panchor=(0.1, 0.0),
anchor=(1, 1),
shrink=0.7,
pad=0.01,
)
# colorbar label: hacked version as the label can't be set in the other end
cax = cbar.ax
lbl = metric + "\n" + dict_lbl_metric[metric]
_ = cax.text(
-0.08,
0.0,
lbl,
size=11,
va="center",
ha="center",
transform=cax.transAxes,
)
list_cax.append(cax)
# ============================================================
# note:
# > Constrained Layout will be effectuated each time the figure is drawn.
# > Hence you would need to draw the figure first, then you can get the actual position of the axes inside of it.
# ref: https://stackoverflow.com/a/54585004/920789
fig.canvas.draw()
# ============================================================
# outskirt labels: variables
for r0, var in enumerate(list_var):
y = []
for r1, cat in enumerate(list_cat):
r = r0 * len(list_cat) + r1
ax_test = axes[r, 0]
bbox = ax_test.get_position()
x_ax, y_ax, w, h = bbox.bounds
# print(r, x_ax, y_ax, w, h, y_ax + h / 2)
y.append(y_ax + h / 2)
# y_ax_mid = y_ax + h / 2
# print(y)
x = -0.01
y = np.mean(y)
lbl = dict_lbl_var[var].split(" ")[0]
_ = fig.text(
x,
y,
lbl,
rotation="vertical",
ha="right",
va="center",
fontsize=13,
)
# outskirt labels: metrics
for c0, metric in enumerate(list_metric):
x = []
for c1, cat in enumerate(list_cat):
c = c0 * len(list_cat) + c1
ax_test = axes[0, c]
bbox = ax_test.get_position()
x_ax, y_ax, w, h = bbox.bounds
# print(c, x_ax, y_ax, w, h, x_ax + w / 2)
x.append(x_ax + w / 2)
# x_ax_mid = x_ax + w / 2
# print(x)
x = np.mean(x)
lbl = metric
_ = fig.text(
x,
1,
lbl,
rotation="horizontal",
ha="center",
va="bottom",
fontsize=13,
)
# ============================================================
# # note:
# # > Constrained Layout will be effectuated each time the figure is drawn.
# # > Hence you would need to draw the figure first, then you can get the actual position of the axes inside of it.
# # ref: https://stackoverflow.com/a/54585004/920789
fig.canvas.draw()
# ============================================================
# draw legend: quartrant wedge
# location of legend: use y of cax
ar_bbox_cax = np.array([cax.get_position().bounds for cax in list_cax])
y0 = np.mean(ar_bbox_cax[:, 1])
# location of legend: use x of axes
x0, _, w, h = axes[-1, 0].get_position().bounds
# bbox size of legend: use bbox of axes
w, h = w * 1.3, h * 1.6
# new axes to hold legend
ax = fig.add_axes([x0 - 0.02, y0 - h / 2, w, h])
# shift distance of month labels
offset = 0.28
x = [0, offset, 0, -offset]
y = [offset, 0, -offset, 0]
p = []
for s, month in enumerate(["Jan", "Apr", "Jul", "Oct"]):
p += [
Wedge(
(0.5, 0.5),
1,
-45 + 90 * s,
45 + 90 * s,
facecolor="none",
edgecolor="grey",
)
]
# month label
_ = ax.text(x[s] + 0.5, y[s] + 0.5, month, ha="center", va="center")
p = PatchCollection(p, match_original=True)
_ = ax.add_collection(p)
_ = ax.xaxis.set_ticks([])
_ = ax.yaxis.set_ticks([])
fig.savefig("metric_wedge_quadrant.pdf", bbox_inches="tight")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment