Skip to content

Commit f32e26f

Browse files
committed
add parameter cbar_height & update docs
1 parent 04ba90d commit f32e26f

275 files changed

Lines changed: 2980 additions & 4737 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

PyComplexHeatmap/annotations.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class AnnotationBase:
5050
`legend_kws={'color_text':False}`, then, black would be the default color for the text.
5151
If the user want to use a custom color instead of black (such as blue), please set
5252
legend_kws={'color_text':False,'labelcolor':'blue'}.
53+
An extra parameter `cbar_height` could be passed in legend_kws to control the height of the colorbar for heatmap, default is 15 [mm].
5354
ylim: tuple
5455
y axis limits for the annotation when axis=1, x axis limits when axis=0.
5556
label: str
@@ -453,12 +454,10 @@ class anno_label(AnnotationBase):
453454
passed to plt.annotate, including annotation_clip, arrowprops and matplotlib.text.Text,
454455
more information about arrowprops could be found in
455456
matplotlib.patches.FancyArrowPatch. For example, to remove arrow, just set
456-
arrowprops = dict(visible=False). See: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.annotate.html for more information.
457-
arrowprops:
458-
arrowstyle:
459-
https://matplotlib.org/stable/gallery/text_labels_and_annotations/fancyarrow_demo.html
460-
connectionstyle:
461-
https://matplotlib.org/stable/gallery/userdemo/connectionstyle_demo.html
457+
arrowprops = dict(visible=False). See https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.annotate.html for more information.
458+
459+
- arrowprops.arrowstyle: https://matplotlib.org/stable/gallery/text_labels_and_annotations/fancyarrow_demo.html
460+
- arrowprops.connectionstyle: https://matplotlib.org/stable/gallery/userdemo/connectionstyle_demo.html
462461
463462
Returns
464463
----------
@@ -2038,7 +2037,7 @@ def collect_legends(self):
20382037
[
20392038
annotation.cmap,
20402039
annotation.label,
2041-
legend_kws, 4, "cmap"]
2040+
legend_kws, legend_kws.pop('cbar_height',15), "cmap"]
20422041
)
20432042
self.get_legend_list() #self.legend_list will be created
20442043

PyComplexHeatmap/clustermap.py

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -936,14 +936,16 @@ def get_coords(self,ax,gap_pixel=None,root_x=None):
936936
def plot(self, ax, gap_pixel=None, root_x=None,tree_kws=None,
937937
bezier=False,dotsize=1,root_dot=True,axis=1,label=False,
938938
orientation=None,invert=True):
939-
"""Plots a dendrogram of the similarities between data on the axes
939+
"""Plots a dendrogram of the similarities between data on the axes.
940+
940941
Parameters
941942
----------
942943
ax : matplotlib.axes.Axes
943944
Axes object upon which the dendrogram is plotted
944945
axis : int
945946
0 for rows (default) and 1 for columns.
946947
label : bool
948+
Whether to show labels on the dendrogram.
947949
rotate : bool
948950
If axis==0 and one would like to plot row dendrogram, rotate
949951
should be True
@@ -1144,16 +1146,19 @@ class ClusterMapPlotter:
11441146
legend_kws :dict
11451147
vmax, vmin and other kws passed to plot legend, such as
11461148
use_gridspec, location, orientation, fraction, shrink, aspect, pad, anchor, panchor,
1147-
extend, extendfrac, extendrect, ticks, format, drawedges, label, doundaries, values, spacing:
1148-
```
1149-
cm=ClusterMapPlotter(...)
1150-
for cbar in cm.cbars:
1151-
if isinstance(cbar,matplotlib.colorbar.Colorbar):
1152-
cbar.outline.set_color('white')
1153-
cbar.outline.set_linewidth(2)
1154-
cbar.dividers.set_color('red')
1155-
cbar.dividers.set_linewidth(2)
1156-
```
1149+
extend, extendfrac, extendrect, ticks, format, drawedges, label, doundaries, values, spacing.
1150+
Default extend is set to 'both' (change to neither to turn off) and default extendfrac is set to 0.1
1151+
An extra parameter ``cbar_height`` could be passed in legend_kws to control the height of the colorbar for heatmap, default is 20 [mm].
1152+
Example::
1153+
1154+
cm=ClusterMapPlotter(...)
1155+
for cbar in cm.cbars:
1156+
if isinstance(cbar,matplotlib.colorbar.Colorbar):
1157+
cbar.outline.set_color('white')
1158+
cbar.outline.set_linewidth(2)
1159+
cbar.dividers.set_color('red')
1160+
cbar.dividers.set_linewidth(2)
1161+
11571162
In addition to vmax,vmin, other parameters will be passed to https://matplotlib.org/stable/api/_as_gen/matplotlib.figure.Figure.colorbar.html#matplotlib.figure.Figure.colorbar
11581163
11591164
plot :bool
@@ -1198,14 +1203,12 @@ class ClusterMapPlotter:
11981203
alpha,color,fontfamily,fontname,fontproperties,fontsize,fontstyle,
11991204
fontweight,label,rasterized,rotation,rotation_mode(default,anchor),visible,
12001205
zorder,verticalalignment,horizontalalignment.
1201-
See ax.xaxis.label.properties(), for example:
1202-
```
1203-
cm=ClusterMapPlotter(***), print(cm.ax.xaxis.label.properties())
1204-
```
1205-
or
1206-
```
1207-
matplotlib.axis.XAxis.label.properties() for detail.
1208-
```
1206+
See ax.xaxis.label.properties(), for example::
1207+
1208+
cm=ClusterMapPlotter(...)
1209+
print(cm.ax.xaxis.label.properties())
1210+
1211+
or ``matplotlib.axis.XAxis.label.properties()`` for detail.
12091212
ylabel_kws: dict
12101213
sams as xlabel_kws
12111214
xlabel_side: str
@@ -1216,10 +1219,11 @@ class ClusterMapPlotter:
12161219
alpha,clip_box, clip_on,edgecolor,facecolor,fill,height,in_layout,label,
12171220
linestyle, linewidth,rasterized,visible,width.
12181221
See ax.xaxis.label.get_bbox_patch().properties() for more information.
1219-
For example:
1220-
```
1221-
cm=ClusterMapPlotter(***), print(cm.ax.xaxis.label.get_bbox_patch().properties())
1222-
```
1222+
For example::
1223+
1224+
cm=ClusterMapPlotter(...)
1225+
print(cm.ax.xaxis.label.get_bbox_patch().properties())
1226+
12231227
ylabel_bbox_kws: dict
12241228
same as xlabel_bbox_kws
12251229
rasterized :bool
@@ -2307,7 +2311,10 @@ def collect_legends(self):
23072311
self.legend_kws.setdefault("vmin", self.kwargs.get('vmin')) #round(vmin, 2))
23082312
self.legend_kws.setdefault("vmax", self.kwargs.get('vmax')) #round(vmax, 2))
23092313
self.legend_kws.setdefault("center", self.kwargs.get('center', None))
2310-
self.legend_dict[self.label]=tuple([self.cmap, self.label, self.legend_kws, 4, "cmap"])
2314+
self.legend_kws.setdefault("extend", 'both')
2315+
self.legend_kws.setdefault("extendfrac", 0.15)
2316+
cbar_height=self.legend_kws.pop('cbar_height',20)
2317+
self.legend_dict[self.label]=tuple([self.cmap, self.label, self.legend_kws, cbar_height, "cmap"])
23112318
if len(self.yticklabels) > 0 and self.row_names_side == "right":
23122319
max_yticklabel_w = max(
23132320
[label.get_window_extent().width for label in self.yticklabels]

PyComplexHeatmap/dotHeatmap.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,22 +599,28 @@ def collect_legends(self):
599599
cmap_legend_kws.setdefault("vmin", self.kwargs.get('vmin')) # round(vmin, 2))
600600
cmap_legend_kws.setdefault("vmax", self.kwargs.get('vmax')) # round(vmax, 2))
601601
cmap_legend_kws.setdefault("center", self.kwargs.get('center', None))
602+
cmap_legend_kws.setdefault("extend", 'both')
603+
cmap_legend_kws.setdefault("extendfrac", 0.15)
604+
cbar_height=cmap_legend_kws.pop('cbar_height',20)
602605
for key in self.cmap:
603-
self.legend_dict[f"{key} (cmap)"]=tuple([self.cmap[key], key, cmap_legend_kws, 4, "cmap"])
606+
self.legend_dict[f"{key} (cmap)"]=tuple([self.cmap[key], key, cmap_legend_kws, cbar_height, "cmap"])
604607
else: # hue is None
605608
cmap = self.cmap
606609
c = self.kwargs.get("c", None)
607610
cmap_legend_kws = self.cmap_legend_kws.copy()
608611
cmap_legend_kws.setdefault("vmin", self.kwargs.get('vmin')) # round(vmin, 2))
609612
cmap_legend_kws.setdefault("vmax", self.kwargs.get('vmax')) # round(vmax, 2))
610613
cmap_legend_kws.setdefault("center", self.kwargs.get('center', None))
614+
cmap_legend_kws.setdefault("extend", 'both')
615+
cmap_legend_kws.setdefault("extendfrac", 0.15)
616+
cbar_height=cmap_legend_kws.pop('cbar_height',20)
611617
if (
612618
not cmap is None
613619
and type(cmap) == str
614620
and not c is None
615621
and type(c) != str
616622
):
617-
self.legend_dict[f"{self.value} (cmap)"]=tuple([cmap, self.value, cmap_legend_kws, 4, "cmap"])
623+
self.legend_dict[f"{self.value} (cmap)"]=tuple([cmap, self.value, cmap_legend_kws, cbar_height, "cmap"])
618624
# dot size legend:
619625
if type(self.s) == str:
620626
# s=self.kwargs.get('s',None)

PyComplexHeatmap/utils.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ def despine(fig=None, ax=None, top=True, right=True, left=False, bottom=False):
210210

211211

212212
# =============================================================================
213-
def _draw_figure(fig):
213+
def _draw_figure_deprecated(fig):
214214
"""
215215
Force draw of a matplotlib figure, accounting for back-compat.
216216
@@ -223,6 +223,17 @@ def _draw_figure(fig):
223223
except AttributeError:
224224
pass
225225

226+
def _draw_figure(fig):
227+
"""
228+
Force draw of a matplotlib figure, accounting for back-compat.
229+
fig.canvas.draw() re-renders the entire figure (every axis, every artist), which is very expensive for complex heatmaps. get_renderer() just returns the renderer object so you can measure sizes without triggering a full draw.
230+
231+
"""
232+
try:
233+
renderer = fig.canvas.get_renderer()
234+
except AttributeError:
235+
fig.canvas.draw()
236+
renderer = fig.canvas.get_renderer()
226237

227238
# =============================================================================
228239
def axis_ticklabels_overlap(labels):
@@ -364,10 +375,15 @@ def cluster_labels(labels=None, xticks=None, majority=True):
364375
"""
365376
clusters_x = collections.defaultdict(list)
366377
clusters_labels = {}
367-
scanned_labels = ""
378+
_sentinel = object()
379+
scanned_labels = _sentinel
368380
i = 0
369381
for label, x in zip(labels, xticks):
370-
if label != scanned_labels:
382+
is_nan = pd.isna(label)
383+
prev_is_nan = scanned_labels is not _sentinel and pd.isna(scanned_labels)
384+
if is_nan and prev_is_nan:
385+
pass # consecutive NaNs belong to the same cluster
386+
elif is_nan or label != scanned_labels:
371387
scanned_labels = label
372388
i += 1
373389
clusters_labels[i] = scanned_labels
@@ -376,18 +392,20 @@ def cluster_labels(labels=None, xticks=None, majority=True):
376392
cluster_size = collections.defaultdict(int)
377393
largest_cluster = {}
378394
for i in clusters_labels:
379-
if len(clusters_x[i]) > cluster_size[clusters_labels[i]]:
380-
cluster_size[clusters_labels[i]] = len(clusters_x[i])
381-
largest_cluster[clusters_labels[i]] = i
395+
lbl = clusters_labels[i]
396+
key = lbl if not pd.isna(lbl) else "__nan__"
397+
if len(clusters_x[i]) > cluster_size[key]:
398+
cluster_size[key] = len(clusters_x[i])
399+
largest_cluster[key] = i
382400
labels = [
383401
clusters_labels[i]
384402
for i in clusters_x
385-
if i == largest_cluster[clusters_labels[i]]
403+
if i == largest_cluster[clusters_labels[i] if not pd.isna(clusters_labels[i]) else "__nan__"]
386404
]
387405
x = [
388406
np.mean(clusters_x[i])
389407
for i in clusters_x
390-
if i == largest_cluster[clusters_labels[i]]
408+
if i == largest_cluster[clusters_labels[i] if not pd.isna(clusters_labels[i]) else "__nan__"]
391409
]
392410
return labels, x
393411

@@ -543,8 +561,8 @@ def plot_cmap_legend(
543561
cbar_kws.setdefault("fraction", 1)
544562
cbar_kws.setdefault("shrink", 1)
545563
cbar_kws.setdefault("pad", 0)
546-
# cbar_kws.setdefault("extend", 'both')
547-
# cbar_kws.setdefault("extendfrac", 0.1)
564+
cbar_kws.setdefault("extend", 'both')
565+
cbar_kws.setdefault("extendfrac", 0.1)
548566
vmax = cbar_kws.pop("vmax", 1)
549567
vmin = cbar_kws.pop("vmin", 0)
550568
# print(vmin,vmax,'vmax,vmin')
@@ -765,14 +783,11 @@ def plot_legend_list(
765783
) # labelpad unit is points
766784
left = ax.get_position().x1 + pad
767785
if legend_width is None:
768-
# try:
769786
legend_width = (
770787
cal_legend_width(legend_list) + 3
771788
) # base width for color rectangle is set to 3 mm
772789
if verbose > 0:
773790
print(f"Estimated legend width: {legend_width} mm")
774-
# except:
775-
# legend_width=15
776791
legend_width = (
777792
legend_width * mm2inch * ax.figure.dpi / ax.figure.get_window_extent().width
778793
) # mm to px to fraction
@@ -803,14 +818,14 @@ def plot_legend_list(
803818
h_gap=h_gap * mm2inch * ax.figure.dpi # pixels
804819
i = 0
805820
while i <= len(legend_list) - 1:
806-
obj, title, legend_kws, n, lgd_t = legend_list[i]
821+
obj, title, legend_kws, lgd_h, lgd_t = legend_list[i]
807822
ax1 = legend_axes[-1] # ax for the legend on the right
808823
ax1.set_axis_off()
809824
color_text = legend_kws.pop("color_text", True)
810825
if lgd_t == "cmap": # type(obj)==str: # a cmap, plot colorbar
811826
f = (
812-
15 * mm2inch * ax.figure.dpi / ax.figure.get_window_extent().height
813-
) # 15 mm
827+
lgd_h * mm2inch * ax.figure.dpi / ax.figure.get_window_extent().height
828+
) # 15 mm in default for colorbar height, f is a fraction of legend height
814829
if y - f < 0: # add a new column of axes to plot legends
815830
offset = (
816831
lgd_col_max_width + h_gap + ax.yaxis.labelpad * ax.figure.dpi / 72
@@ -861,7 +876,7 @@ def plot_legend_list(
861876
# print(obj, title, legend_kws)
862877
legend_kws["bbox_to_anchor"] = (
863878
leg_pos.x0,
864-
y,
879+
y, # y is the bottom position of the first legend (from top to the bottom)
865880
) # lower left position of the box.
866881
# x, y, width, height #kws['bbox_transform'] = ax.figure.transFigure
867882
# ax1.scatter(leg_pos.x0,y,s=6,color='red',zorder=20,transform=ax1.figure.transFigure)
-1.25 KB
Binary file not shown.
-299 Bytes
Binary file not shown.
-38.2 KB
Binary file not shown.
34 Bytes
Binary file not shown.

docs/build/doctrees/nbsphinx/notebooks/GBM.ipynb

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,21 +1540,13 @@
15401540
"plt.savefig(\"oncoPrint.horizontal.pdf\",bbox_inches='tight')\n",
15411541
"plt.show()"
15421542
]
1543-
},
1544-
{
1545-
"cell_type": "code",
1546-
"execution_count": null,
1547-
"id": "5411e7cd-8b50-4d7d-bbd4-df4b4b0b9288",
1548-
"metadata": {},
1549-
"outputs": [],
1550-
"source": []
15511543
}
15521544
],
15531545
"metadata": {
15541546
"kernelspec": {
1555-
"display_name": "m3c",
1547+
"display_name": "Python 3 (ipykernel)",
15561548
"language": "python",
1557-
"name": "m3c"
1549+
"name": "python3"
15581550
},
15591551
"language_info": {
15601552
"codemirror_mode": {
@@ -1566,7 +1558,7 @@
15661558
"name": "python",
15671559
"nbconvert_exporter": "python",
15681560
"pygments_lexer": "ipython3",
1569-
"version": "3.10.18"
1561+
"version": "3.8.19"
15701562
}
15711563
},
15721564
"nbformat": 4,

0 commit comments

Comments
 (0)