marsilea 0.3.1__py3-none-any.whl → 0.3.3__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 +3 -2
- marsilea/_api.py +3 -2
- marsilea/_deform.py +44 -38
- marsilea/base.py +702 -118
- marsilea/dataset.py +49 -23
- marsilea/dendrogram.py +119 -84
- marsilea/exceptions.py +5 -6
- marsilea/heatmap.py +85 -25
- marsilea/layers.py +77 -69
- marsilea/layout.py +132 -102
- marsilea/plotter/__init__.py +29 -0
- marsilea/plotter/_images.py +37 -7
- marsilea/plotter/_seaborn.py +35 -24
- marsilea/plotter/_utils.py +3 -2
- marsilea/plotter/arc.py +29 -19
- marsilea/plotter/area.py +80 -0
- marsilea/plotter/bar.py +119 -92
- marsilea/plotter/base.py +76 -49
- marsilea/plotter/bio.py +40 -30
- marsilea/plotter/mesh.py +184 -88
- marsilea/plotter/text.py +303 -194
- marsilea/upset.py +229 -151
- marsilea/utils.py +23 -10
- {marsilea-0.3.1.dist-info → marsilea-0.3.3.dist-info}/LICENSE +1 -1
- marsilea-0.3.3.dist-info/METADATA +74 -0
- marsilea-0.3.3.dist-info/RECORD +27 -0
- marsilea-0.3.1.dist-info/METADATA +0 -50
- marsilea-0.3.1.dist-info/RECORD +0 -26
- {marsilea-0.3.1.dist-info → marsilea-0.3.3.dist-info}/WHEEL +0 -0
marsilea/dataset.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
2
3
|
from platformdirs import user_cache_path
|
|
3
4
|
from urllib.request import urlretrieve
|
|
4
5
|
|
|
@@ -16,7 +17,8 @@ def load_data(name, cache=True):
|
|
|
16
17
|
(TCGA, PanCancer Atlas)
|
|
17
18
|
- 'mouse_embryo': Spatial mapping of mouse embryo at E12.5
|
|
18
19
|
- 'seq_align': Sequence alignment data
|
|
19
|
-
- 'les_miserables': Characters in Les
|
|
20
|
+
- 'les_miserables': Characters in Les Misérables
|
|
21
|
+
- 'sc_multiomics': Single-cell multi-omics dataset from COVID-19 patients
|
|
20
22
|
|
|
21
23
|
Parameters
|
|
22
24
|
----------
|
|
@@ -33,6 +35,8 @@ def load_data(name, cache=True):
|
|
|
33
35
|
return _load_imdb(cache)
|
|
34
36
|
elif name == "pbmc3k":
|
|
35
37
|
return _load_pbmc3k(cache)
|
|
38
|
+
elif name == "sc_multiomics":
|
|
39
|
+
return _load_sc_multiomics(cache)
|
|
36
40
|
elif name == "oncoprint":
|
|
37
41
|
return _load_oncoprint(cache)
|
|
38
42
|
elif name == "mouse_embryo":
|
|
@@ -61,7 +65,11 @@ def _cache_remote(files, cache=True):
|
|
|
61
65
|
dest = data_dir / dfname
|
|
62
66
|
# Not download when cache and exist
|
|
63
67
|
if not (cache and dest.exists()):
|
|
64
|
-
|
|
68
|
+
try:
|
|
69
|
+
urlretrieve(url, dest)
|
|
70
|
+
except Exception as e:
|
|
71
|
+
print(url)
|
|
72
|
+
raise e
|
|
65
73
|
|
|
66
74
|
if len(files) == 1:
|
|
67
75
|
return data_dir / download_files[0]
|
|
@@ -75,28 +83,50 @@ def _load_imdb(cache=True):
|
|
|
75
83
|
|
|
76
84
|
|
|
77
85
|
def _load_pbmc3k(cache=True):
|
|
78
|
-
exp, pct_cells, count = _cache_remote(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
cache=cache)
|
|
86
|
+
exp, pct_cells, count = _cache_remote(
|
|
87
|
+
["pbmc3k/exp.csv", "pbmc3k/pct_cells.csv", "pbmc3k/count.csv"], cache=cache
|
|
88
|
+
)
|
|
82
89
|
return {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
90
|
+
"exp": pd.read_csv(exp, index_col=0),
|
|
91
|
+
"pct_cells": pd.read_csv(pct_cells, index_col=0),
|
|
92
|
+
"count": pd.read_csv(count, index_col=0),
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
|
|
96
|
+
def _load_sc_multiomics(cache=True):
|
|
97
|
+
stack, interaction = _cache_remote(
|
|
98
|
+
[
|
|
99
|
+
"sc-multiomics/sc-multiomics.npz",
|
|
100
|
+
"sc-multiomics/sc-multiomics-interaction.csv",
|
|
101
|
+
]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
dataset = np.load(stack, allow_pickle=True)
|
|
105
|
+
interaction = pd.read_csv(interaction)
|
|
106
|
+
|
|
107
|
+
data = {}
|
|
108
|
+
for key in dataset.files:
|
|
109
|
+
data[key] = dataset[key]
|
|
110
|
+
data["interaction"] = interaction
|
|
111
|
+
|
|
112
|
+
return data
|
|
113
|
+
|
|
114
|
+
|
|
89
115
|
def _load_oncoprint(cache=True):
|
|
90
116
|
cna, mrna, methyl, clinical = _cache_remote(
|
|
91
|
-
[
|
|
92
|
-
|
|
93
|
-
|
|
117
|
+
[
|
|
118
|
+
"oncoprint/cna.csv",
|
|
119
|
+
"oncoprint/mrna_exp.csv",
|
|
120
|
+
"oncoprint/methyl_exp.csv",
|
|
121
|
+
"oncoprint/clinical.csv",
|
|
122
|
+
],
|
|
123
|
+
cache=cache,
|
|
94
124
|
)
|
|
95
125
|
return {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
126
|
+
"cna": pd.read_csv(cna, index_col=0),
|
|
127
|
+
"mrna_exp": pd.read_csv(mrna, index_col=0),
|
|
128
|
+
"methyl_exp": pd.read_csv(methyl, index_col=0),
|
|
129
|
+
"clinical": pd.read_csv(clinical, index_col=0),
|
|
100
130
|
}
|
|
101
131
|
|
|
102
132
|
|
|
@@ -117,11 +147,7 @@ def _load_cooking_oils(cache=True):
|
|
|
117
147
|
|
|
118
148
|
def _load_les_miserables(cache=True):
|
|
119
149
|
nodes, links = _cache_remote(
|
|
120
|
-
[
|
|
121
|
-
|
|
122
|
-
], cache=cache
|
|
150
|
+
["les-miserables/miserables_nodes.csv", "les-miserables/miserables_links.csv"],
|
|
151
|
+
cache=cache,
|
|
123
152
|
)
|
|
124
|
-
return {
|
|
125
|
-
'nodes': pd.read_csv(nodes),
|
|
126
|
-
'links': pd.read_csv(links)
|
|
127
|
-
}
|
|
153
|
+
return {"nodes": pd.read_csv(nodes), "links": pd.read_csv(links)}
|
marsilea/dendrogram.py
CHANGED
|
@@ -8,17 +8,17 @@ from typing import List, Sequence
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class _DendrogramBase:
|
|
11
|
-
|
|
12
11
|
is_singleton = False
|
|
13
12
|
|
|
14
|
-
def __init__(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
data,
|
|
16
|
+
method=None,
|
|
17
|
+
metric=None,
|
|
18
|
+
linkage=None,
|
|
19
|
+
get_meta_center=None,
|
|
20
|
+
key=None,
|
|
21
|
+
):
|
|
22
22
|
self.key = key
|
|
23
23
|
self.data = data
|
|
24
24
|
if method is None:
|
|
@@ -29,8 +29,8 @@ class _DendrogramBase:
|
|
|
29
29
|
if len(data) == 1:
|
|
30
30
|
# TODO: the y coords are heuristic value,
|
|
31
31
|
# need a better way to handle
|
|
32
|
-
self.x_coords = np.array([[1
|
|
33
|
-
self.y_coords = np.array([[0
|
|
32
|
+
self.x_coords = np.array([[1.0, 1.0, 1.0, 1.0]])
|
|
33
|
+
self.y_coords = np.array([[0.0, 0.75, 0.75, 0.0]])
|
|
34
34
|
self._reorder_index = np.array([0])
|
|
35
35
|
self.is_singleton = True
|
|
36
36
|
else:
|
|
@@ -40,21 +40,21 @@ class _DendrogramBase:
|
|
|
40
40
|
self.Z = scipy_linkage(data, method=method, metric=metric)
|
|
41
41
|
self._plot_data = dendrogram(self.Z, no_plot=True)
|
|
42
42
|
|
|
43
|
-
self.x_coords = np.asarray(self._plot_data[
|
|
44
|
-
self.y_coords = np.asarray(self._plot_data[
|
|
45
|
-
self._reorder_index = self._plot_data[
|
|
43
|
+
self.x_coords = np.asarray(self._plot_data["icoord"]) / 5
|
|
44
|
+
self.y_coords = np.asarray(self._plot_data["dcoord"])
|
|
45
|
+
self._reorder_index = self._plot_data["leaves"]
|
|
46
46
|
|
|
47
47
|
if len(self.y_coords) == 1:
|
|
48
|
-
self.y_coords = np.array([[0
|
|
48
|
+
self.y_coords = np.array([[0.0, 0.75, 0.75, 0.0]])
|
|
49
49
|
else:
|
|
50
50
|
ycoords = np.unique(self.y_coords)
|
|
51
51
|
ycoords = ycoords[np.nonzero(ycoords)]
|
|
52
52
|
y_min, y_max = np.min(ycoords), np.max(ycoords)
|
|
53
53
|
interval = y_max - y_min
|
|
54
54
|
for i, j in zip(*np.nonzero(self.y_coords)):
|
|
55
|
-
if self.y_coords[i, j] != 0
|
|
55
|
+
if self.y_coords[i, j] != 0.0:
|
|
56
56
|
v = self.y_coords[i, j]
|
|
57
|
-
self.y_coords[i, j] = (v - y_min) / interval + .2
|
|
57
|
+
self.y_coords[i, j] = (v - y_min) / interval + 0.2
|
|
58
58
|
# self.y_coords[i, j] -= offset
|
|
59
59
|
# self.y_coords[np.nonzero(self.y_coords)] - offset
|
|
60
60
|
|
|
@@ -80,7 +80,8 @@ class _DendrogramBase:
|
|
|
80
80
|
else:
|
|
81
81
|
raise ValueError(
|
|
82
82
|
"The get_meta_center must return a numpy array with shape "
|
|
83
|
-
"matching the number of features in the data."
|
|
83
|
+
"matching the number of features in the data."
|
|
84
|
+
)
|
|
84
85
|
else:
|
|
85
86
|
raise TypeError("The get_meta_center must be a callable function or None.")
|
|
86
87
|
|
|
@@ -107,8 +108,9 @@ class _DendrogramBase:
|
|
|
107
108
|
|
|
108
109
|
if y_end is not None:
|
|
109
110
|
if y_end < self.ylim[1]:
|
|
110
|
-
raise ValueError(
|
|
111
|
-
|
|
111
|
+
raise ValueError(
|
|
112
|
+
f"{y_end} is lower than " f"current ylim at {self.ylim[1]}"
|
|
113
|
+
)
|
|
112
114
|
self._render_ylim = (0, y_end)
|
|
113
115
|
|
|
114
116
|
@property
|
|
@@ -123,7 +125,7 @@ class _DendrogramBase:
|
|
|
123
125
|
def root(self):
|
|
124
126
|
xc = self.x_coords[-1]
|
|
125
127
|
yc = self.y_coords[-1]
|
|
126
|
-
x1 = (xc[2] - xc[1]) / 2. + xc[1]
|
|
128
|
+
x1 = (xc[2] - xc[1]) / 2.0 + xc[1]
|
|
127
129
|
y1 = yc[1]
|
|
128
130
|
return x1, y1
|
|
129
131
|
|
|
@@ -131,11 +133,11 @@ class _DendrogramBase:
|
|
|
131
133
|
def render_root(self):
|
|
132
134
|
xc = self._render_x_coords[-1]
|
|
133
135
|
yc = self._render_y_coords[-1]
|
|
134
|
-
x1 = (xc[2] - xc[1]) / 2. + xc[1]
|
|
136
|
+
x1 = (xc[2] - xc[1]) / 2.0 + xc[1]
|
|
135
137
|
y1 = yc[1]
|
|
136
138
|
return x1, y1
|
|
137
139
|
|
|
138
|
-
def _draw_dendrogram(self, ax, orient="top", color=".1", linewidth
|
|
140
|
+
def _draw_dendrogram(self, ax, orient="top", color=".1", linewidth=0.7):
|
|
139
141
|
x_coords = self._render_x_coords
|
|
140
142
|
y_coords = self._render_y_coords
|
|
141
143
|
if orient in ["right", "left"]:
|
|
@@ -143,14 +145,15 @@ class _DendrogramBase:
|
|
|
143
145
|
|
|
144
146
|
lines = LineCollection(
|
|
145
147
|
[list(zip(x, y)) for x, y in zip(x_coords, y_coords)],
|
|
146
|
-
color=color,
|
|
148
|
+
color=color,
|
|
149
|
+
linewidth=linewidth,
|
|
147
150
|
)
|
|
148
151
|
ax.add_collection(lines)
|
|
149
152
|
|
|
150
153
|
|
|
151
154
|
class Dendrogram(_DendrogramBase):
|
|
152
155
|
"""A dendrogram class
|
|
153
|
-
|
|
156
|
+
|
|
154
157
|
Parameters
|
|
155
158
|
----------
|
|
156
159
|
|
|
@@ -159,26 +162,39 @@ class Dendrogram(_DendrogramBase):
|
|
|
159
162
|
Refer to :func:`scipy.cluster.hierarchy.linkage`
|
|
160
163
|
metric : str
|
|
161
164
|
Refer to :func:`scipy.cluster.hierarchy.linkage`
|
|
162
|
-
|
|
165
|
+
|
|
163
166
|
"""
|
|
164
167
|
|
|
165
|
-
def __init__(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
168
|
+
def __init__(
|
|
169
|
+
self,
|
|
170
|
+
data: np.ndarray,
|
|
171
|
+
method=None,
|
|
172
|
+
metric=None,
|
|
173
|
+
linkage=None,
|
|
174
|
+
get_meta_center=None,
|
|
175
|
+
key=None,
|
|
176
|
+
):
|
|
177
|
+
super().__init__(
|
|
178
|
+
data,
|
|
179
|
+
method=method,
|
|
180
|
+
metric=metric,
|
|
181
|
+
key=key,
|
|
182
|
+
linkage=linkage,
|
|
183
|
+
get_meta_center=get_meta_center,
|
|
184
|
+
)
|
|
175
185
|
|
|
176
186
|
# here we left an empty **kwargs to align api with GroupDendrogram
|
|
177
|
-
def draw(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
187
|
+
def draw(
|
|
188
|
+
self,
|
|
189
|
+
ax,
|
|
190
|
+
orient="top",
|
|
191
|
+
color=None,
|
|
192
|
+
linewidth=None,
|
|
193
|
+
add_root=False,
|
|
194
|
+
root_color=None,
|
|
195
|
+
control_ax=True,
|
|
196
|
+
**kwargs,
|
|
197
|
+
):
|
|
182
198
|
"""
|
|
183
199
|
|
|
184
200
|
Parameters
|
|
@@ -201,10 +217,9 @@ class Dendrogram(_DendrogramBase):
|
|
|
201
217
|
"""
|
|
202
218
|
color = ".1" if color is None else color
|
|
203
219
|
root_color = color if root_color is None else root_color
|
|
204
|
-
linewidth = .7 if linewidth is None else linewidth
|
|
220
|
+
linewidth = 0.7 if linewidth is None else linewidth
|
|
205
221
|
|
|
206
|
-
self._draw_dendrogram(ax, orient=orient, color=color,
|
|
207
|
-
linewidth=linewidth)
|
|
222
|
+
self._draw_dendrogram(ax, orient=orient, color=color, linewidth=linewidth)
|
|
208
223
|
|
|
209
224
|
xlim = self._render_xlim
|
|
210
225
|
ylim = self._render_ylim
|
|
@@ -228,8 +243,9 @@ class Dendrogram(_DendrogramBase):
|
|
|
228
243
|
else:
|
|
229
244
|
x2 = x1
|
|
230
245
|
y2 = ylim[1]
|
|
231
|
-
root_line = Line2D(
|
|
232
|
-
|
|
246
|
+
root_line = Line2D(
|
|
247
|
+
[x1, x2], [y1, y2], color=root_color, linewidth=linewidth
|
|
248
|
+
)
|
|
233
249
|
ax.add_artist(root_line)
|
|
234
250
|
|
|
235
251
|
|
|
@@ -243,20 +259,22 @@ class GroupDendrogram(_DendrogramBase):
|
|
|
243
259
|
A list of :class:`Dendrogram`
|
|
244
260
|
method : str
|
|
245
261
|
metric : str
|
|
246
|
-
|
|
262
|
+
|
|
247
263
|
"""
|
|
248
264
|
|
|
249
|
-
def __init__(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
265
|
+
def __init__(
|
|
266
|
+
self,
|
|
267
|
+
dens: List[Dendrogram],
|
|
268
|
+
method=None,
|
|
269
|
+
metric=None,
|
|
270
|
+
get_meta_center=None,
|
|
271
|
+
key=None,
|
|
272
|
+
**kwargs,
|
|
273
|
+
):
|
|
257
274
|
data = np.vstack([d.center for d in dens])
|
|
258
|
-
super().__init__(
|
|
259
|
-
|
|
275
|
+
super().__init__(
|
|
276
|
+
data, method=method, metric=metric, get_meta_center=get_meta_center, key=key
|
|
277
|
+
)
|
|
260
278
|
self.orig_dens = np.asarray(dens)
|
|
261
279
|
self.dens = np.asarray(dens)[self.reorder_index]
|
|
262
280
|
self.n = len(self.dens)
|
|
@@ -274,19 +292,20 @@ class GroupDendrogram(_DendrogramBase):
|
|
|
274
292
|
self.den_ylim = ylim
|
|
275
293
|
self.divider = ylim * 1.05
|
|
276
294
|
|
|
277
|
-
def draw(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
295
|
+
def draw(
|
|
296
|
+
self,
|
|
297
|
+
ax,
|
|
298
|
+
orient="top",
|
|
299
|
+
spacing=None,
|
|
300
|
+
add_meta=True,
|
|
301
|
+
add_base=True,
|
|
302
|
+
base_colors=None,
|
|
303
|
+
meta_color=None,
|
|
304
|
+
linewidth=None,
|
|
305
|
+
divide=True,
|
|
306
|
+
divide_style="--",
|
|
307
|
+
meta_ratio=0.2,
|
|
308
|
+
):
|
|
290
309
|
"""
|
|
291
310
|
|
|
292
311
|
Parameters
|
|
@@ -315,7 +334,7 @@ class GroupDendrogram(_DendrogramBase):
|
|
|
315
334
|
"""
|
|
316
335
|
|
|
317
336
|
meta_color = ".1" if meta_color is None else meta_color
|
|
318
|
-
linewidth = .7 if linewidth is None else linewidth
|
|
337
|
+
linewidth = 0.7 if linewidth is None else linewidth
|
|
319
338
|
if base_colors is None:
|
|
320
339
|
base_colors = cycle([None])
|
|
321
340
|
elif is_color_like(base_colors):
|
|
@@ -332,8 +351,7 @@ class GroupDendrogram(_DendrogramBase):
|
|
|
332
351
|
|
|
333
352
|
render_xlim = self.den_xlim / (1 - np.sum(spacing))
|
|
334
353
|
skeleton = np.sort(np.unique(self.x_coords[self.y_coords == 0]))
|
|
335
|
-
ranger = [(skeleton[i], skeleton[i + 1])
|
|
336
|
-
for i in range(len(skeleton) - 1)]
|
|
354
|
+
ranger = [(skeleton[i], skeleton[i + 1]) for i in range(len(skeleton) - 1)]
|
|
337
355
|
|
|
338
356
|
draw_dens = self.dens if add_meta else self.orig_dens
|
|
339
357
|
|
|
@@ -399,28 +417,45 @@ class GroupDendrogram(_DendrogramBase):
|
|
|
399
417
|
|
|
400
418
|
if add_meta:
|
|
401
419
|
# Add meta dendrogram
|
|
402
|
-
self._draw_dendrogram(
|
|
403
|
-
|
|
420
|
+
self._draw_dendrogram(
|
|
421
|
+
ax, orient=orient, color=meta_color, linewidth=linewidth
|
|
422
|
+
)
|
|
404
423
|
|
|
405
424
|
if divide & add_base & add_meta:
|
|
406
425
|
xmin = np.min(draw_dens[0].x_coords)
|
|
407
426
|
xmax = np.max(draw_dens[-1]._render_x_coords)
|
|
408
427
|
if orient in ["top", "bottom"]:
|
|
409
|
-
ax.hlines(
|
|
410
|
-
|
|
411
|
-
|
|
428
|
+
ax.hlines(
|
|
429
|
+
self.divider,
|
|
430
|
+
xmin,
|
|
431
|
+
xmax, # 0, xlim,
|
|
432
|
+
linestyles=divide_style,
|
|
433
|
+
color=meta_color,
|
|
434
|
+
linewidth=linewidth,
|
|
435
|
+
)
|
|
412
436
|
else:
|
|
413
|
-
ax.vlines(
|
|
414
|
-
|
|
415
|
-
|
|
437
|
+
ax.vlines(
|
|
438
|
+
self.divider,
|
|
439
|
+
xmin,
|
|
440
|
+
xmax, # 0, ylim,
|
|
441
|
+
linestyles=divide_style,
|
|
442
|
+
color=meta_color,
|
|
443
|
+
linewidth=linewidth,
|
|
444
|
+
)
|
|
416
445
|
|
|
417
446
|
if add_base:
|
|
418
447
|
for den, color in zip(draw_dens, base_colors):
|
|
419
448
|
# The singleton dendrogram will only be drawn if meta is drawn
|
|
420
449
|
if not den.is_singleton or add_meta:
|
|
421
|
-
den.draw(
|
|
422
|
-
|
|
423
|
-
|
|
450
|
+
den.draw(
|
|
451
|
+
ax,
|
|
452
|
+
orient=orient,
|
|
453
|
+
add_root=add_meta,
|
|
454
|
+
color=color,
|
|
455
|
+
linewidth=linewidth,
|
|
456
|
+
root_color=meta_color,
|
|
457
|
+
control_ax=False,
|
|
458
|
+
)
|
|
424
459
|
|
|
425
460
|
xlim = render_xlim
|
|
426
461
|
# reserve room to avoid clipping of the top
|
marsilea/exceptions.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
class DuplicateName(Exception):
|
|
2
|
-
|
|
3
2
|
def __init__(self, name):
|
|
4
3
|
self.name = name
|
|
5
4
|
|
|
@@ -8,7 +7,6 @@ class DuplicateName(Exception):
|
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
class SplitTwice(Exception):
|
|
11
|
-
|
|
12
10
|
def __init__(self, axis="col"):
|
|
13
11
|
self.axis = axis
|
|
14
12
|
|
|
@@ -21,11 +19,12 @@ class SplitConflict(Exception):
|
|
|
21
19
|
|
|
22
20
|
|
|
23
21
|
class AppendLayoutError(Exception):
|
|
24
|
-
|
|
25
22
|
def __str__(self):
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
return (
|
|
24
|
+
"Append a concatenated plot is not allowed,"
|
|
25
|
+
"you can only append "
|
|
26
|
+
"plots to a concatenated plot."
|
|
27
|
+
)
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
class DataError(Exception):
|
marsilea/heatmap.py
CHANGED
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
5
6
|
|
|
6
7
|
from .base import ClusterBoard
|
|
7
8
|
from .plotter import ColorMesh, SizedMesh, Colors
|
|
@@ -27,22 +28,54 @@ class Heatmap(ClusterBoard):
|
|
|
27
28
|
|
|
28
29
|
"""
|
|
29
30
|
|
|
30
|
-
def __init__(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
data: np.ndarray,
|
|
34
|
+
vmin=None,
|
|
35
|
+
vmax=None,
|
|
36
|
+
cmap=None,
|
|
37
|
+
norm=None,
|
|
38
|
+
center=None,
|
|
39
|
+
mask=None,
|
|
40
|
+
alpha=None,
|
|
41
|
+
linewidth=0,
|
|
42
|
+
linecolor="white",
|
|
43
|
+
annot=None,
|
|
44
|
+
fmt=None,
|
|
45
|
+
annot_kws=None,
|
|
46
|
+
label=None,
|
|
47
|
+
cbar_kws=None,
|
|
48
|
+
name=None,
|
|
49
|
+
width=None,
|
|
50
|
+
height=None,
|
|
51
|
+
cluster_data=None,
|
|
52
|
+
init_main=True,
|
|
53
|
+
):
|
|
37
54
|
if cluster_data is None:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
if isinstance(data, pd.DataFrame):
|
|
56
|
+
cluster_data = data.values
|
|
57
|
+
else:
|
|
58
|
+
cluster_data = data
|
|
59
|
+
super().__init__(
|
|
60
|
+
cluster_data, width=width, height=height, name=name, init_main=init_main
|
|
61
|
+
)
|
|
62
|
+
mesh = ColorMesh(
|
|
63
|
+
data,
|
|
64
|
+
vmin=vmin,
|
|
65
|
+
vmax=vmax,
|
|
66
|
+
cmap=cmap,
|
|
67
|
+
norm=norm,
|
|
68
|
+
center=center,
|
|
69
|
+
mask=mask,
|
|
70
|
+
alpha=alpha,
|
|
71
|
+
linewidth=linewidth,
|
|
72
|
+
linecolor=linecolor,
|
|
73
|
+
annot=annot,
|
|
74
|
+
fmt=fmt,
|
|
75
|
+
annot_kws=annot_kws,
|
|
76
|
+
label=label,
|
|
77
|
+
cbar_kws=cbar_kws,
|
|
78
|
+
)
|
|
46
79
|
name = get_plot_name(name, "main", mesh.__class__.__name__)
|
|
47
80
|
mesh.set(name=name)
|
|
48
81
|
self.add_layer(mesh)
|
|
@@ -65,12 +98,29 @@ class CatHeatmap(ClusterBoard):
|
|
|
65
98
|
|
|
66
99
|
"""
|
|
67
100
|
|
|
68
|
-
def __init__(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
101
|
+
def __init__(
|
|
102
|
+
self,
|
|
103
|
+
data,
|
|
104
|
+
palette=None,
|
|
105
|
+
cmap=None,
|
|
106
|
+
mask=None,
|
|
107
|
+
name=None,
|
|
108
|
+
width=None,
|
|
109
|
+
height=None,
|
|
110
|
+
cluster_data=None,
|
|
111
|
+
linewidth=0,
|
|
112
|
+
linecolor="white",
|
|
113
|
+
**kwargs,
|
|
114
|
+
):
|
|
115
|
+
mesh = Colors(
|
|
116
|
+
data,
|
|
117
|
+
palette=palette,
|
|
118
|
+
cmap=cmap,
|
|
119
|
+
mask=mask,
|
|
120
|
+
linewidth=linewidth,
|
|
121
|
+
linecolor=linecolor,
|
|
122
|
+
**kwargs,
|
|
123
|
+
)
|
|
74
124
|
if cluster_data is None:
|
|
75
125
|
cluster_data = mesh.cluster_data
|
|
76
126
|
super().__init__(cluster_data, width=width, height=height, name=name)
|
|
@@ -96,11 +146,21 @@ class SizedHeatmap(ClusterBoard):
|
|
|
96
146
|
|
|
97
147
|
"""
|
|
98
148
|
|
|
99
|
-
def __init__(
|
|
100
|
-
|
|
101
|
-
|
|
149
|
+
def __init__(
|
|
150
|
+
self,
|
|
151
|
+
size,
|
|
152
|
+
color=None,
|
|
153
|
+
cluster_data=None,
|
|
154
|
+
name=None,
|
|
155
|
+
width=None,
|
|
156
|
+
height=None,
|
|
157
|
+
**kwargs,
|
|
158
|
+
):
|
|
102
159
|
if cluster_data is None:
|
|
103
|
-
|
|
160
|
+
if isinstance(size, pd.DataFrame):
|
|
161
|
+
cluster_data = size.values
|
|
162
|
+
else:
|
|
163
|
+
cluster_data = size
|
|
104
164
|
super().__init__(cluster_data, width=width, height=height, name=name)
|
|
105
165
|
|
|
106
166
|
mesh = SizedMesh(size=size, color=color, **kwargs)
|