iplotx 0.6.4__py3-none-any.whl → 0.6.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.
iplotx/edge/__init__.py CHANGED
@@ -131,6 +131,9 @@ class EdgeCollection(mpl.collections.PatchCollection):
131
131
  self._style["split"],
132
132
  )
133
133
 
134
+ zorder = self._style.get("zorder", 2)
135
+ self.set_zorder(zorder)
136
+
134
137
  def _add_subedges(
135
138
  self,
136
139
  nedges,
@@ -357,6 +360,8 @@ class EdgeCollection(mpl.collections.PatchCollection):
357
360
  waypoints = edge_stylei.get("waypoints", "none")
358
361
  if waypoints is False or waypoints is np.False_:
359
362
  waypoints = "none"
363
+ elif isinstance(waypoints, (list, tuple)) and len(waypoints) == 0:
364
+ waypoints = "none"
360
365
  elif waypoints is True or waypoints is np.True_:
361
366
  raise ValueError(
362
367
  "Could not determine automatically type of edge waypoints.",
@@ -101,7 +101,13 @@ class NetworkXDataProvider(NetworkDataProvider):
101
101
  else:
102
102
  if len(edge_labels) != len(edge_df):
103
103
  raise ValueError("Edge labels must be the same length as the number of edges.")
104
- edge_df["label"] = edge_labels
104
+ if isinstance(edge_labels, dict):
105
+ edge_labels = pd.Series(edge_labels)
106
+ edge_df["label"] = edge_labels.loc[
107
+ edge_df.set_index(["_ipx_source", "_ipx_target"]).index
108
+ ].values
109
+ else:
110
+ edge_df["label"] = edge_labels
105
111
 
106
112
  network_data = {
107
113
  "vertex_df": vertex_df,
iplotx/ingest/typing.py CHANGED
@@ -138,11 +138,13 @@ class TreeDataProvider(Protocol):
138
138
 
139
139
  Note: This is a default implemntation that can be overridden by the provider.
140
140
  """
141
- root_attr = self.tree.root
142
- if callable(root_attr):
143
- return root_attr()
144
- else:
145
- return root_attr
141
+ if hasattr(self.tree, "root"):
142
+ root_attr = self.tree.root
143
+ if callable(root_attr):
144
+ return root_attr()
145
+ else:
146
+ return root_attr
147
+ return self.tree.get_root()
146
148
 
147
149
  def get_leaves(self) -> Sequence[Any]:
148
150
  """Get the tree leaves/tips in a provider-specific data structure.
@@ -311,11 +313,12 @@ class TreeDataProvider(Protocol):
311
313
  leaf_name_attrs = ("name",)
312
314
 
313
315
  # Add edge_df
314
- edge_data = {"_ipx_source": [], "_ipx_target": []}
316
+ edge_data = {"_ipx_source": [], "_ipx_target": [], "branch_length": []}
315
317
  for node in self.preorder():
316
318
  for child in self.get_children(node):
317
319
  edge_data["_ipx_source"].append(node)
318
320
  edge_data["_ipx_target"].append(child)
321
+ edge_data["branch_length"].append(self.get_branch_length(child))
319
322
  edge_df = pd.DataFrame(edge_data)
320
323
  tree_data["edge_df"] = edge_df
321
324
 
iplotx/network.py CHANGED
@@ -140,7 +140,10 @@ class NetworkArtist(mpl.artist.Artist):
140
140
  return self
141
141
 
142
142
  def get_children(self):
143
- return (self._vertices, self._edges)
143
+ if hasattr(self, "_edges"):
144
+ return (self._vertices, self._edges)
145
+ else:
146
+ return (self._vertices,)
144
147
 
145
148
  def set_figure(self, fig):
146
149
  super().set_figure(fig)
@@ -184,12 +187,14 @@ class NetworkArtist(mpl.artist.Artist):
184
187
  if len(layout) == 0:
185
188
  return mpl.transforms.Bbox([[0, 0], [1, 1]])
186
189
 
187
- bbox = mpl.transforms.Bbox.union(
188
- [
189
- self._vertices.get_datalim(transData),
190
+ bboxes = [
191
+ self._vertices.get_datalim(transData),
192
+ ]
193
+ if hasattr(self, "_edges"):
194
+ bboxes.append(
190
195
  self._edges.get_datalim(transData),
191
- ]
192
- )
196
+ )
197
+ bbox = mpl.transforms.Bbox.union(bboxes)
193
198
 
194
199
  bbox = bbox.expanded(sw=(1.0 + pad), sh=(1.0 + pad))
195
200
  return bbox
@@ -239,17 +244,21 @@ class NetworkArtist(mpl.artist.Artist):
239
244
  labels = self._get_label_series("edge")
240
245
  edge_style = get_style(".edge")
241
246
 
247
+ edge_df = self._ipx_internal_data["edge_df"].set_index(["_ipx_source", "_ipx_target"])
248
+
249
+ if len(edge_df) == 0:
250
+ return
251
+
242
252
  if "cmap" in edge_style:
243
253
  cmap_fun = _build_cmap_fun(
244
- edge_style["color"],
245
- edge_style["cmap"],
254
+ edge_style,
255
+ "color",
246
256
  edge_style.get("norm", None),
257
+ internal=edge_df,
247
258
  )
248
259
  else:
249
260
  cmap_fun = None
250
261
 
251
- edge_df = self._ipx_internal_data["edge_df"].set_index(["_ipx_source", "_ipx_target"])
252
-
253
262
  if "cmap" in edge_style:
254
263
  colorarray = []
255
264
  edgepatches = []
@@ -312,8 +321,7 @@ class NetworkArtist(mpl.artist.Artist):
312
321
  if not self.get_visible():
313
322
  return
314
323
 
315
- # NOTE: looks like we have to manage the zorder ourselves
316
- # this is kind of funny actually
324
+ # Handle zorder manually, just like in AxesBase in mpl
317
325
  children = list(self.get_children())
318
326
  children.sort(key=lambda x: x.zorder)
319
327
  for art in children:
iplotx/plotting.py CHANGED
@@ -28,6 +28,7 @@ def network(
28
28
  title: Optional[str] = None,
29
29
  aspect: Optional[str | float] = None,
30
30
  margins: float | tuple[float, float] = 0,
31
+ strip_axes: bool = True,
31
32
  **kwargs,
32
33
  ) -> list[mpl.artist.Artist]:
33
34
  """Plot this network and/or vertex grouping using the specified layout.
@@ -53,6 +54,7 @@ def network(
53
54
  used as a quick fix when some vertex shapes reach beyond the plot edge. This is
54
55
  a fraction of the data limits, so 0.1 means 10% of the data limits will be left
55
56
  as margin.
57
+ strip_axes: If True, remove axis spines and ticks.
56
58
  kwargs: Additional arguments are treated as an alternate way to specify style. If
57
59
  both "style" and additional **kwargs are provided, they are both applied in that
58
60
  order (style, then **kwargs).
@@ -110,7 +112,7 @@ def network(
110
112
  if aspect is not None:
111
113
  ax.set_aspect(aspect)
112
114
 
113
- _postprocess_axis(ax, artists)
115
+ _postprocess_axes(ax, artists, strip=strip_axes)
114
116
 
115
117
  if np.isscalar(margins):
116
118
  margins = (margins, margins)
@@ -132,6 +134,7 @@ def tree(
132
134
  title: Optional[str] = None,
133
135
  aspect: Optional[str | float] = None,
134
136
  margins: float | tuple[float, float] = 0,
137
+ strip_axes: bool = True,
135
138
  **kwargs,
136
139
  ) -> TreeArtist:
137
140
  """Plot a tree using the specified layout.
@@ -143,6 +146,7 @@ def tree(
143
146
  show_support: If True, show the support values for the nodes (assumed to be from 0 to 100,
144
147
  rounded to nearest integer). If both this parameter and vertex_labels are set,
145
148
  show_support takes precedence and hides the vertex labels.
149
+ strip_axes: If True, remove axis spines and ticks.
146
150
 
147
151
  Returns:
148
152
  A TreeArtist object, set as a direct child of the matplotlib Axes.
@@ -173,7 +177,7 @@ def tree(
173
177
  if aspect is not None:
174
178
  ax.set_aspect(aspect)
175
179
 
176
- _postprocess_axis(ax, [artist])
180
+ _postprocess_axes(ax, [artist], strip=strip_axes)
177
181
 
178
182
  if np.isscalar(margins):
179
183
  margins = (margins, margins)
@@ -184,18 +188,19 @@ def tree(
184
188
 
185
189
 
186
190
  # INTERNAL ROUTINES
187
- def _postprocess_axis(ax, artists):
191
+ def _postprocess_axes(ax, artists, strip=True):
188
192
  """Postprocess axis after plotting."""
189
193
 
190
- # Despine
191
- ax.spines["right"].set_visible(False)
192
- ax.spines["top"].set_visible(False)
193
- ax.spines["left"].set_visible(False)
194
- ax.spines["bottom"].set_visible(False)
194
+ if strip:
195
+ # Despine
196
+ ax.spines["right"].set_visible(False)
197
+ ax.spines["top"].set_visible(False)
198
+ ax.spines["left"].set_visible(False)
199
+ ax.spines["bottom"].set_visible(False)
195
200
 
196
- # Remove axis ticks
197
- ax.set_xticks([])
198
- ax.set_yticks([])
201
+ # Remove axis ticks
202
+ ax.set_xticks([])
203
+ ax.set_yticks([])
199
204
 
200
205
  # Set new data limits
201
206
  bboxes = []
iplotx/tree.py CHANGED
@@ -329,8 +329,8 @@ class TreeArtist(mpl.artist.Artist):
329
329
 
330
330
  if "cmap" in edge_style:
331
331
  cmap_fun = _build_cmap_fun(
332
- edge_style["color"],
333
- edge_style["cmap"],
332
+ edge_style,
333
+ "color",
334
334
  )
335
335
  else:
336
336
  cmap_fun = None
@@ -537,16 +537,17 @@ class TreeArtist(mpl.artist.Artist):
537
537
  labels = self._get_label_series("edge")
538
538
  edge_style = get_style(".edge")
539
539
 
540
+ edge_df = self._ipx_internal_data["edge_df"].set_index(["_ipx_source", "_ipx_target"])
541
+
540
542
  if "cmap" in edge_style:
541
543
  cmap_fun = _build_cmap_fun(
542
- edge_style["color"],
543
- edge_style["cmap"],
544
+ edge_style,
545
+ "color",
546
+ internal=edge_df,
544
547
  )
545
548
  else:
546
549
  cmap_fun = None
547
550
 
548
- edge_df = self._ipx_internal_data["edge_df"].set_index(["_ipx_source", "_ipx_target"])
549
-
550
551
  if "cmap" in edge_style:
551
552
  colorarray = []
552
553
  edgepatches = []
@@ -1,6 +1,8 @@
1
+ from typing import Optional, Any
1
2
  from functools import wraps, partial
2
3
  from math import atan2
3
4
  import numpy as np
5
+ import pandas as pd
4
6
  import matplotlib as mpl
5
7
 
6
8
  from .geometry import (
@@ -162,15 +164,39 @@ def _compute_mid_coord_and_rot(path, trans):
162
164
  return coord, rot
163
165
 
164
166
 
165
- def _build_cmap_fun(values, cmap, norm=None):
166
- """Map colormap on top of numerical values."""
167
+ def _build_cmap_fun(
168
+ style: dict[str, Any],
169
+ key: str,
170
+ norm=None,
171
+ internal: Optional[pd.DataFrame] = None,
172
+ ):
173
+ """Map colormap on top of numerical values.
174
+
175
+ Parameters:
176
+ style: A dictionary of style properties.
177
+ key: The key in the style dictionary to look for values. Values can be a list/array,
178
+ a dictionary of numerical values, or a string, in which case the corresponding
179
+ column in the "internal" DataFrame is used as an array of numerical values.
180
+ norm: An optional matplotlib Normalize instance. If None, the values are normalized.
181
+ internal: An optional DataFrame, required if "values" is a string.
182
+ """
183
+ values = style[key]
184
+ cmap = style["cmap"]
185
+
167
186
  cmap = mpl.cm._ensure_cmap(cmap)
168
187
 
169
- if np.isscalar(values):
170
- values = [values]
188
+ if isinstance(values, str):
189
+ if not isinstance(internal, pd.DataFrame):
190
+ raise ValueError("If 'values' is a string, 'internal' must be a DataFrame.")
191
+ values = internal[values].values
192
+ internal[key] = values
193
+
194
+ else:
195
+ if np.isscalar(values):
196
+ values = [values]
171
197
 
172
- if isinstance(values, dict):
173
- values = np.array(list(values.values()))
198
+ if isinstance(values, dict):
199
+ values = np.array(list(values.values()))
174
200
 
175
201
  if norm is None:
176
202
  vmin = np.nanmin(values)
iplotx/version.py CHANGED
@@ -2,4 +2,4 @@
2
2
  iplotx version information module.
3
3
  """
4
4
 
5
- __version__ = "0.6.4"
5
+ __version__ = "0.6.6"
iplotx/vertex.py CHANGED
@@ -68,7 +68,7 @@ class VertexCollection(PatchCollection):
68
68
  """
69
69
 
70
70
  self._index = layout.index
71
- self._style = style
71
+ self._style = style if style is not None else {}
72
72
  self._layout = layout
73
73
  self._layout_coordinate_system = layout_coordinate_system
74
74
 
@@ -94,6 +94,9 @@ class VertexCollection(PatchCollection):
94
94
  if self._labels is not None:
95
95
  self._compute_label_collection()
96
96
 
97
+ zorder = self._style.get("zorder", 1)
98
+ self.set_zorder(zorder)
99
+
97
100
  def __len__(self):
98
101
  """Return the number of vertices in the collection."""
99
102
  return len(self.get_paths())
@@ -226,8 +229,8 @@ class VertexCollection(PatchCollection):
226
229
  style = self._style or {}
227
230
  if "cmap" in style:
228
231
  cmap_fun = _build_cmap_fun(
229
- style["facecolor"],
230
- style["cmap"],
232
+ style,
233
+ "facecolor",
231
234
  )
232
235
  else:
233
236
  cmap_fun = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iplotx
3
- Version: 0.6.4
3
+ Version: 0.6.6
4
4
  Summary: Plot networkx from igraph and networkx.
5
5
  Project-URL: Homepage, https://github.com/fabilab/iplotx
6
6
  Project-URL: Documentation, https://readthedocs.org/iplotx
@@ -4,22 +4,22 @@ iplotx/cascades.py,sha256=OPqF7Huls-HFmDA5MCF6DEZlUeRVaXsbQcHBoKAgNJs,8182
4
4
  iplotx/groups.py,sha256=_9KdIiTAi1kXtd2mDywgBJCbqoRq2z-5fzOPf76Wgb8,6287
5
5
  iplotx/label.py,sha256=6am3a0ejcW_bWEXSOODE1Ke3AyCU1lJ45RfnXNbHAQw,8923
6
6
  iplotx/layout.py,sha256=KxmRLqjo8AYCBAmXez8rIiLU2sM34qhb6ox9AHYwRyE,4839
7
- iplotx/network.py,sha256=SlmDgc4tbCfvO08QWk-jUXrUfaz6S3xoXQVg6rP1910,11345
8
- iplotx/plotting.py,sha256=yACxkD6unKc5eDsAp7ZabRCAwLEXBowSMESX2oGNBDU,7291
9
- iplotx/tree.py,sha256=S_9tf8Mixv9P5dq616tjxuxdDYRmUXLNAcSXTxEgm_I,27310
7
+ iplotx/network.py,sha256=LaW9zQZ4sKiDVb25_icnquGNnN7HrKC7NO07o6PSmGI,11527
8
+ iplotx/plotting.py,sha256=eLsdOOZ15SQTcNeSxqpxYtzfiVPo5Npqgg5wrxyJfrs,7561
9
+ iplotx/tree.py,sha256=Zzz7nCPZrSjh9_yHXFdd8hjbF-FYURTZs00rUHc4OT8,27304
10
10
  iplotx/typing.py,sha256=QLdzV358IiD1CFe88MVp0D77FSx5sSAVUmM_2WPPE8I,1463
11
- iplotx/version.py,sha256=vBajZHKRfgreYlbl4HM9tYBbRK-Xcm4ddKiCB6_M6nY,66
12
- iplotx/vertex.py,sha256=T9j8Copv88cbh6ztC8uSGo4tIVERuHYEIeHf53Uh2aE,14578
13
- iplotx/edge/__init__.py,sha256=VkAsuxphQa-co79MZWzWErkRAkp97CwB20ozPEnpvrM,26888
11
+ iplotx/version.py,sha256=96OI0pGtnG3BPfu9NZSoIkqwIZYegDQOF3vky5yMB_k,66
12
+ iplotx/vertex.py,sha256=-JZaQPjIAWWP8Wap1HyR4g4HXLNGLwjf4v6jyo994Tk,14671
13
+ iplotx/edge/__init__.py,sha256=HlxeIs88RbRrTetJNLcoq9gjV7cBhOdqQoiVZFFylFc,27081
14
14
  iplotx/edge/arrow.py,sha256=C4XoHGCYou1z2alz5Q2VhdaWYEzgebtEF70zVYY_frk,15533
15
15
  iplotx/edge/geometry.py,sha256=tiaF4PzvsNBoROrEgcCsw0YdxxZr3oBxF4ord_k4ThA,15069
16
16
  iplotx/edge/leaf.py,sha256=SyGMv2PIOoH0pey8-aMVaZheK3hNe1Qz_okcyWbc4E4,4268
17
17
  iplotx/edge/ports.py,sha256=BpkbiEhX4mPBBAhOv4jcKFG4Y8hxXz5GRtVLCC0jbtI,1235
18
18
  iplotx/ingest/__init__.py,sha256=S0YfnXcFKseB7ZBQc4yRt0cNDsLlhqdom0TmSY3OY2E,4756
19
19
  iplotx/ingest/heuristics.py,sha256=715VqgfKek5LOJnu1vTo7RqPgCl-Bb8Cf6o7_Tt57fA,5797
20
- iplotx/ingest/typing.py,sha256=hVEcAREjFFFbAWsxRkQuvpy1B4L7JEv_NRVVmrEbUVk,13984
20
+ iplotx/ingest/typing.py,sha256=61LwNwrTHVh8eqqC778Gr81zPYcUKW61mDgGCCsuGSk,14181
21
21
  iplotx/ingest/providers/network/igraph.py,sha256=8dWeaQ_ZNdltC098V2YeLXsGdJHQnBa6shF1GAfl0Zg,2973
22
- iplotx/ingest/providers/network/networkx.py,sha256=FIXMI3hXU1WtAzPVlQZcz47b-4V2omeHttnNTgS2gQw,4328
22
+ iplotx/ingest/providers/network/networkx.py,sha256=4sPFOx87ipOYlXu0hjJl25Z4So_RnhO1CYYozGp-wJg,4626
23
23
  iplotx/ingest/providers/network/simple.py,sha256=e_aHhiHhN9DrMoNrt7tEMPURXGhQ1TYRPzsxDEptUlc,3766
24
24
  iplotx/ingest/providers/tree/biopython.py,sha256=4N_54cVyHHPcASJZGr6pHKE2p5R3i8Cm307SLlSLHLA,1480
25
25
  iplotx/ingest/providers/tree/cogent3.py,sha256=JmELbDK7LyybiJzFNbmeqZ4ySJoDajvFfJebpNfFKWo,1073
@@ -31,8 +31,8 @@ iplotx/style/leaf_info.py,sha256=JoX1cPjRM_k3f93jzUPQ3gPlVP4wY_n032nOVhrgelU,969
31
31
  iplotx/style/library.py,sha256=wO-eeY3EZfAl0v21aX9f5_MiZhHuL2kGsBYA3uJkIGs,8535
32
32
  iplotx/utils/geometry.py,sha256=6RrC6qaB0-1vIk1LhGA4CfsiMd-9JNniSPyL_l9mshE,9245
33
33
  iplotx/utils/internal.py,sha256=WWfcZDGK8Ut1y_tOHRGg9wSqY1bwSeLQO7dHM_8Tvwo,107
34
- iplotx/utils/matplotlib.py,sha256=TutVJ1dEWYgX_-CY6MvdhRvWYqxpByGb3TKrSByYPNM,5330
34
+ iplotx/utils/matplotlib.py,sha256=wELE73quQv10-1w9uA5eDTgkZkylJvjg7pd3K5tZPOo,6294
35
35
  iplotx/utils/style.py,sha256=wMWxJykxBD-JmcN8-rSKlWcV6pMfwKgR4EzSpk_NX8k,547
36
- iplotx-0.6.4.dist-info/METADATA,sha256=YOeufF6IOi-7LNky1OZb6lYoBb_4WEz4IINvzM-96UA,4908
37
- iplotx-0.6.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
- iplotx-0.6.4.dist-info/RECORD,,
36
+ iplotx-0.6.6.dist-info/METADATA,sha256=48xdO8iccA0Y53CJLK-7xCI3rRYptFlq1MQJ_UR-cnE,4908
37
+ iplotx-0.6.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ iplotx-0.6.6.dist-info/RECORD,,
File without changes