marsilea 0.3.2__py3-none-any.whl → 0.3.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
marsilea/plotter/mesh.py CHANGED
@@ -1,5 +1,4 @@
1
- __all__ = ["ColorMesh", "Colors", "SizedMesh", "MarkerMesh",
2
- "TextMesh", "PatchMesh"]
1
+ __all__ = ["ColorMesh", "Colors", "SizedMesh", "MarkerMesh", "TextMesh", "PatchMesh"]
3
2
 
4
3
  import numpy as np
5
4
  import pandas as pd
@@ -7,15 +6,13 @@ import warnings
7
6
  from itertools import cycle
8
7
  from legendkit import ColorArt, CatLegend, SizeLegend
9
8
  from matplotlib.cm import ScalarMappable
10
- from matplotlib.colors import ListedColormap, TwoSlopeNorm, Normalize, \
11
- is_color_like
9
+ from matplotlib.colors import ListedColormap, TwoSlopeNorm, Normalize, is_color_like
12
10
  from typing import Mapping
13
11
 
14
12
  from ._utils import _format_label
15
13
  from .base import RenderPlan
16
14
  from ..layout import close_ticks
17
- from ..utils import relative_luminance, get_colormap, ECHARTS16, \
18
- get_canvas_size_by_data
15
+ from ..utils import relative_luminance, get_colormap, ECHARTS16, get_canvas_size_by_data
19
16
 
20
17
 
21
18
  def _mask_data(data, mask=None):
@@ -36,8 +33,7 @@ class MeshBase(RenderPlan):
36
33
  render_main = True
37
34
  _legend_kws = {}
38
35
 
39
- def _process_cmap(self, data, vmin, vmax,
40
- cmap, norm, center):
36
+ def _process_cmap(self, data, vmin, vmax, cmap, norm, center):
41
37
  self.cmap = "coolwarm" if cmap is None else cmap
42
38
  self.norm = norm
43
39
 
@@ -76,7 +72,7 @@ class ColorMesh(MeshBase):
76
72
  alpha : float
77
73
  The transparency value
78
74
  linewidth : float
79
- The width of grid line
75
+ The width of grid line
80
76
  linecolor : color
81
77
  The color of grid line
82
78
  annot : bool, array-like
@@ -89,9 +85,9 @@ class ColorMesh(MeshBase):
89
85
  See :class:`matplotlib.colorbar.Colorbar`,
90
86
  label : str
91
87
  The label of the plot, only show if added to the side plot
92
- label_loc : str
88
+ label_loc : {'top', 'bottom', 'left', 'right'}
93
89
  Where to add the label
94
- props : dict
90
+ label_props : dict
95
91
  See :class:`matplotlib.text.Text`
96
92
  kwargs :
97
93
 
@@ -127,12 +123,27 @@ class ColorMesh(MeshBase):
127
123
 
128
124
  """
129
125
 
130
- def __init__(self, data, cmap=None, norm=None, vmin=None, vmax=None,
131
- mask=None, center=None, alpha=None, linewidth=None,
132
- linecolor=None, annot=None, fmt=None, annot_kws=None,
133
- cbar_kws=None, label=None, label_loc=None, props=None,
134
- **kwargs,
135
- ):
126
+ def __init__(
127
+ self,
128
+ data,
129
+ cmap=None,
130
+ norm=None,
131
+ vmin=None,
132
+ vmax=None,
133
+ mask=None,
134
+ center=None,
135
+ alpha=None,
136
+ linewidth=None,
137
+ linecolor=None,
138
+ annot=None,
139
+ fmt=None,
140
+ annot_kws=None,
141
+ cbar_kws=None,
142
+ label=None,
143
+ label_loc=None,
144
+ label_props=None,
145
+ **kwargs,
146
+ ):
136
147
  self.data = _mask_data(data, mask)
137
148
  self.alpha = alpha
138
149
  self.linewidth = linewidth
@@ -145,11 +156,11 @@ class ColorMesh(MeshBase):
145
156
  else:
146
157
  self.annot = True
147
158
  self.annotated_texts = annot
148
- self.fmt = "0" if fmt is None else fmt
159
+ self.fmt = ".2g" if fmt is None else fmt
149
160
  self.annot_kws = {} if annot_kws is None else annot_kws
150
161
  self._process_cmap(data, vmin, vmax, cmap, norm, center)
151
162
 
152
- self.set_label(label, label_loc, props)
163
+ self.set_label(label, label_loc, label_props)
153
164
  self.set_data(self.data, self.annotated_texts)
154
165
  self._legend_kws = dict(title=self.label)
155
166
  if cbar_kws is not None:
@@ -161,13 +172,17 @@ class ColorMesh(MeshBase):
161
172
  """Add textual labels with the value in each cell."""
162
173
  mesh.update_scalarmappable()
163
174
  height, width = texts.shape
164
- xpos, ypos = np.meshgrid(np.arange(width) + .5, np.arange(height) + .5)
165
- for x, y, m, color, val in zip(xpos.flat, ypos.flat,
166
- mesh.get_array(), mesh.get_facecolors(),
167
- texts.flat):
175
+ xpos, ypos = np.meshgrid(np.arange(width) + 0.5, np.arange(height) + 0.5)
176
+ for x, y, m, color, val in zip(
177
+ xpos.flat,
178
+ ypos.flat,
179
+ mesh.get_array().flatten(),
180
+ mesh.get_facecolors(),
181
+ texts.flat,
182
+ ):
168
183
  if m is not np.ma.masked:
169
184
  lum = relative_luminance(color)
170
- text_color = ".15" if lum > .408 else "w"
185
+ text_color = ".15" if lum > 0.408 else "w"
171
186
  annotation = _format_label(val, self.fmt)
172
187
  text_kwargs = dict(color=text_color, ha="center", va="center")
173
188
  text_kwargs.update(self.annot_kws)
@@ -183,10 +198,14 @@ class ColorMesh(MeshBase):
183
198
  if self.is_flank:
184
199
  values = values.T
185
200
  texts = texts.T
186
- mesh = ax.pcolormesh(values, cmap=self.cmap, norm=self.norm,
187
- linewidth=self.linewidth,
188
- edgecolor=self.linecolor,
189
- **self.kwargs, )
201
+ mesh = ax.pcolormesh(
202
+ values,
203
+ cmap=self.cmap,
204
+ norm=self.norm,
205
+ linewidth=self.linewidth,
206
+ edgecolor=self.linecolor,
207
+ **self.kwargs,
208
+ )
190
209
  if self.annot:
191
210
  self._annotate_text(ax, mesh, texts)
192
211
  # set the mesh for legend
@@ -216,9 +235,11 @@ def _enough_colors(n_colors, n_cats):
216
235
  # Inform the user if the same color
217
236
  # will be used more than once
218
237
  if n_colors < n_cats:
219
- warnings.warn(f"Current colormap has only {n_colors} colors "
220
- f"which is less that your input "
221
- f"with {n_cats} elements")
238
+ warnings.warn(
239
+ f"Current colormap has only {n_colors} colors "
240
+ f"which is less that your input "
241
+ f"with {n_cats} elements"
242
+ )
222
243
 
223
244
 
224
245
  class Colors(MeshBase):
@@ -240,11 +261,11 @@ class Colors(MeshBase):
240
261
  The label of the plot, only show if added to the side plot
241
262
  label_loc : str
242
263
  Where to add the label
243
- props : dict
264
+ label_props : dict
244
265
  See :class:`matplotlib.text.Text`
245
- legend_kws :
266
+ legend_kws :
246
267
  See :class:`legendkit.legend`
247
- kwargs :
268
+ kwargs :
248
269
  Pass to :meth:`pcolormesh <matplotlib.axes.Axes.pcolormesh>`
249
270
 
250
271
  See Also
@@ -276,12 +297,22 @@ class Colors(MeshBase):
276
297
 
277
298
  """
278
299
 
279
- def __init__(self, data, palette=None, cmap=None, mask=None,
280
- linewidth=None, linecolor=None,
281
- label=None, label_loc=None, props=None, legend_kws=None,
282
- **kwargs):
300
+ def __init__(
301
+ self,
302
+ data,
303
+ palette=None,
304
+ cmap=None,
305
+ mask=None,
306
+ linewidth=None,
307
+ linecolor=None,
308
+ label=None,
309
+ label_loc=None,
310
+ label_props=None,
311
+ legend_kws=None,
312
+ **kwargs,
313
+ ):
283
314
  data = np.asarray(data)
284
- self.set_label(label, label_loc, props)
315
+ self.set_label(label, label_loc, label_props)
285
316
  self.linewidth = linewidth
286
317
  self.linecolor = linecolor
287
318
  self.kwargs = kwargs
@@ -350,10 +381,15 @@ class Colors(MeshBase):
350
381
 
351
382
  if self.is_flank:
352
383
  data = data.T
353
- ax.pcolormesh(data, cmap=self.render_cmap,
354
- linewidth=self.linewidth,
355
- edgecolor=self.linecolor,
356
- vmin=0, vmax=self.vmax, **self.kwargs)
384
+ ax.pcolormesh(
385
+ data,
386
+ cmap=self.render_cmap,
387
+ linewidth=self.linewidth,
388
+ edgecolor=self.linecolor,
389
+ vmin=0,
390
+ vmax=self.vmax,
391
+ **self.kwargs,
392
+ )
357
393
  ax.set_axis_off()
358
394
  ax.invert_yaxis()
359
395
 
@@ -400,9 +436,9 @@ class SizedMesh(MeshBase):
400
436
  See :mod:`matplotlib.markers`
401
437
  label : str
402
438
  The label of the plot, only show if added to the side plot
403
- label_loc : str
439
+ label_loc : {'top', 'bottom', 'left', 'right'}
404
440
  Where to add the label
405
- props : dict
441
+ label_props : dict
406
442
  See :class:`matplotlib.text.Text`
407
443
  legend : bool, default: True
408
444
  Whether to add a legend
@@ -443,14 +479,31 @@ class SizedMesh(MeshBase):
443
479
 
444
480
  """
445
481
 
446
- def __init__(self, size, color=None, cmap=None, norm=None,
447
- vmin=None, vmax=None, alpha=None,
448
- center=None, sizes=(1, 200), size_norm=None,
449
- edgecolor=None, linewidth=1,
450
- frameon=True, palette=None, marker="o",
451
- label=None, label_loc=None, props=None,
452
- legend=True, size_legend_kws=None, color_legend_kws=None,
453
- **kwargs):
482
+ def __init__(
483
+ self,
484
+ size,
485
+ color=None,
486
+ cmap=None,
487
+ norm=None,
488
+ vmin=None,
489
+ vmax=None,
490
+ alpha=None,
491
+ center=None,
492
+ sizes=(1, 200),
493
+ size_norm=None,
494
+ edgecolor=None,
495
+ linewidth=1,
496
+ frameon=True,
497
+ palette=None,
498
+ marker="o",
499
+ label=None,
500
+ label_loc=None,
501
+ label_props=None,
502
+ legend=True,
503
+ size_legend_kws=None,
504
+ color_legend_kws=None,
505
+ **kwargs,
506
+ ):
454
507
  # normalize size
455
508
  size = np.asarray(size)
456
509
  if size_norm is None:
@@ -487,15 +540,14 @@ class SizedMesh(MeshBase):
487
540
  self.cmap = ListedColormap(render_colors)
488
541
  self.color2d = encode_numeric(color, encoder)
489
542
  else:
490
- self._process_cmap(color, vmin, vmax, cmap,
491
- norm, center)
543
+ self._process_cmap(color, vmin, vmax, cmap, norm, center)
492
544
  self.color2d = color
493
545
  self._has_colormesh = True
494
546
  self.alpha = alpha
495
547
  self.frameon = frameon
496
548
  self.edgecolor = edgecolor
497
549
  self.linewidth = linewidth
498
- self.set_label(label, label_loc, props)
550
+ self.set_label(label, label_loc, label_props)
499
551
  self.kwargs = kwargs
500
552
 
501
553
  self._collections = None
@@ -511,19 +563,16 @@ class SizedMesh(MeshBase):
511
563
  size_color = self.color
512
564
  else:
513
565
  size_color = "black"
514
- handler_kw = dict(edgecolor=self.edgecolor,
515
- linewidth=self.linewidth)
566
+ handler_kw = dict(edgecolor=self.edgecolor, linewidth=self.linewidth)
516
567
  options = dict(
517
568
  colors=size_color,
518
569
  handle=self.marker,
519
- show_at=(.1, .25, .5, .75, 1.),
570
+ show_at=(0.1, 0.25, 0.5, 0.75, 1.0),
520
571
  spacing="uniform",
521
- handler_kw=handler_kw)
572
+ handler_kw=handler_kw,
573
+ )
522
574
  options.update(self.size_legend_kws)
523
- size_legend = SizeLegend(self.size_matrix,
524
- array=self.orig_size,
525
- **options
526
- )
575
+ size_legend = SizeLegend(self.size_matrix, array=self.orig_size, **options)
527
576
 
528
577
  if self._has_colormesh & (self.color != "none"):
529
578
  if self.palette is not None:
@@ -538,13 +587,10 @@ class SizedMesh(MeshBase):
538
587
  frameon=False,
539
588
  )
540
589
  options.update(self.color_legend_kws)
541
- color_legend = CatLegend(labels=labels, colors=colors,
542
- **options
543
- )
590
+ color_legend = CatLegend(labels=labels, colors=colors, **options)
544
591
  else:
545
592
  ScalarMappable(norm=self.norm, cmap=self.cmap)
546
- color_legend = ColorArt(self._collections,
547
- **self.color_legend_kws)
593
+ color_legend = ColorArt(self._collections, **self.color_legend_kws)
548
594
  return [size_legend, color_legend]
549
595
  else:
550
596
  return size_legend
@@ -560,11 +606,15 @@ class SizedMesh(MeshBase):
560
606
  yticks = np.arange(Y) + 0.5
561
607
  xv, yv = np.meshgrid(xticks, yticks)
562
608
 
563
- options = dict(s=size, edgecolor=self.edgecolor,
564
- linewidths=self.linewidth, marker=self.marker)
609
+ options = dict(
610
+ s=size,
611
+ edgecolor=self.edgecolor,
612
+ linewidths=self.linewidth,
613
+ marker=self.marker,
614
+ )
565
615
 
566
616
  if self.color is not None:
567
- options['c'] = self.color
617
+ options["c"] = self.color
568
618
  else:
569
619
  options.update(dict(c=color, norm=self.norm, cmap=self.cmap))
570
620
  self._collections = ax.scatter(xv, yv, **options, **self.kwargs)
@@ -598,9 +648,9 @@ class MarkerMesh(MeshBase):
598
648
  The of marker in fontsize unit
599
649
  label : str
600
650
  The label of the plot, only show when added to the side plot
601
- label_loc : str
651
+ label_loc : {'top', 'bottom', 'left', 'right'}
602
652
  The position of the label
603
- props : dict
653
+ label_props : dict
604
654
  See :class:`matplotlib.text.Text`
605
655
  kwargs :
606
656
 
@@ -617,20 +667,31 @@ class MarkerMesh(MeshBase):
617
667
  >>> MarkerMesh(data, color="darkgreen", marker="x", size=50).render(ax)
618
668
 
619
669
  """
670
+
620
671
  render_main = True
621
672
 
622
- def __init__(self, data, color="black", marker="*", size=35,
623
- label=None, label_loc=None, props=None, **kwargs):
673
+ def __init__(
674
+ self,
675
+ data,
676
+ color="black",
677
+ marker="*",
678
+ size=35,
679
+ label=None,
680
+ label_loc=None,
681
+ label_props=None,
682
+ **kwargs,
683
+ ):
624
684
  self.set_data(np.asarray(data))
625
685
  self.color = color
626
686
  self.marker = marker
627
- self.set_label(label, label_loc, props)
687
+ self.set_label(label, label_loc, label_props)
628
688
  self.kwargs = kwargs
629
689
  self.marker_size = size
630
690
 
631
691
  def get_legends(self):
632
- return CatLegend(colors=[self.color], labels=[self.label],
633
- handle=self.marker, draw=False)
692
+ return CatLegend(
693
+ colors=[self.color], labels=[self.label], handle=self.marker, draw=False
694
+ )
634
695
 
635
696
  def render_ax(self, spec):
636
697
  ax = spec.ax
@@ -641,8 +702,14 @@ class MarkerMesh(MeshBase):
641
702
  yticks = np.arange(Y) + 0.5
642
703
  yv, xv = np.where(data)
643
704
 
644
- ax.scatter(xv + .5, yv + .5, s=self.marker_size,
645
- c=self.color, marker=self.marker, **self.kwargs)
705
+ ax.scatter(
706
+ xv + 0.5,
707
+ yv + 0.5,
708
+ s=self.marker_size,
709
+ c=self.color,
710
+ marker=self.marker,
711
+ **self.kwargs,
712
+ )
646
713
 
647
714
  close_ticks(ax)
648
715
  ax.set_xlim(0, xticks[-1] + 0.5)
@@ -650,15 +717,40 @@ class MarkerMesh(MeshBase):
650
717
  ax.invert_yaxis()
651
718
 
652
719
 
653
- # TODO: Maybe a text mesh
654
720
  class TextMesh(MeshBase):
721
+ """The mesh that draw text on each cell
722
+
723
+ Parameters
724
+ ----------
725
+ texts : np.ndarray
726
+ The text to draw
727
+ color : color
728
+ The color of the text
729
+ label : str
730
+ The label of the plot, only show when added to the side plot
731
+ label_loc : {'top', 'bottom', 'left', 'right'}
732
+ The position of the label
733
+ label_props : dict
734
+ Props for label :class:`matplotlib.text.Text`
735
+ kwargs : dict
736
+ Pass to :meth:`matplotlib.axes.Axes.text`
737
+
738
+ """
739
+
655
740
  render_main = True
656
741
 
657
- def __init__(self, texts, color="black",
658
- label=None, label_loc=None, props=None, **kwargs):
742
+ def __init__(
743
+ self,
744
+ texts,
745
+ color="black",
746
+ label=None,
747
+ label_loc=None,
748
+ label_props=None,
749
+ **kwargs,
750
+ ):
659
751
  self.set_data(self.data_validator(texts))
660
752
  self.color = color
661
- self.set_label(label, label_loc, props)
753
+ self.set_label(label, label_loc, label_props)
662
754
  self.kwargs = kwargs
663
755
 
664
756
  def render_ax(self, spec):
@@ -669,11 +761,15 @@ class TextMesh(MeshBase):
669
761
  xticks = np.arange(X) + 0.5
670
762
  yticks = np.arange(Y) + 0.5
671
763
 
672
- text_options = {"ha": "center", "va": "center",
673
- "color": self.color, **self.kwargs}
764
+ text_options = {
765
+ "ha": "center",
766
+ "va": "center",
767
+ "color": self.color,
768
+ **self.kwargs,
769
+ }
674
770
  for x in range(X):
675
771
  for y in range(Y):
676
- ax.text(x + .5, y + .5, data[y, x], **text_options)
772
+ ax.text(x + 0.5, y + 0.5, data[y, x], **text_options)
677
773
 
678
774
  close_ticks(ax)
679
775
  ax.set_xlim(0, xticks[-1] + 0.5)