marsilea 0.4.2__py3-none-any.whl → 0.4.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/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Declarative creation of composable visualization"""
2
2
 
3
- __version__ = "0.4.2"
3
+ __version__ = "0.4.4"
4
4
 
5
5
  import marsilea.plotter as plotter
6
6
  from ._deform import Deformation
marsilea/_deform.py CHANGED
@@ -84,19 +84,29 @@ class Deformation:
84
84
  self.data_col_reindex = reindex
85
85
  self._col_clustered = False
86
86
 
87
- def set_cluster(self, col=None, row=None, use_meta=True, linkage=None, **kwargs):
87
+ def set_cluster(
88
+ self,
89
+ col=None,
90
+ row=None,
91
+ use_meta=True,
92
+ linkage=None,
93
+ meta_linkage=None,
94
+ **kwargs,
95
+ ):
88
96
  if col is not None:
89
97
  self.is_col_cluster = col
90
98
  self.col_cluster_kws = kwargs
91
99
  self._col_clustered = False
92
100
  self._use_col_meta = use_meta
93
101
  self.col_linkage = linkage
102
+ self.col_meta_linkage = meta_linkage
94
103
  if row is not None:
95
104
  self.is_row_cluster = row
96
105
  self.row_cluster_kws = kwargs
97
106
  self._row_clustered = False
98
107
  self._use_row_meta = use_meta
99
108
  self.row_linkage = linkage
109
+ self.row_meta_linkage = meta_linkage
100
110
 
101
111
  def get_data(self):
102
112
  data = self.data
@@ -204,7 +214,9 @@ class Deformation:
204
214
  Dendrogram(chunk, linkage=linkage, key=k, **self.row_cluster_kws)
205
215
  )
206
216
 
207
- dg = GroupDendrogram(dens, **self.row_cluster_kws)
217
+ dg = GroupDendrogram(
218
+ dens, linkage=self.row_meta_linkage, **self.row_cluster_kws
219
+ )
208
220
  if self._use_row_meta:
209
221
  self.row_chunk_index = dg.reorder_index
210
222
  else:
@@ -232,7 +244,9 @@ class Deformation:
232
244
  dens.append(
233
245
  Dendrogram(chunk.T, linkage=linkage, key=k, **self.col_cluster_kws)
234
246
  )
235
- dg = GroupDendrogram(dens, **self.col_cluster_kws)
247
+ dg = GroupDendrogram(
248
+ dens, linkage=self.col_meta_linkage, **self.col_cluster_kws
249
+ )
236
250
  if self._use_col_meta:
237
251
  self.col_chunk_index = dg.reorder_index
238
252
  else:
marsilea/base.py CHANGED
@@ -606,9 +606,15 @@ class WhiteBoard(LegendMaker):
606
606
  return compose_board
607
607
 
608
608
  def _freeze_flex_plots(self, figure):
609
+ main_cell = self.layout.main_cell
610
+ main_width = main_cell.width
611
+ main_height = main_cell.height
612
+
609
613
  for plan in self._col_plan + self._row_plan:
610
614
  if plan.size is None:
611
- render_size = plan.get_canvas_size(figure)
615
+ render_size = plan.get_canvas_size(
616
+ figure, main_width=main_width, main_height=main_height
617
+ )
612
618
  if render_size is not None:
613
619
  self.layout.set_render_size(plan.name, render_size)
614
620
 
@@ -835,6 +841,7 @@ class ClusterBoard(WhiteBoard):
835
841
  method=None,
836
842
  metric=None,
837
843
  linkage=None,
844
+ meta_linkage=None,
838
845
  add_meta=True,
839
846
  add_base=True,
840
847
  add_divider=True,
@@ -848,6 +855,7 @@ class ClusterBoard(WhiteBoard):
848
855
  size=0.5,
849
856
  pad=0.0,
850
857
  get_meta_center=None,
858
+ rasterized=False,
851
859
  ):
852
860
  """Run cluster and add dendrogram
853
861
 
@@ -869,6 +877,10 @@ class ClusterBoard(WhiteBoard):
869
877
  Precomputed linkage matrix.
870
878
  See scipy's :meth:`linkage <scipy.cluster.hierarchy.linkage>` for
871
879
  specific format.
880
+ meta_linkage : ndarray
881
+ Precomputed chunk-level linkage matrix.
882
+ See scipy's :meth:`linkage <scipy.cluster.hierarchy.linkage>` for
883
+ specific format.
872
884
  add_meta : None | bool
873
885
  By default, add_meta is set to False if the linkage is provided, otherwise True.
874
886
  If the data is split, a meta dendrogram can be drawn for data
@@ -905,6 +917,8 @@ class ClusterBoard(WhiteBoard):
905
917
  array as input and return a 1D numpy array of the same length as the number
906
918
  of columns in the input, representing the centroid. The default will use the
907
919
  mean values.
920
+ rasterized : bool
921
+ If True, the dendrogram will be rasterized
908
922
 
909
923
  Examples
910
924
  --------
@@ -926,7 +940,7 @@ class ClusterBoard(WhiteBoard):
926
940
  :context: close-figs
927
941
 
928
942
  >>> h = ma.Heatmap(data)
929
- >>> h.hsplit(cut=[4, 8])
943
+ >>> h.cut_rows(cut=[4, 8])
930
944
  >>> h.add_dendrogram("left", add_base=False)
931
945
  >>> h.render()
932
946
 
@@ -936,7 +950,7 @@ class ClusterBoard(WhiteBoard):
936
950
  :context: close-figs
937
951
 
938
952
  >>> h = ma.Heatmap(data)
939
- >>> h.hsplit(cut=[4, 8])
953
+ >>> h.cut_rows(cut=[4, 8])
940
954
  >>> h.add_dendrogram("left", colors=["#5470c6", "#91cc75", "#fac858"])
941
955
  >>> h.render()
942
956
 
@@ -974,6 +988,7 @@ class ClusterBoard(WhiteBoard):
974
988
  colors=colors,
975
989
  divider_style=divider_style,
976
990
  meta_ratio=meta_ratio,
991
+ rasterized=rasterized,
977
992
  )
978
993
 
979
994
  deform = self.get_deform()
@@ -985,8 +1000,10 @@ class ClusterBoard(WhiteBoard):
985
1000
  method=method,
986
1001
  metric=metric,
987
1002
  linkage=linkage,
1003
+ meta_linkage=meta_linkage,
988
1004
  use_meta=add_meta,
989
1005
  get_meta_center=get_meta_center,
1006
+ rasterized=rasterized,
990
1007
  )
991
1008
  else:
992
1009
  den_options["pos"] = "col"
@@ -996,8 +1013,10 @@ class ClusterBoard(WhiteBoard):
996
1013
  method=method,
997
1014
  metric=metric,
998
1015
  linkage=linkage,
1016
+ meta_linkage=meta_linkage,
999
1017
  use_meta=add_meta,
1000
1018
  get_meta_center=get_meta_center,
1019
+ rasterized=rasterized,
1001
1020
  )
1002
1021
 
1003
1022
  def hsplit(self, cut=None, labels=None, order=None, spacing=0.01):
@@ -1160,7 +1179,7 @@ class ClusterBoard(WhiteBoard):
1160
1179
  >>> h = ma.Heatmap(data)
1161
1180
  >>> labels = ["A", "B", "C", "A", "B", "C", "A", "B", "C", "A"]
1162
1181
  >>> h.group_rows(labels, order=["A", "B", "C"])
1163
- >>> h.add_left(ma.plotter.Labels(labels), pad=.1)
1182
+ >>> h.add_left(ma.plotter.Labels(labels), pad=0.1)
1164
1183
  >>> h.render()
1165
1184
 
1166
1185
  """
@@ -1202,7 +1221,7 @@ class ClusterBoard(WhiteBoard):
1202
1221
  >>> h = ma.Heatmap(data)
1203
1222
  >>> labels = ["A", "B", "C", "A", "B", "C", "A", "B", "C", "A"]
1204
1223
  >>> h.group_cols(labels, order=["A", "B", "C"])
1205
- >>> h.add_top(ma.plotter.Labels(labels), pad=.1)
1224
+ >>> h.add_top(ma.plotter.Labels(labels), pad=0.1)
1206
1225
  >>> h.render()
1207
1226
 
1208
1227
  """
@@ -1334,7 +1353,11 @@ class ClusterBoard(WhiteBoard):
1334
1353
  if (color is not None) & (not is_color_like(color)):
1335
1354
  color = color[0]
1336
1355
  den_obj.draw(
1337
- ax, orient=den["side"], color=color, linewidth=den["linewidth"]
1356
+ ax,
1357
+ orient=den["side"],
1358
+ color=color,
1359
+ linewidth=den["linewidth"],
1360
+ rasterized=den["rasterized"],
1338
1361
  )
1339
1362
  else:
1340
1363
  den_obj.draw(
@@ -1349,6 +1372,7 @@ class ClusterBoard(WhiteBoard):
1349
1372
  divide=den["add_divider"],
1350
1373
  divide_style=den["divider_style"],
1351
1374
  meta_ratio=den["meta_ratio"],
1375
+ rasterized=den["rasterized"],
1352
1376
  )
1353
1377
 
1354
1378
  def _render_plan(self):
marsilea/dendrogram.py CHANGED
@@ -18,6 +18,7 @@ class _DendrogramBase:
18
18
  linkage=None,
19
19
  get_meta_center=None,
20
20
  key=None,
21
+ **kwargs,
21
22
  ):
22
23
  self.key = key
23
24
  self.data = data
@@ -137,7 +138,9 @@ class _DendrogramBase:
137
138
  y1 = yc[1]
138
139
  return x1, y1
139
140
 
140
- def _draw_dendrogram(self, ax, orient="top", color=".1", linewidth=0.7):
141
+ def _draw_dendrogram(
142
+ self, ax, orient="top", color=".1", linewidth=0.7, rasterized=False
143
+ ):
141
144
  x_coords = self._render_x_coords
142
145
  y_coords = self._render_y_coords
143
146
  if orient in ["right", "left"]:
@@ -147,6 +150,7 @@ class _DendrogramBase:
147
150
  [list(zip(x, y)) for x, y in zip(x_coords, y_coords)],
148
151
  color=color,
149
152
  linewidth=linewidth,
153
+ rasterized=rasterized,
150
154
  )
151
155
  ax.add_collection(lines)
152
156
 
@@ -173,6 +177,7 @@ class Dendrogram(_DendrogramBase):
173
177
  linkage=None,
174
178
  get_meta_center=None,
175
179
  key=None,
180
+ **kwargs,
176
181
  ):
177
182
  super().__init__(
178
183
  data,
@@ -181,6 +186,7 @@ class Dendrogram(_DendrogramBase):
181
186
  key=key,
182
187
  linkage=linkage,
183
188
  get_meta_center=get_meta_center,
189
+ **kwargs,
184
190
  )
185
191
 
186
192
  # here we left an empty **kwargs to align api with GroupDendrogram
@@ -193,6 +199,7 @@ class Dendrogram(_DendrogramBase):
193
199
  add_root=False,
194
200
  root_color=None,
195
201
  control_ax=True,
202
+ rasterized=False,
196
203
  **kwargs,
197
204
  ):
198
205
  """
@@ -210,6 +217,8 @@ class Dendrogram(_DendrogramBase):
210
217
  The color of the root line
211
218
  control_ax : bool
212
219
  Adjust the axes to ensure the dendrogram will display correctly
220
+ rasterized : bool
221
+ Rasterize the dendrogram to speed up rendering
213
222
 
214
223
  Returns
215
224
  -------
@@ -219,7 +228,9 @@ class Dendrogram(_DendrogramBase):
219
228
  root_color = color if root_color is None else root_color
220
229
  linewidth = 0.7 if linewidth is None else linewidth
221
230
 
222
- self._draw_dendrogram(ax, orient=orient, color=color, linewidth=linewidth)
231
+ self._draw_dendrogram(
232
+ ax, orient=orient, color=color, linewidth=linewidth, rasterized=rasterized
233
+ )
223
234
 
224
235
  xlim = self._render_xlim
225
236
  ylim = self._render_ylim
@@ -244,7 +255,11 @@ class Dendrogram(_DendrogramBase):
244
255
  x2 = x1
245
256
  y2 = ylim[1]
246
257
  root_line = Line2D(
247
- [x1, x2], [y1, y2], color=root_color, linewidth=linewidth
258
+ [x1, x2],
259
+ [y1, y2],
260
+ color=root_color,
261
+ linewidth=linewidth,
262
+ rasterized=rasterized,
248
263
  )
249
264
  ax.add_artist(root_line)
250
265
 
@@ -267,13 +282,20 @@ class GroupDendrogram(_DendrogramBase):
267
282
  dens: List[Dendrogram],
268
283
  method=None,
269
284
  metric=None,
285
+ linkage=None,
270
286
  get_meta_center=None,
271
287
  key=None,
272
288
  **kwargs,
273
289
  ):
274
290
  data = np.vstack([d.center for d in dens])
275
291
  super().__init__(
276
- data, method=method, metric=metric, get_meta_center=get_meta_center, key=key
292
+ data,
293
+ method=method,
294
+ metric=metric,
295
+ linkage=linkage,
296
+ get_meta_center=get_meta_center,
297
+ key=key,
298
+ **kwargs,
277
299
  )
278
300
  self.orig_dens = np.asarray(dens)
279
301
  self.dens = np.asarray(dens)[self.reorder_index]
@@ -305,6 +327,7 @@ class GroupDendrogram(_DendrogramBase):
305
327
  divide=True,
306
328
  divide_style="--",
307
329
  meta_ratio=0.2,
330
+ rasterized=False,
308
331
  ):
309
332
  """
310
333
 
@@ -330,6 +353,8 @@ class GroupDendrogram(_DendrogramBase):
330
353
  The linestyle of the divide line
331
354
  meta_ratio : float
332
355
  The size of meta dendrogram relative to the base dendrogram
356
+ rasterized : bool
357
+ Rasterize the dendrogram to speed up rendering
333
358
 
334
359
  """
335
360
 
@@ -418,7 +443,11 @@ class GroupDendrogram(_DendrogramBase):
418
443
  if add_meta:
419
444
  # Add meta dendrogram
420
445
  self._draw_dendrogram(
421
- ax, orient=orient, color=meta_color, linewidth=linewidth
446
+ ax,
447
+ orient=orient,
448
+ color=meta_color,
449
+ linewidth=linewidth,
450
+ rasterized=rasterized,
422
451
  )
423
452
 
424
453
  if divide & add_base & add_meta:
@@ -432,6 +461,7 @@ class GroupDendrogram(_DendrogramBase):
432
461
  linestyles=divide_style,
433
462
  color=meta_color,
434
463
  linewidth=linewidth,
464
+ rasterized=rasterized,
435
465
  )
436
466
  else:
437
467
  ax.vlines(
@@ -441,6 +471,7 @@ class GroupDendrogram(_DendrogramBase):
441
471
  linestyles=divide_style,
442
472
  color=meta_color,
443
473
  linewidth=linewidth,
474
+ rasterized=rasterized,
444
475
  )
445
476
 
446
477
  if add_base:
@@ -455,6 +486,7 @@ class GroupDendrogram(_DendrogramBase):
455
486
  linewidth=linewidth,
456
487
  root_color=meta_color,
457
488
  control_ax=False,
489
+ rasterized=rasterized,
458
490
  )
459
491
 
460
492
  xlim = render_xlim
marsilea/exceptions.py CHANGED
@@ -7,15 +7,16 @@ class DuplicateName(Exception):
7
7
 
8
8
 
9
9
  class DuplicatePlotter(Exception):
10
-
11
10
  def __init__(self, plotter):
12
11
  self.plotter = plotter
13
12
 
14
13
  def __str__(self):
15
14
  name = self.plotter.__class__.__name__
16
- return (f"You have added `{name}` "
17
- f"to the `{self.plotter.side}`, "
18
- f"please create a new `{name}` if you want to add it again.")
15
+ return (
16
+ f"The `{name}` added"
17
+ f"to the `{self.plotter.side}` has been registered,"
18
+ f"please create a new `{name}` if you want to add the same plotter again."
19
+ )
19
20
 
20
21
 
21
22
  class SplitTwice(Exception):
marsilea/heatmap.py CHANGED
@@ -50,6 +50,7 @@ class Heatmap(ClusterBoard):
50
50
  height=None,
51
51
  cluster_data=None,
52
52
  init_main=True,
53
+ **kwargs,
53
54
  ):
54
55
  if cluster_data is None:
55
56
  if isinstance(data, pd.DataFrame):
@@ -75,6 +76,7 @@ class Heatmap(ClusterBoard):
75
76
  annot_kws=annot_kws,
76
77
  label=label,
77
78
  cbar_kws=cbar_kws,
79
+ **kwargs,
78
80
  )
79
81
  name = get_plot_name(name, "main", mesh.__class__.__name__)
80
82
  mesh.set(name=name)
@@ -23,6 +23,8 @@ __all__ = [
23
23
  "Chunk",
24
24
  "FixedChunk",
25
25
  "Area",
26
+ "Image",
27
+ "Emoji",
26
28
  ]
27
29
 
28
30
  from ._seaborn import Bar, Box, Boxen, Violin, Point, Strip, Swarm
@@ -33,5 +35,5 @@ from .bio import SeqLogo
33
35
  from .mesh import Colors, ColorMesh, SizedMesh, MarkerMesh, TextMesh
34
36
  from .text import Labels, AnnoLabels, Title, Chunk, FixedChunk
35
37
 
36
- # from ._images import Emoji
38
+ from .images import Emoji, Image
37
39
  from .area import Area
@@ -96,7 +96,7 @@ class _SeabornBase(StatsBase):
96
96
  df["hue"] = hue
97
97
  dfs.append(df)
98
98
 
99
- pdata = pd.concat(dfs)
99
+ pdata = pd.concat(dfs).reset_index(drop=True)
100
100
  self.kws["hue"] = "hue"
101
101
  self.kws["hue_order"] = self.hue
102
102
  if self.get_orient() == "h":
@@ -123,7 +123,8 @@ class _SeabornBase(StatsBase):
123
123
  ax.invert_xaxis()
124
124
  # barplot(data=data, orient=orient, ax=ax, **self.kws)
125
125
  plotter = getattr(seaborn, self._seaborn_plot)
126
-
126
+ print(pdata)
127
+ print(options)
127
128
  plotter(data=pdata, orient=orient, ax=ax, **options)
128
129
  ax.set(xlabel=None, ylabel=None)
129
130
  leg = ax.get_legend()
@@ -195,7 +196,7 @@ def _seaborn_doc(obj: _SeabornBase):
195
196
  >>> sdata = {sdata}
196
197
  >>> plot = {cls_name}(sdata, {kws})
197
198
  >>> h = ma.Heatmap(data)
198
- >>> h.hsplit(cut=[3, 7])
199
+ >>> h.cut_rows(cut=[3, 7])
199
200
  >>> h.add_right(plot)
200
201
  >>> h.render()
201
202
  """
@@ -208,7 +209,7 @@ def _seaborn_doc(obj: _SeabornBase):
208
209
 
209
210
  >>> plot = {cls_name}({hue_data}, {kws})
210
211
  >>> h = ma.Heatmap(data)
211
- >>> h.hsplit(cut=[3, 7])
212
+ >>> h.cut_rows(cut=[3, 7])
212
213
  >>> h.add_right(plot)
213
214
  >>> h.render()
214
215
 
@@ -222,7 +223,7 @@ def _seaborn_doc(obj: _SeabornBase):
222
223
  >>> anno = ma.plotter.Chunk(['C1', 'C2', 'C3'], colors, padding=10)
223
224
  >>> cb = ma.ClusterBoard(data, height=2, margin=.5)
224
225
  >>> cb.add_layer(plot)
225
- >>> cb.vsplit(cut=[3, 7])
226
+ >>> cb.cut_cols([3, 7])
226
227
  >>> cb.add_bottom(anno)
227
228
  >>> cb.render()
228
229
 
@@ -233,9 +234,10 @@ def _seaborn_doc(obj: _SeabornBase):
233
234
 
234
235
  >>> plot = {cls_name}(sdata, orient='h',
235
236
  ... {h_kws})
237
+ >>> anno = ma.plotter.Chunk(['C1', 'C2', 'C3'], colors, padding=10)
236
238
  >>> cb = ma.ClusterBoard(data.T, width=2)
237
239
  >>> cb.add_layer(plot)
238
- >>> cb.hsplit(cut=[3, 7])
240
+ >>> cb.cut_rows([3, 7])
239
241
  >>> cb.add_left(anno)
240
242
  >>> cb.render()
241
243
 
marsilea/plotter/arc.py CHANGED
@@ -141,8 +141,7 @@ class Arc(StatsBase):
141
141
  >>> colors = ["C0", "C1", "C2", "C3", "C4", "C5", "C6"]
142
142
  >>> labels = ["A", "B", "C", "D", "E", "F", "G"]
143
143
  >>> h = ma.Heatmap(np.random.rand(10, 10))
144
- >>> h.add_top(Arc(anchors, links, weights=weights,
145
- ... colors=colors, labels=labels))
144
+ >>> h.add_top(Arc(anchors, links, weights=weights, colors=colors, labels=labels))
146
145
  >>> h.render()
147
146
 
148
147
 
marsilea/plotter/bar.py CHANGED
@@ -276,13 +276,13 @@ class CenterBar(_BarBase):
276
276
  class StackBar(_BarBase):
277
277
  """Stacked Bar
278
278
 
279
- Parameters
280
- ----------
281
- data : np.ndarray, pd.DataFrame
279
+ Parameters
280
+ ----------
281
+ data : np.ndarray, pd.DataFrame
282
282
  2D data, index of dataframe is used as the name of items.
283
- items : list of str
283
+ items : list of str
284
284
  The name of items.
285
- colors : list of colors, mapping of (item, color)
285
+ colors : list of colors, mapping of (item, color)
286
286
  The colors of the bar for each item.
287
287
  orient : {"v", "h"}
288
288
  The orientation of the plot
@@ -309,8 +309,9 @@ class StackBar(_BarBase):
309
309
  :context: close-figs
310
310
 
311
311
  >>> from marsilea.plotter import StackBar
312
- >>> stack_data = pd.DataFrame(data=np.random.randint(1, 10, (5, 10)),
313
- ... index=list("abcde"))
312
+ >>> stack_data = pd.DataFrame(
313
+ ... data=np.random.randint(1, 10, (5, 10)), index=list("abcde")
314
+ ... )
314
315
  >>> _, ax = plt.subplots()
315
316
  >>> StackBar(stack_data).render(ax)
316
317
 
marsilea/plotter/base.py CHANGED
@@ -14,6 +14,13 @@ from .._deform import Deformation
14
14
  from ..exceptions import DataError, SplitConflict
15
15
 
16
16
 
17
+ # class DataValidator:
18
+ #
19
+ # @singledispatch(np.ndarray)
20
+ # def parse(self, data):
21
+ # pass
22
+
23
+
17
24
  class DataLoader:
18
25
  """Handle user data"""
19
26
 
@@ -397,7 +404,8 @@ class RenderPlan:
397
404
  return self._get_intact_render_spec(axes)
398
405
  except Exception as _:
399
406
  raise DataError(
400
- f"Please check your data input with {self.__class__.__name__}"
407
+ f"Please check your data input "
408
+ f"with {self.__class__.__name__} at '{self.side}'"
401
409
  )
402
410
 
403
411
  # def get_render_data(self):
@@ -508,7 +516,9 @@ class RenderPlan:
508
516
  if self._plan_label is not None:
509
517
  self._plan_label.add(axes, self.side)
510
518
 
511
- def get_canvas_size(self, figure) -> float:
519
+ def get_canvas_size(
520
+ self, figure, main_height=None, main_width=None, **kwargs
521
+ ) -> float:
512
522
  """
513
523
  If the size is unknown before rendering, this function must be
514
524
  implemented to return the canvas size in inches.
marsilea/plotter/bio.py CHANGED
@@ -102,8 +102,9 @@ class SeqLogo(StatsBase):
102
102
 
103
103
  >>> import pandas as pd
104
104
  >>> from marsilea.plotter import SeqLogo
105
- >>> matrix = pd.DataFrame(data=np.random.randint(1, 10, (4, 10)),
106
- ... index=list("ACGT"))
105
+ >>> matrix = pd.DataFrame(
106
+ ... data=np.random.randint(1, 10, (4, 10)), index=list("ACGT")
107
+ ... )
107
108
  >>> _, ax = plt.subplots()
108
109
  >>> colors = {"A": "r", "C": "b", "G": "g", "T": "black"}
109
110
  >>> SeqLogo(matrix, color_encode=colors).render(ax)