marsilea 0.5.4__tar.gz → 0.5.5__tar.gz

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.
Files changed (34) hide show
  1. {marsilea-0.5.4 → marsilea-0.5.5}/.gitignore +4 -1
  2. {marsilea-0.5.4 → marsilea-0.5.5}/PKG-INFO +1 -1
  3. {marsilea-0.5.4 → marsilea-0.5.5}/pyproject.toml +8 -2
  4. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/__init__.py +3 -1
  5. marsilea-0.5.5/src/marsilea/_version.py +1 -0
  6. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/layout.py +2 -2
  7. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/mesh.py +84 -14
  8. {marsilea-0.5.4 → marsilea-0.5.5}/LICENSE +0 -0
  9. {marsilea-0.5.4 → marsilea-0.5.5}/README.md +0 -0
  10. {marsilea-0.5.4 → marsilea-0.5.5}/setup.py +0 -0
  11. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/_api.py +0 -0
  12. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/_deform.py +0 -0
  13. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/base.py +0 -0
  14. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/dataset.py +0 -0
  15. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/dendrogram.py +0 -0
  16. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/exceptions.py +0 -0
  17. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/heatmap.py +0 -0
  18. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/layers.py +0 -0
  19. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/__init__.py +0 -0
  20. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/_seaborn.py +0 -0
  21. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/_utils.py +0 -0
  22. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/arc.py +0 -0
  23. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/area.py +0 -0
  24. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/bar.py +0 -0
  25. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/base.py +0 -0
  26. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/bio.py +0 -0
  27. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/images.py +0 -0
  28. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/range.py +0 -0
  29. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/plotter/text.py +0 -0
  30. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/upset.py +0 -0
  31. {marsilea-0.5.4 → marsilea-0.5.5}/src/marsilea/utils.py +0 -0
  32. {marsilea-0.5.4 → marsilea-0.5.5}/src/oncoprinter/__init__.py +0 -0
  33. {marsilea-0.5.4 → marsilea-0.5.5}/src/oncoprinter/core.py +0 -0
  34. {marsilea-0.5.4 → marsilea-0.5.5}/src/oncoprinter/preset.py +0 -0
@@ -161,4 +161,7 @@ cython_debug/
161
161
 
162
162
  *.ipynb
163
163
  .fonts/
164
- .DS_Store
164
+ .DS_Store
165
+
166
+ # Skip version file
167
+ *_version.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: marsilea
3
- Version: 0.5.4
3
+ Version: 0.5.5
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,5 +1,5 @@
1
1
  [build-system]
2
- requires = ["hatchling"]
2
+ requires = ["hatchling", "uv-dynamic-versioning"]
3
3
  build-backend = "hatchling.build"
4
4
 
5
5
  [project]
@@ -32,7 +32,13 @@ dependencies = [
32
32
  ]
33
33
 
34
34
  [tool.hatch.version]
35
- path = "src/marsilea/__init__.py"
35
+ source = "uv-dynamic-versioning"
36
+
37
+ [tool.hatch.build.hooks.version]
38
+ path = "src/marsilea/_version.py"
39
+ template = '''
40
+ version = "{version}"
41
+ '''
36
42
 
37
43
  [tool.hatch.build.targets.wheel]
38
44
  packages = ["src/marsilea", "src/oncoprinter"]
@@ -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
@@ -0,0 +1 @@
1
+ version = "0.5.5"
@@ -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
@@ -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,23 @@ 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
+ self.edgecolor = None
581
+ if edgecolor is not None:
582
+ if is_color_like(edgecolor):
583
+ self.edgecolor = np.repeat(edgecolor, size.size).reshape(size.shape)
584
+ self._single_edgecolor = True
585
+ else:
586
+ edgecolor = np.asarray(edgecolor)
587
+ if edgecolor.shape != self.size_matrix.shape:
588
+ raise ValueError(
589
+ "If use multiple edgecolors, "
590
+ "the shape must be the same as input size"
591
+ )
592
+ # Covert to hex
593
+ edgecolor = [[to_hex(c) for c in row] for row in edgecolor]
594
+ self.edgecolor = np.asarray(edgecolor)
595
+ self._single_edgecolor = False
561
596
  self.linewidth = linewidth
562
597
  self.set_label(label, label_loc, label_props)
563
598
  self.grid = grid
@@ -566,7 +601,11 @@ class SizedMesh(MeshBase):
566
601
  self.kwargs = kwargs
567
602
 
568
603
  self._collections = None
569
- self.set_data(self.size_matrix, self.color2d)
604
+ render_data = [self.size_matrix, self.color2d]
605
+ if self.edgecolor is not None:
606
+ render_data.append(self.edgecolor)
607
+
608
+ self.set_data(*render_data)
570
609
 
571
610
  def update_main_canvas_size(self):
572
611
  return get_canvas_size_by_data(self.orig_size.shape)
@@ -574,11 +613,18 @@ class SizedMesh(MeshBase):
574
613
  def get_legends(self):
575
614
  if not self.legend:
576
615
  return None
616
+
617
+ legends = []
618
+
577
619
  if self.color is not None:
578
620
  size_color = self.color
579
621
  else:
580
622
  size_color = "black"
581
- handler_kw = dict(edgecolor=self.edgecolor, linewidth=self.linewidth)
623
+ edgecolor = None
624
+ if self.edgecolor is not None:
625
+ if self._single_edgecolor:
626
+ edgecolor = self.edgecolor.flatten()[0]
627
+ handler_kw = dict(edgecolor=edgecolor, linewidth=self.linewidth)
582
628
  options = dict(
583
629
  colors=size_color,
584
630
  handle=self.marker,
@@ -588,6 +634,26 @@ class SizedMesh(MeshBase):
588
634
  )
589
635
  options.update(self.size_legend_kws)
590
636
  size_legend = SizeLegend(self.size_matrix, array=self.orig_size, **options)
637
+ legends.append(size_legend)
638
+
639
+ # Construct edgecolor legend
640
+ if self.edgecolor is not None:
641
+ if self.edgecolor_legend_text is not None:
642
+ unique_ecs = np.unique(self.edgecolor)
643
+ if len(self.edgecolor_legend_text) == len(unique_ecs):
644
+ legend_items = [
645
+ ("circle", text, dict(ec=ec, fc="none"))
646
+ for text, ec in zip(self.edgecolor_legend_text, unique_ecs)
647
+ ]
648
+ ec_legend = ListLegend(
649
+ legend_items=legend_items, **self.color_legend_kws
650
+ )
651
+ legends.append(ec_legend)
652
+ else:
653
+ raise ValueError(
654
+ "If edgecolor legend text is provided, the number of unique edgecolors "
655
+ "must match the number of texts"
656
+ )
591
657
 
592
658
  if self._has_colormesh & (self.color != "none"):
593
659
  if self.palette is not None:
@@ -606,16 +672,20 @@ class SizedMesh(MeshBase):
606
672
  else:
607
673
  ScalarMappable(norm=self.norm, cmap=self.cmap)
608
674
  color_legend = ColorArt(self._collections, **self.color_legend_kws)
609
- return [size_legend, color_legend]
675
+ legends.append(color_legend)
676
+
677
+ if len(legends) == 1:
678
+ return legends[0]
610
679
  else:
611
- return size_legend
680
+ return legends
612
681
 
613
682
  def render_ax(self, spec):
614
683
  ax = spec.ax
615
- size, color = spec.data
684
+ size, color, edgecolors = spec.data
616
685
  if self.is_flank:
617
686
  size = size.T
618
687
  color = color.T
688
+ edgecolors = edgecolors.T
619
689
  Y, X = size.shape
620
690
  xticks = np.arange(X) + 0.5
621
691
  yticks = np.arange(Y) + 0.5
@@ -633,7 +703,7 @@ class SizedMesh(MeshBase):
633
703
 
634
704
  options = dict(
635
705
  s=size,
636
- edgecolor=self.edgecolor,
706
+ edgecolors=edgecolors.flatten(),
637
707
  linewidths=self.linewidth,
638
708
  marker=self.marker,
639
709
  )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes