marsilea 0.5.4__py3-none-any.whl → 0.5.6__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/__init__.py CHANGED
@@ -1,6 +1,8 @@
1
1
  """Declarative creation of composable visualization"""
2
2
 
3
- __version__ = "0.5.4"
3
+ from ._version import version
4
+
5
+ __version__ = version
4
6
 
5
7
  import marsilea.plotter as plotter
6
8
  from ._deform import Deformation
marsilea/_version.py ADDED
@@ -0,0 +1 @@
1
+ version = "0.5.6"
marsilea/base.py CHANGED
@@ -440,18 +440,16 @@ class WhiteBoard(LegendMaker):
440
440
  self.add_plot("bottom", plot, name, size, pad, legend)
441
441
 
442
442
  def _render_plan(self):
443
- for plan in self._col_plan:
444
- axes = self.layout.get_ax(plan.name)
445
- plan.render(axes)
443
+ try:
444
+ for plan in self._col_plan + self._row_plan:
445
+ axes = self.layout.get_ax(plan.name)
446
+ plan.render(axes)
446
447
 
447
- # render other plots
448
- for plan in self._row_plan:
449
- axes = self.layout.get_ax(plan.name)
450
- plan.render(axes)
451
-
452
- main_ax = self.get_main_ax()
453
- for plan in self._get_layers_zorder():
454
- plan.render(main_ax)
448
+ main_ax = self.get_main_ax()
449
+ for plan in self._get_layers_zorder():
450
+ plan.render(main_ax)
451
+ except Exception as e:
452
+ raise Exception(f"An error occur during rendering of {plan}") from e
455
453
 
456
454
  def add_layer(self, plot: RenderPlan, zorder=None, name=None, legend=True):
457
455
  """Add a plotter to the main canvas
@@ -655,7 +653,8 @@ class WhiteBoard(LegendMaker):
655
653
  def save(self, fname, **kwargs):
656
654
  """Save the figure to a file
657
655
 
658
- This will force a re-render of the figure
656
+ Save the current opened figure to a file, if no figure is open,
657
+ a render will be performed first.
659
658
 
660
659
  Parameters
661
660
  ----------
@@ -665,7 +664,8 @@ class WhiteBoard(LegendMaker):
665
664
  Additional options for saving the figure, will be passed to :meth:`~matplotlib.pyplot.savefig`
666
665
 
667
666
  """
668
- self.render()
667
+ if self.figure is None:
668
+ self.render()
669
669
  save_options = dict(bbox_inches="tight")
670
670
  save_options.update(kwargs)
671
671
  self.figure.savefig(fname, **save_options)
@@ -1364,23 +1364,19 @@ class ClusterBoard(WhiteBoard):
1364
1364
 
1365
1365
  def _render_plan(self):
1366
1366
  deform = self.get_deform()
1367
- for plan in self._col_plan:
1368
- if plan.allow_split:
1369
- plan.set_deform(deform)
1370
- axes = self.layout.get_ax(plan.name)
1371
- plan.render(axes)
1367
+ try:
1368
+ for plan in self._col_plan + self._row_plan:
1369
+ if plan.allow_split:
1370
+ plan.set_deform(deform)
1371
+ axes = self.layout.get_ax(plan.name)
1372
+ plan.render(axes)
1372
1373
 
1373
- # render other plots
1374
- for plan in self._row_plan:
1375
- if plan.allow_split:
1374
+ main_ax = self.get_main_ax()
1375
+ for plan in self._get_layers_zorder():
1376
1376
  plan.set_deform(deform)
1377
- axes = self.layout.get_ax(plan.name)
1378
- plan.render(axes)
1379
-
1380
- main_ax = self.get_main_ax()
1381
- for plan in self._get_layers_zorder():
1382
- plan.set_deform(deform)
1383
- plan.render(main_ax)
1377
+ plan.render(main_ax)
1378
+ except Exception as e:
1379
+ raise Exception(f"An error occur during rending of {plan}") from e
1384
1380
 
1385
1381
  def get_deform(self):
1386
1382
  """Return the deformation object of the cluster data"""
marsilea/layout.py CHANGED
@@ -461,8 +461,8 @@ class CrossLayout(_MarginMixin):
461
461
  y = self.get_side_size("bottom")
462
462
  if self.is_composite:
463
463
  return x, y
464
- x += self.margin.bottom
465
- y += self.margin.left
464
+ x += self.margin.left
465
+ y += self.margin.bottom
466
466
  return x, y
467
467
  else:
468
468
  return self.anchor
marsilea/plotter/mesh.py CHANGED
@@ -1,14 +1,21 @@
1
1
  __all__ = ["ColorMesh", "Colors", "SizedMesh", "MarkerMesh", "TextMesh", "PatchMesh"]
2
2
 
3
- import numpy as np
4
- import pandas as pd
5
3
  import warnings
6
4
  from itertools import cycle
7
- from legendkit import ColorArt, CatLegend, SizeLegend
8
- from matplotlib.cm import ScalarMappable
9
- from matplotlib.colors import ListedColormap, TwoSlopeNorm, Normalize, is_color_like
10
5
  from typing import Mapping
11
6
 
7
+ import numpy as np
8
+ import pandas as pd
9
+ from legendkit import ColorArt, CatLegend, SizeLegend, ListLegend
10
+ from matplotlib.cm import ScalarMappable
11
+ from matplotlib.colors import (
12
+ ListedColormap,
13
+ TwoSlopeNorm,
14
+ Normalize,
15
+ is_color_like,
16
+ to_hex,
17
+ )
18
+
12
19
  from ._utils import _format_label
13
20
  from .base import RenderPlan
14
21
  from ..layout import close_ticks
@@ -426,8 +433,9 @@ class SizedMesh(MeshBase):
426
433
  The range of the size of elements
427
434
  size_norm : :class:`matplotlib.colors.Normalize`
428
435
  A Normalize instance to map size
429
- edgecolor : color
430
- The border color of each elements
436
+ edgecolor : color or array of colors
437
+ The border color of each elements, if you want to control every element,
438
+ please supply a 2d array with the same shape as input.
431
439
  linewidth : float
432
440
  The width of the border of each elements
433
441
  frameon : bool
@@ -454,6 +462,10 @@ class SizedMesh(MeshBase):
454
462
  Control the size legend, See :class:`legendkit.size_legend`
455
463
  color_legend_kws : dict
456
464
  Control the color legend, See :class:`legendkit.colorart`
465
+ edgecolor_legend_text : list of str
466
+ The texts for the edgecolor legend
467
+ edgecolor_legend_kws : dict
468
+ Control the edgecolor legend, See :class:`legendkit.legend`
457
469
  kwargs : dict
458
470
  Pass to :meth:`matplotlib.axes.Axes.scatter`
459
471
 
@@ -511,6 +523,8 @@ class SizedMesh(MeshBase):
511
523
  legend=True,
512
524
  size_legend_kws=None,
513
525
  color_legend_kws=None,
526
+ edgecolor_legend_text=None,
527
+ edgecolor_legend_kws=None,
514
528
  **kwargs,
515
529
  ):
516
530
  # normalize size
@@ -528,6 +542,11 @@ class SizedMesh(MeshBase):
528
542
  self.legend = legend
529
543
  self.color_legend_kws = {} if color_legend_kws is None else color_legend_kws
530
544
  self.size_legend_kws = {} if size_legend_kws is None else size_legend_kws
545
+ self.edgecolor_legend_text = edgecolor_legend_text
546
+ self.edgecolor_legend_kws = (
547
+ {} if edgecolor_legend_kws is None else edgecolor_legend_kws
548
+ )
549
+
531
550
  self._has_colormesh = False
532
551
  # process color
533
552
  # By default, the circles colors are uniform
@@ -557,7 +576,25 @@ class SizedMesh(MeshBase):
557
576
  self._has_colormesh = True
558
577
  self.alpha = alpha
559
578
  self.frameon = frameon
560
- self.edgecolor = edgecolor
579
+
580
+ if edgecolor is not None:
581
+ if is_color_like(edgecolor):
582
+ self.edgecolor = np.repeat(edgecolor, size.size).reshape(size.shape)
583
+ self._single_edgecolor = True
584
+ else:
585
+ edgecolor = np.asarray(edgecolor)
586
+ if edgecolor.shape != self.size_matrix.shape:
587
+ raise ValueError(
588
+ "If use multiple edgecolors, "
589
+ "the shape must be the same as input size"
590
+ )
591
+ # Covert to hex
592
+ edgecolor = [[to_hex(c) for c in row] for row in edgecolor]
593
+ self.edgecolor = np.asarray(edgecolor)
594
+ self._single_edgecolor = False
595
+ else:
596
+ self.edgecolor = np.full_like(size, "#00000000", dtype=object)
597
+ self._single_edgecolor = True
561
598
  self.linewidth = linewidth
562
599
  self.set_label(label, label_loc, label_props)
563
600
  self.grid = grid
@@ -566,7 +603,12 @@ class SizedMesh(MeshBase):
566
603
  self.kwargs = kwargs
567
604
 
568
605
  self._collections = None
569
- self.set_data(self.size_matrix, self.color2d)
606
+ self._transparent_marker = "none" in self.color2d
607
+ render_data = [self.size_matrix, self.color2d]
608
+ if self.edgecolor is not None:
609
+ render_data.append(self.edgecolor)
610
+
611
+ self.set_data(*render_data)
570
612
 
571
613
  def update_main_canvas_size(self):
572
614
  return get_canvas_size_by_data(self.orig_size.shape)
@@ -574,11 +616,20 @@ class SizedMesh(MeshBase):
574
616
  def get_legends(self):
575
617
  if not self.legend:
576
618
  return None
619
+
620
+ legends = []
621
+
577
622
  if self.color is not None:
578
623
  size_color = self.color
579
624
  else:
580
625
  size_color = "black"
581
- handler_kw = dict(edgecolor=self.edgecolor, linewidth=self.linewidth)
626
+ edgecolor = None
627
+ if self.edgecolor is not None:
628
+ if self._single_edgecolor:
629
+ edgecolor = self.edgecolor.flatten()[0]
630
+ if self._transparent_marker & (edgecolor is None):
631
+ edgecolor = "black"
632
+ handler_kw = dict(edgecolor=edgecolor, linewidth=self.linewidth)
582
633
  options = dict(
583
634
  colors=size_color,
584
635
  handle=self.marker,
@@ -588,8 +639,27 @@ class SizedMesh(MeshBase):
588
639
  )
589
640
  options.update(self.size_legend_kws)
590
641
  size_legend = SizeLegend(self.size_matrix, array=self.orig_size, **options)
591
-
592
- if self._has_colormesh & (self.color != "none"):
642
+ legends.append(size_legend)
643
+
644
+ # Construct edgecolor legend
645
+ if self.edgecolor is not None:
646
+ if self.edgecolor_legend_text is not None:
647
+ unique_ecs = np.unique(self.edgecolor)
648
+ if len(self.edgecolor_legend_text) == len(unique_ecs):
649
+ legend_items = [
650
+ ("circle", text, dict(ec=ec, fc="none", lw=self.linewidth))
651
+ for text, ec in zip(self.edgecolor_legend_text, unique_ecs)
652
+ ]
653
+ ec_legend = ListLegend(
654
+ legend_items=legend_items, **self.color_legend_kws
655
+ )
656
+ legends.append(ec_legend)
657
+ else:
658
+ raise ValueError(
659
+ "If edgecolor legend text is provided, the number of unique edgecolors "
660
+ "must match the number of texts"
661
+ )
662
+ if self._has_colormesh & (self._transparent_marker):
593
663
  if self.palette is not None:
594
664
  labels, colors = [], []
595
665
  for label, c in self.palette.items():
@@ -606,16 +676,20 @@ class SizedMesh(MeshBase):
606
676
  else:
607
677
  ScalarMappable(norm=self.norm, cmap=self.cmap)
608
678
  color_legend = ColorArt(self._collections, **self.color_legend_kws)
609
- return [size_legend, color_legend]
679
+ legends.append(color_legend)
680
+
681
+ if len(legends) == 1:
682
+ return legends[0]
610
683
  else:
611
- return size_legend
684
+ return legends
612
685
 
613
686
  def render_ax(self, spec):
614
687
  ax = spec.ax
615
- size, color = spec.data
688
+ size, color, edgecolors = spec.data
616
689
  if self.is_flank:
617
690
  size = size.T
618
691
  color = color.T
692
+ edgecolors = edgecolors.T
619
693
  Y, X = size.shape
620
694
  xticks = np.arange(X) + 0.5
621
695
  yticks = np.arange(Y) + 0.5
@@ -633,14 +707,14 @@ class SizedMesh(MeshBase):
633
707
 
634
708
  options = dict(
635
709
  s=size,
636
- edgecolor=self.edgecolor,
710
+ edgecolors=edgecolors.flatten(),
637
711
  linewidths=self.linewidth,
638
712
  marker=self.marker,
639
713
  )
640
714
  if self.color is not None:
641
- options["c"] = color
715
+ options["c"] = color.flatten()
642
716
  else:
643
- options.update(dict(c=color, norm=self.norm, cmap=self.cmap))
717
+ options.update(dict(c=color.flatten(), norm=self.norm, cmap=self.cmap))
644
718
  self._collections = ax.scatter(xv, yv, **options, **self.kwargs)
645
719
 
646
720
  close_ticks(ax)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: marsilea
3
- Version: 0.5.4
3
+ Version: 0.5.6
4
4
  Summary: Declarative creation of composable visualizations
5
5
  Project-URL: Home, https://github.com/Marsilea-viz/marsilea
6
6
  Project-URL: Documentation, https://marsilea.readthedocs.io
@@ -1,13 +1,14 @@
1
- marsilea/__init__.py,sha256=Ml3xgdyN-IinhsaejVadUjIURHJzg8fhi2Y_uERAuI8,595
1
+ marsilea/__init__.py,sha256=mMtII0QzSVGrr1vJXTglr6wBd2AHElrDnDp1iGQbOxo,626
2
2
  marsilea/_api.py,sha256=tymWZHfjhx8-0NNd9762znfdIu36NrARRweEIr5L1mA,283
3
3
  marsilea/_deform.py,sha256=QRz4OGXMsQzbiIkC3ASzZayMPhHhoFsEK38oBzSeQG8,14440
4
- marsilea/base.py,sha256=CmzuwFu0JzgkNpPdI1SqAnkUxINShjT73hrJpksKjhE,46872
4
+ marsilea/_version.py,sha256=FtfC8ptaFH0Unc72bPRynMH_N62bxa4tnazGgIcgnTY,18
5
+ marsilea/base.py,sha256=GBgSirueH65nhfkunCVYhV7ePX4i01jog5gzkBBOsi0,46951
5
6
  marsilea/dataset.py,sha256=Qh8k1DhTiwP_Me709CkpNwRaYLDzlenRTIk0U6D58_g,4631
6
7
  marsilea/dendrogram.py,sha256=Ung43zseybZKzTEvH5P_ge3WGfsr7i7qsX7YEVDlC74,15590
7
8
  marsilea/exceptions.py,sha256=wN5ElUZxuaJKSnnwWdkNx6P-Oc16dzSuaRPbRKWIBEM,1046
8
9
  marsilea/heatmap.py,sha256=8Wo1NxFTBp1a7NOISmer0yQYWWgf51Xsvjav1h1vTYk,4316
9
10
  marsilea/layers.py,sha256=w_yvghwYAO112mVvp1krjUjEIQYq_1nz2uCsTUJdB-Y,12909
10
- marsilea/layout.py,sha256=yNuGvIDfWjbCksQZO-4aUWSA78LJwBBKF5Z5cthC6fM,39741
11
+ marsilea/layout.py,sha256=b6aynXl2senRYjaYnNikjIzK84RkXaBvAyt86VRWw-g,39741
11
12
  marsilea/upset.py,sha256=9m4Los-yVvVZOEHg6X-yfOdLA9eUBQdK1A_BkOaLIMA,30602
12
13
  marsilea/utils.py,sha256=y_KYs4ToiuKEsiBdmcIVtmxMXFpD4wKiJ0k7iBa11z8,2854
13
14
  marsilea/plotter/__init__.py,sha256=la30o20zYiHWN2RzElhS8MMCbGKbDDEe0WHXakq9OBQ,806
@@ -19,13 +20,13 @@ marsilea/plotter/bar.py,sha256=RWDsNbyCUKbybpsBOgbl43lVZc_ynZmTOevE-CtJ5KE,12354
19
20
  marsilea/plotter/base.py,sha256=b_NmrW_oNPc-HwQsjx1NsC2lknYK6qSaDp_7SxeoUEM,18938
20
21
  marsilea/plotter/bio.py,sha256=34tucmxs4LM3TFZoGsrjnXTolyrzYaHVEiRe4dzDH68,5040
21
22
  marsilea/plotter/images.py,sha256=gb0xIQhUch3rNAt3FfvuUoamSGEynoBBBky2eE754ec,9560
22
- marsilea/plotter/mesh.py,sha256=D6snenMjQ-rM9g3O4PGelSiaBNKmY2OPZU5onILlmO0,24530
23
+ marsilea/plotter/mesh.py,sha256=ARDPWrQLbdIS22B7Ct_aYPXY7IxwVKcICdBBhg8UplE,27482
23
24
  marsilea/plotter/range.py,sha256=sXWKrvpq7nU7giiHPjayM7h9Q7gblhjXxHm8ioB3cm4,3770
24
25
  marsilea/plotter/text.py,sha256=6S4mnAxLJLMkduKiyor03lPd86oTOJ5TojVREA9oU6s,37466
25
26
  oncoprinter/__init__.py,sha256=efshcAD1h9s-NVJj4HLU9-hXc_LtTeIrNYqLHl-sm_g,106
26
- oncoprinter/core.py,sha256=5KPnKW5ivlxPp14uJd0OtfTv-pXV2UEym8EbII2VCcw,11846
27
- oncoprinter/preset.py,sha256=mBk2tFCqoTj_1ZZKRYuv4j2I3NTBa6Swc9wjzbmxRVw,8238
28
- marsilea-0.5.4.dist-info/METADATA,sha256=9SS9_JT6N9FRdBenz-95mnzFY5LVal4lhMocoyvcUsA,7265
29
- marsilea-0.5.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
- marsilea-0.5.4.dist-info/licenses/LICENSE,sha256=RhHHDuP61qzKmfHtOQUVLZfCgMkKx9PXzxzkLtmAjHo,1078
31
- marsilea-0.5.4.dist-info/RECORD,,
27
+ oncoprinter/core.py,sha256=XBIwTsAvcA-mJSwHU0xpaTBCxBNh7yusmzblRATmekY,11866
28
+ oncoprinter/preset.py,sha256=lo6Llz31KVFt3QOFgFc0BunloZV1FxcFVpH8kOfarNw,8363
29
+ marsilea-0.5.6.dist-info/METADATA,sha256=Ve7qLr7YzOz8bKG10ZIgJHYxPSiGyOXfdhhQrk4oDTE,7265
30
+ marsilea-0.5.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
+ marsilea-0.5.6.dist-info/licenses/LICENSE,sha256=RhHHDuP61qzKmfHtOQUVLZfCgMkKx9PXzxzkLtmAjHo,1078
32
+ marsilea-0.5.6.dist-info/RECORD,,
oncoprinter/core.py CHANGED
@@ -168,7 +168,7 @@ class GenomicData:
168
168
  cs = {}
169
169
  for track, df in gb:
170
170
  cs[track] = Counter(df["event"])
171
- return pd.DataFrame(cs).fillna(0.0)
171
+ return pd.DataFrame(cs).fillna(0.0).loc[:, self.tracks]
172
172
 
173
173
  def get_sample_mutation_types(self):
174
174
  gb = self.data.groupby("sample", sort=False, observed=True)
oncoprinter/preset.py CHANGED
@@ -125,6 +125,9 @@ class MatchRule:
125
125
 
126
126
  MATCH_POOL = {
127
127
  Alteration.AMP: MatchRule(startswith="amp"),
128
+ Alteration.BACKGROUND: MatchRule(
129
+ contains=["background", "no", "none", "na", "nan", "bg"], flexible=True
130
+ ),
128
131
  Alteration.GAIN: MatchRule(startswith="gain"),
129
132
  Alteration.HOMDEL: MatchRule(
130
133
  startswith="homdel", contains=["deep", "deletion"], flexible=True