icplot 0.0.8__tar.gz → 0.0.9__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 (36) hide show
  1. {icplot-0.0.8/src/icplot.egg-info → icplot-0.0.9}/PKG-INFO +2 -2
  2. {icplot-0.0.8 → icplot-0.0.9}/pyproject.toml +2 -2
  3. icplot-0.0.9/src/icplot/color.py +54 -0
  4. icplot-0.0.9/src/icplot/graph/__init__.py +2 -0
  5. icplot-0.0.9/src/icplot/graph/axis.py +44 -0
  6. icplot-0.0.9/src/icplot/graph/matplotlib.py +101 -0
  7. icplot-0.0.9/src/icplot/graph/plot.py +92 -0
  8. icplot-0.0.9/src/icplot/graph/plotter.py +22 -0
  9. icplot-0.0.9/src/icplot/graph/series.py +111 -0
  10. {icplot-0.0.8 → icplot-0.0.9/src/icplot.egg-info}/PKG-INFO +2 -2
  11. {icplot-0.0.8 → icplot-0.0.9}/src/icplot.egg-info/SOURCES.txt +3 -1
  12. {icplot-0.0.8 → icplot-0.0.9}/src/icplot.egg-info/requires.txt +1 -1
  13. {icplot-0.0.8 → icplot-0.0.9}/test/test_line_plots.py +8 -2
  14. icplot-0.0.8/src/icplot/color.py +0 -13
  15. icplot-0.0.8/src/icplot/graph/__init__.py +0 -1
  16. icplot-0.0.8/src/icplot/graph/matplotlib_plot_backend.py +0 -69
  17. icplot-0.0.8/src/icplot/graph/plot.py +0 -163
  18. icplot-0.0.8/src/icplot/graph/series.py +0 -54
  19. {icplot-0.0.8 → icplot-0.0.9}/LICENSE +0 -0
  20. {icplot-0.0.8 → icplot-0.0.9}/README.md +0 -0
  21. {icplot-0.0.8 → icplot-0.0.9}/setup.cfg +0 -0
  22. {icplot-0.0.8 → icplot-0.0.9}/src/icplot/__init__.py +0 -0
  23. {icplot-0.0.8 → icplot-0.0.9}/src/icplot/cairo_interface.py +0 -0
  24. {icplot-0.0.8 → icplot-0.0.9}/src/icplot/gantt.py +0 -0
  25. {icplot-0.0.8 → icplot-0.0.9}/src/icplot/geometry.py +0 -0
  26. {icplot-0.0.8 → icplot-0.0.9}/src/icplot/image_utils.py +0 -0
  27. {icplot-0.0.8 → icplot-0.0.9}/src/icplot/main_cli.py +0 -0
  28. {icplot-0.0.8 → icplot-0.0.9}/src/icplot/project_elements.py +0 -0
  29. {icplot-0.0.8 → icplot-0.0.9}/src/icplot/tex_interface.py +0 -0
  30. {icplot-0.0.8 → icplot-0.0.9}/src/icplot.egg-info/dependency_links.txt +0 -0
  31. {icplot-0.0.8 → icplot-0.0.9}/src/icplot.egg-info/entry_points.txt +0 -0
  32. {icplot-0.0.8 → icplot-0.0.9}/src/icplot.egg-info/top_level.txt +0 -0
  33. {icplot-0.0.8 → icplot-0.0.9}/test/test_cairo_interface.py +0 -0
  34. {icplot-0.0.8 → icplot-0.0.9}/test/test_gantt_chart.py +0 -0
  35. {icplot-0.0.8 → icplot-0.0.9}/test/test_image_utils.py +0 -0
  36. {icplot-0.0.8 → icplot-0.0.9}/test/test_tex_interface.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: icplot
3
- Version: 0.0.8
3
+ Version: 0.0.9
4
4
  Summary: Utilities for generating plots and graphics for technical reports.
5
5
  Author-email: "James Grogan, Irish Centre for High End Computing" <james.grogan@ichec.ie>
6
6
  Project-URL: Repository, https://git.ichec.ie/performance/toolshed/icplot
@@ -16,7 +16,7 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: wand
18
18
  Requires-Dist: matplotlib
19
- Requires-Dist: iccore==0.0.9
19
+ Requires-Dist: iccore==0.0.10
20
20
  Provides-Extra: test
21
21
  Requires-Dist: pytest; extra == "test"
22
22
  Requires-Dist: pytest-cov; extra == "test"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "icplot"
3
- version = "0.0.8"
3
+ version = "0.0.9"
4
4
  authors = [
5
5
  { name="James Grogan, Irish Centre for High End Computing", email="james.grogan@ichec.ie" },
6
6
  ]
@@ -18,7 +18,7 @@ keywords = ["Publishing", "Technical Reports", "Graphics"]
18
18
 
19
19
  dependencies = ["wand",
20
20
  "matplotlib",
21
- "iccore==0.0.9"]
21
+ "iccore==0.0.10"]
22
22
 
23
23
  [project.urls]
24
24
  Repository = "https://git.ichec.ie/performance/toolshed/icplot"
@@ -0,0 +1,54 @@
1
+ """
2
+ This module handles colors
3
+ """
4
+
5
+ from typing import Callable
6
+
7
+ from iccore.serialization import Serializable
8
+
9
+
10
+ class Color(Serializable):
11
+ """
12
+ A color class, internal storage is rgba as float.
13
+ """
14
+
15
+ def __init__(self, r: float = 0.0, g: float = 0.0, b: float = 0.0, a: float = 1.0):
16
+ self.r = r
17
+ self.g = g
18
+ self.b = b
19
+ self.a = a
20
+
21
+ @staticmethod
22
+ def from_list(data: list):
23
+ return Color(data[0], data[1], data[2])
24
+
25
+ def serialize(self):
26
+ return {"r": self.r, "g": self.g, "b": self.b, "a": self.a}
27
+
28
+ def as_list(self) -> list:
29
+ return [self.r, self.g, self.b]
30
+
31
+ def is_black(self) -> bool:
32
+ return self.r == 0.0 and self.g == 0.0 and self.b == 0.0 and self.a == 1.0
33
+
34
+
35
+ class ColorMap:
36
+ """
37
+ A mapping from a flat index to a color
38
+ """
39
+
40
+ def __init__(self, label: str, data_func: Callable):
41
+ self.label = label
42
+ self.data_func = data_func
43
+ self.start_offset = 0.25
44
+ self.scale = 2
45
+
46
+ def get_color(self, cursor: int, values: list) -> Color:
47
+ """
48
+ Returns a colour based on a cmap and how far across
49
+ the datasets you are
50
+ """
51
+ position = cursor / len(values)
52
+ return Color.from_list(
53
+ self.data_func(self.start_offset + (position / self.scale))
54
+ )
@@ -0,0 +1,2 @@
1
+ from .plot import * # NOQA
2
+ from .series import * # NOQA
@@ -0,0 +1,44 @@
1
+ """
2
+ This module has a plot's axis
3
+ """
4
+
5
+ from iccore.serialization import Serializable
6
+
7
+
8
+ class Range(Serializable):
9
+ """
10
+ This is a sequence of numbers specified by lower and upper
11
+ bounds and a step size. Eg (1, 5, 1) gives: [1, 2, 3, 4]
12
+ """
13
+
14
+ def __init__(self, lower, upper, step):
15
+ self.lower = lower
16
+ self.upper = upper
17
+ self.step = step
18
+
19
+ def eval(self) -> list:
20
+ return list(range(self.lower, self.upper, self.step))
21
+
22
+ def serialize(self):
23
+ return {"lower": self.lower, "upper": self.upper, "step": self.step}
24
+
25
+
26
+ class PlotAxis(Serializable):
27
+ """
28
+ This is a plot's axis
29
+ """
30
+
31
+ def __init__(self, label: str = ""):
32
+ self.label = label
33
+ self.ticks: Range | None = None
34
+
35
+ def set_ticks(self, lower: int, upper: int, step: int):
36
+ self.ticks = Range(lower, upper + 1, step)
37
+
38
+ def serialize(self):
39
+ return {"label": self.label, "ticks": self.ticks.serialize()}
40
+
41
+ def get_resolved_ticks(self) -> list:
42
+ if self.ticks:
43
+ return self.ticks.eval()
44
+ return []
@@ -0,0 +1,101 @@
1
+ from pathlib import Path
2
+ import logging
3
+ from typing import cast
4
+
5
+ logging.getLogger("matplotlib").setLevel(logging.WARNING)
6
+ import matplotlib as mpl # NOQA
7
+
8
+ default_backend = mpl.get_backend()
9
+ mpl.use("Agg")
10
+ import matplotlib.pyplot as plt # NOQA
11
+
12
+ from icplot.color import ColorMap # NOQA
13
+ from .series import PlotSeries, LinePlotSeries, ScatterPlotSeries, ImageSeries # NOQA
14
+ from .plot import Plot, GridPlot # NOQA
15
+ from .plotter import Plotter # NOQA
16
+
17
+
18
+ class MatplotlibColorMap(ColorMap):
19
+ """
20
+ A matplotlib based colormap
21
+ """
22
+
23
+ def __init__(self, label: str):
24
+ super().__init__(label, mpl.colormaps[label])
25
+
26
+
27
+ class MatplotlibPlotter(Plotter):
28
+ """
29
+ A plot backend for matplotlib
30
+ """
31
+
32
+ def __init__(self, cmap_label="viridis"):
33
+ super().__init__(MatplotlibColorMap(cmap_label))
34
+
35
+ def set_decorations(self, ax, plot: Plot):
36
+ ax.legend(loc="upper left")
37
+ if plot.x_axis.label:
38
+ ax.set_xlabel(plot.x_axis.label)
39
+ if plot.y_axis.label:
40
+ ax.set_ylabel(plot.y_axis.label)
41
+ if plot.x_axis.ticks:
42
+ ax.set_xticks(plot.x_axis.get_resolved_ticks())
43
+ if plot.y_axis.ticks:
44
+ ax.set_yticks(plot.y_axis.get_resolved_ticks())
45
+ if plot.title:
46
+ ax.set_title(plot.title)
47
+
48
+ def plot_line(self, ax, series: LinePlotSeries):
49
+ ax.plot(
50
+ series.x,
51
+ series.y,
52
+ label=series.label,
53
+ color=series.get_color().as_list(),
54
+ marker=series.marker,
55
+ )
56
+
57
+ def plot_scatter(self, ax, series: ScatterPlotSeries):
58
+ ax.scatter(series.data, label=series.label, color=series.get_color().as_list())
59
+
60
+ def plot_image(self, ax, series: ImageSeries):
61
+ ax.imshow(series.data)
62
+ ax.axis("off")
63
+
64
+ def plot_series(self, ax, series: PlotSeries):
65
+ if series.series_type == "line":
66
+ self.plot_line(ax, cast(LinePlotSeries, series))
67
+ elif series.series_type == "scatter":
68
+ self.plot_scatter(ax, cast(ScatterPlotSeries, series))
69
+ elif series.series_type == "image":
70
+ self.plot_image(ax, cast(ImageSeries, series))
71
+
72
+ def plot(self, plot: Plot, path: Path | None = None):
73
+
74
+ self.apply_cmap_colors(plot)
75
+
76
+ fig, ax = plt.subplots()
77
+
78
+ self.set_decorations(ax, plot)
79
+
80
+ for series in plot.series:
81
+ self.plot_series(ax, series)
82
+
83
+ self.render(fig, path)
84
+
85
+ def plot_grid(self, plot: GridPlot, path: Path | None = None, num_samples: int = 0):
86
+
87
+ rows, cols, series = plot.get_subplots(num_samples)
88
+ fig, axs = plt.subplots(rows, cols)
89
+
90
+ for ax, series_item in zip(axs, series):
91
+ self.plot_series(ax, series_item)
92
+
93
+ self.render(fig, path)
94
+
95
+ def render(self, fig, path: Path | None = None):
96
+ if path:
97
+ fig.savefig(path)
98
+ else:
99
+ plt.switch_backend(default_backend)
100
+ fig.show()
101
+ plt.switch_backend("Agg")
@@ -0,0 +1,92 @@
1
+ """
2
+ This module has content for generating plots
3
+ """
4
+
5
+ import random
6
+
7
+ from iccore.serialization import Serializable
8
+
9
+ from .axis import PlotAxis
10
+ from .series import PlotSeries
11
+
12
+
13
+ class Plot(Serializable):
14
+ """
15
+ A generic plot with optional axis ticks
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ title: str = "",
21
+ x_label: str = "",
22
+ y_label: str = "",
23
+ legend_label: str = "",
24
+ ) -> None:
25
+ self.series: list[PlotSeries] = []
26
+ self.title = title
27
+ self.size: tuple | None = None
28
+ self.plot_type = ""
29
+ self.x_axis = PlotAxis(x_label)
30
+ self.y_axis = PlotAxis(y_label)
31
+ self.legend_label = legend_label
32
+
33
+ def set_x_ticks(self, lower, upper, step):
34
+ self.x_axis.set_ticks(lower, upper, step)
35
+
36
+ def set_y_ticks(self, lower, upper, step):
37
+ self.y_axis.set_ticks(lower, upper, step)
38
+
39
+ def add_series(self, series):
40
+ self.series.append(series)
41
+
42
+ def serialize(self):
43
+ return {
44
+ "series": [s.serialize() for s in self.series],
45
+ "title": self.title,
46
+ "type": self.plot_type,
47
+ "x_axis": self.x_axis.serialize(),
48
+ "y_axis": self.y_axis.serialize(),
49
+ "legend_label": self.legend_label,
50
+ }
51
+
52
+
53
+ class GridPlot(Plot):
54
+ """
55
+ Make a grid of plots
56
+ """
57
+
58
+ def __init__(
59
+ self, data, title: str = "", stride: int = 4, size: tuple = (25, 20)
60
+ ) -> None:
61
+ super().__init__(title)
62
+ self.data = data
63
+ self.stride = stride
64
+ self.size = size
65
+
66
+ def get_series_indices(self, num_samples: int = 0):
67
+ rows = num_samples // self.stride
68
+ cols = num_samples // rows
69
+ len_data = len(self.data)
70
+
71
+ if num_samples == 0:
72
+ indices = list(range(0, len_data))
73
+ else:
74
+ indices = [random.randint(0, len_data - 1) for _ in range(num_samples)]
75
+ return rows, cols, indices
76
+
77
+ def get_subplots(self, num_samples: int = 0):
78
+ rows, cols, indices = self.get_series_indices(num_samples)
79
+
80
+ subplots = []
81
+ count = 1
82
+ for index in indices:
83
+ if num_samples > 0 and count == num_samples + 1:
84
+ break
85
+ if isinstance(self.data[index], list):
86
+ for series in self.data[index]:
87
+ subplots.append(series)
88
+ count += 1
89
+ else:
90
+ subplots.append(self.data[index])
91
+ count += 1
92
+ return rows, cols, subplots
@@ -0,0 +1,22 @@
1
+ from pathlib import Path
2
+
3
+ from icplot.color import ColorMap
4
+
5
+ from .plot import Plot
6
+
7
+
8
+ class Plotter:
9
+
10
+ def __init__(self, cmap: ColorMap):
11
+ self.cmap = cmap
12
+
13
+ def apply_cmap_colors(self, plot: Plot):
14
+ skipped_color = 0
15
+ for idx, series in enumerate(plot.series):
16
+ if not series.color:
17
+ series.color = self.cmap.get_color(idx - skipped_color, plot.series)
18
+ else:
19
+ skipped_color += 1
20
+
21
+ def plot(self, plot: Plot, path: Path | None = None):
22
+ pass
@@ -0,0 +1,111 @@
1
+ from iccore.serialization import Serializable
2
+
3
+ from icplot.color import Color
4
+
5
+
6
+ class PlotSeries(Serializable):
7
+ """
8
+ A data series in a plot, such as a single line in a line-plot
9
+ """
10
+
11
+ def __init__(self, label: str = "", color: Color | None = None) -> None:
12
+ self.label = label
13
+ self.color = color
14
+ self.series_type = ""
15
+
16
+ def get_color(self) -> Color:
17
+ if self.color:
18
+ return self.color
19
+ return Color()
20
+
21
+ def serialize(self):
22
+
23
+ color_val = {}
24
+ if self.color:
25
+ color_val = self.color.serialize()
26
+
27
+ return {
28
+ "label": self.label,
29
+ "color": color_val,
30
+ "type": self.series_type,
31
+ }
32
+
33
+
34
+ class ImageSeries(PlotSeries):
35
+ """
36
+ A plot data series where the elements are images
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ data,
42
+ label: str = "",
43
+ transform=None,
44
+ ):
45
+ super().__init__(label)
46
+ self.data = data
47
+ self.series_type = "image"
48
+ self.transform = transform
49
+
50
+ def serialize(self):
51
+ ret = super().serialize()
52
+ ret["data"] = self.data
53
+ return ret
54
+
55
+
56
+ class LinePlotSeries(PlotSeries):
57
+ """
58
+ A plot series for line plots
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ x: list | None = None,
64
+ y: list | None = None,
65
+ label: str = "",
66
+ color: Color | None = None,
67
+ marker: str = "o",
68
+ ) -> None:
69
+ super().__init__(label, color)
70
+ self.marker = marker
71
+ self.series_type = "line"
72
+ if x:
73
+ self.x = x
74
+ else:
75
+ self.x = []
76
+ if y:
77
+ self.y = y
78
+ else:
79
+ self.y = []
80
+
81
+ def add_entry(self, x_point: float, y_point: float):
82
+ self.x.append(x_point)
83
+ self.y.append(y_point)
84
+
85
+ def sort(self):
86
+ """
87
+ Sorts by x values
88
+ """
89
+ tuples = list(zip(self.x, self.y))
90
+ tuples.sort(key=lambda tup: tup[0])
91
+ for idx, _ in enumerate(tuples):
92
+ self.x[idx] = tuples[idx][0]
93
+ self.y[idx] = tuples[idx][1]
94
+
95
+ def serialize(self):
96
+ ret = super().serialize()
97
+ ret["x"] = self.x
98
+ ret["y"] = self.y
99
+ return ret
100
+
101
+
102
+ class ScatterPlotSeries(PlotSeries):
103
+ def __init__(self, data, label: str = "", color: Color | None = None):
104
+ super().__init__(label, color)
105
+ self.data = data
106
+ self.series_type = "scatter"
107
+
108
+ def serialize(self):
109
+ ret = super().serialize()
110
+ ret["data"] = self.data
111
+ return ret
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: icplot
3
- Version: 0.0.8
3
+ Version: 0.0.9
4
4
  Summary: Utilities for generating plots and graphics for technical reports.
5
5
  Author-email: "James Grogan, Irish Centre for High End Computing" <james.grogan@ichec.ie>
6
6
  Project-URL: Repository, https://git.ichec.ie/performance/toolshed/icplot
@@ -16,7 +16,7 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: wand
18
18
  Requires-Dist: matplotlib
19
- Requires-Dist: iccore==0.0.9
19
+ Requires-Dist: iccore==0.0.10
20
20
  Provides-Extra: test
21
21
  Requires-Dist: pytest; extra == "test"
22
22
  Requires-Dist: pytest-cov; extra == "test"
@@ -18,8 +18,10 @@ src/icplot.egg-info/entry_points.txt
18
18
  src/icplot.egg-info/requires.txt
19
19
  src/icplot.egg-info/top_level.txt
20
20
  src/icplot/graph/__init__.py
21
- src/icplot/graph/matplotlib_plot_backend.py
21
+ src/icplot/graph/axis.py
22
+ src/icplot/graph/matplotlib.py
22
23
  src/icplot/graph/plot.py
24
+ src/icplot/graph/plotter.py
23
25
  src/icplot/graph/series.py
24
26
  test/test_cairo_interface.py
25
27
  test/test_gantt_chart.py
@@ -1,6 +1,6 @@
1
1
  wand
2
2
  matplotlib
3
- iccore==0.0.9
3
+ iccore==0.0.10
4
4
 
5
5
  [cairo]
6
6
  pycairo
@@ -1,12 +1,16 @@
1
1
  from pathlib import Path
2
2
 
3
3
  from icplot.graph import Plot, LinePlotSeries
4
+ from icplot.graph.matplotlib import MatplotlibPlotter
4
5
 
5
6
 
6
7
  def test_line_plot():
7
8
 
8
9
  plot = Plot(
9
- title="test", x_label="test_x", y_label="test_y", legend_label="test_legend"
10
+ title="test",
11
+ x_label="test_x",
12
+ y_label="test_y",
13
+ legend_label="test_legend"
10
14
  )
11
15
 
12
16
  data = [([0, 5, 10], [1, 2, 3]), ([0, 5, 10], [3, 6, 9]), ([0, 5, 10], [4, 8, 12])]
@@ -17,7 +21,9 @@ def test_line_plot():
17
21
  plot.set_x_ticks(0, 10, 5)
18
22
 
19
23
  output_path = Path() / "output.svg"
20
- plot.plot(output_path)
24
+
25
+ plotter = MatplotlibPlotter()
26
+ plotter.plot(plot, output_path)
21
27
 
22
28
  assert output_path.exists()
23
29
  output_path.unlink()
@@ -1,13 +0,0 @@
1
- class Color:
2
-
3
- def __init__(self, r=1, g=1, b=1, a=1):
4
- self.r = r
5
- self.g = g
6
- self.b = b
7
- self.a = a
8
-
9
-
10
- class ColorMap:
11
-
12
- def __init__(self, label: str):
13
- self.label = label
@@ -1 +0,0 @@
1
- from .plot import * # NOQA
@@ -1,69 +0,0 @@
1
- from pathlib import Path
2
- import logging
3
-
4
- logging.getLogger("matplotlib").setLevel(logging.WARNING)
5
- import matplotlib # NOQA
6
-
7
- default_backend = matplotlib.get_backend()
8
- matplotlib.use("Agg")
9
- import matplotlib.pyplot as plt # NOQA
10
- import matplotlib as mpl # NOQA
11
-
12
- from icplot.color import ColorMap # NOQA
13
-
14
-
15
- class MatplotlibColorMap(ColorMap):
16
-
17
- def __init__(self, label: str):
18
- super().__init__(label)
19
- self.c_map = mpl.colormaps[label]
20
- self.cmap_start_offset = 0.25
21
- self.cmap_scale = 2
22
-
23
- def get_color(self, cursor: int, values: list):
24
- """
25
- Returns a colour based on a cmap and how far across
26
- the datasets you are
27
- """
28
- position = cursor / len(values)
29
- return self.c_map(self.cmap_start_offset + (position / self.cmap_scale))
30
-
31
-
32
- class MatplotlibPlotBackend:
33
-
34
- def __init__(self):
35
- self.ax = plt.subplot(111)
36
-
37
- def set_decorations(self, x_label, y_label, title, x_ticks, y_ticks):
38
- self.ax.legend(loc="upper left")
39
- if x_label:
40
- self.ax.set_xlabel(x_label)
41
- if y_label:
42
- self.ax.set_ylabel(y_label)
43
- if title:
44
- self.ax.set_title(title)
45
- if x_ticks:
46
- self.ax.set_xticks(x_ticks)
47
- if y_ticks:
48
- self.ax.set_yticks(y_ticks)
49
-
50
- def plot_line(self, x, y, label, color):
51
- self.ax.plot(x, y, label=label, color=color)
52
-
53
- def plot_scatter(self, data, label, color):
54
- self.ax.plot(data, label=label, color=color)
55
-
56
- def make_subplot(self, rows, cols, count):
57
- plt.subplot(rows, cols, count)
58
-
59
- def plot_image(self, data):
60
- plt.imshow(data)
61
- plt.axis("off")
62
-
63
- def render(self, path: Path | None = None):
64
- if path:
65
- plt.savefig(path)
66
- else:
67
- plt.switch_backend(default_backend)
68
- plt.show()
69
- plt.switch_backend("Agg")
@@ -1,163 +0,0 @@
1
- """
2
- This module has content for generating plots
3
- """
4
-
5
- import random
6
- from pathlib import Path
7
- from typing import cast
8
- import json
9
-
10
- from .series import PlotSeries, LinePlotSeries, ScatterPlotSeries, ImageSeries
11
- from .matplotlib_plot_backend import MatplotlibPlotBackend, MatplotlibColorMap
12
-
13
-
14
- class Range:
15
-
16
- def __init__(self, lower, upper, step):
17
- self.lower = lower
18
- self.upper = upper
19
- self.step = step
20
-
21
- def eval(self):
22
- return range(self.lower, self.upper, self.step)
23
-
24
- def serialize(self):
25
- return {"lower": self.lower, "upper": self.upper, "step": self.step}
26
-
27
- def __str__(self):
28
- return json.dumps(self.serialize(), ident=4)
29
-
30
-
31
- class Plot:
32
- """
33
- A generic plot with optional axis ticks
34
- """
35
-
36
- def __init__(
37
- self,
38
- title: str = "",
39
- x_label: str = "",
40
- y_label: str = "",
41
- legend_label: str = "",
42
- ) -> None:
43
- self.series: list[PlotSeries] = []
44
- self.title = title
45
- self.size: tuple | None = None
46
- self.plot_type = ""
47
- self.x_label = x_label
48
- self.y_label = y_label
49
- self.legend_label = legend_label
50
- self.x_ticks: Range | None = None
51
- self.y_ticks: Range | None = None
52
- self.backend = MatplotlibPlotBackend()
53
- self.cmap = MatplotlibColorMap("viridis")
54
-
55
- def set_colour_map(self, cmap):
56
- self.c_map = MatplotlibColorMap(cmap)
57
-
58
- def set_x_ticks(self, lower, upper, step):
59
- self.x_ticks = Range(lower, upper + 1, step)
60
-
61
- def set_y_ticks(self, lower, upper, step):
62
- self.y_ticks = Range(lower, upper + 1, step)
63
-
64
- def set_decorations(self):
65
-
66
- xtick_range = None
67
- ytick_range = None
68
- if self.x_ticks:
69
- xtick_range = self.x_ticks.eval()
70
- if self.y_ticks:
71
- ytick_range = self.y_ticks.eval()
72
-
73
- self.backend.set_decorations(
74
- self.x_label,
75
- self.y_label,
76
- self.title,
77
- xtick_range,
78
- ytick_range,
79
- )
80
-
81
- def add_series(self, series):
82
- self.series.append(series)
83
-
84
- def plot_series(self, series, label):
85
- if series.series_type == "line":
86
- line_series = cast(LinePlotSeries, series)
87
- self.backend.plot_line(
88
- line_series.x, line_series.y, label=label, color=series.color
89
- )
90
- elif series.series_type == "scatter":
91
- scatter_series = cast(ScatterPlotSeries, series)
92
- self.backend.plot_scatter(
93
- scatter_series.data, label=label, color=series.color
94
- )
95
- elif series.series_type == "image":
96
- image_series = cast(ImageSeries, series)
97
- self.backend.plot_image(image_series.data)
98
-
99
- def plot(self, path: Path | None = None):
100
- for idx, series in enumerate(self.series):
101
- series.color = self.cmap.get_color(idx, self.series)
102
-
103
- first = True
104
- for series in self.series:
105
- label = series.label
106
- if first and self.legend_label:
107
- label = f"{label} {self.legend_label}"
108
- first = False
109
- self.plot_series(series, label)
110
-
111
- self.set_decorations()
112
- self.backend.render(path)
113
-
114
- def serialize(self):
115
- return {
116
- "series": [s.serialize() for s in self.series],
117
- "title": self.title,
118
- "type": self.plot_type,
119
- "xlabel": self.x_label,
120
- "ylabel": self.y_label,
121
- "legend_label": self.legend_label,
122
- "xticks": self.x_ticks.serialize(),
123
- "yticks": self.y_ticks.serialize(),
124
- }
125
-
126
-
127
- class GridPlot(Plot):
128
- """
129
- Make a grid of plots
130
- """
131
-
132
- def __init__(
133
- self, title: str = "", stride: int = 4, size: tuple = (25, 20)
134
- ) -> None:
135
- super().__init__(title)
136
- self.stride = stride
137
- self.size = size
138
-
139
- def _subplot(self, rows: int, cols: int, count: int, series):
140
-
141
- self.backend.make_subplot(rows, cols, count)
142
- self.plot_series(series, "")
143
- return count + 1
144
-
145
- def render(self, data, path: Path, num_samples: int = 0):
146
- rows = num_samples // self.stride
147
- cols = num_samples // rows
148
- count = 1
149
- if num_samples == 0:
150
- indices = [random.randint(0, len(data) - 1) for _ in range(num_samples)]
151
- else:
152
- indices = list(range(0, len(data)))
153
-
154
- for index in indices:
155
- if num_samples > 0 and count == num_samples + 1:
156
- break
157
- if isinstance(data[index], list):
158
- for series in data[index]:
159
- count = self._subplot(rows, cols, count, series)
160
- else:
161
- count = self._subplot(rows, cols, count, data[index])
162
-
163
- self.backend.render(path)
@@ -1,54 +0,0 @@
1
- import json
2
-
3
-
4
- class PlotSeries:
5
-
6
- def __init__(self, label: str = ""):
7
- self.label = label
8
- self.color: list[float] = [0.0, 0.0, 0.0]
9
- self.series_type = ""
10
-
11
- def serialize(self):
12
- return {"label": self.label, "color": self.color, "type": self.series_type}
13
-
14
- def __str__(self):
15
- return json.dumps(self.serialize(), indent=4)
16
-
17
-
18
- class ImageSeries(PlotSeries):
19
- def __init__(self, data, label: str = "", transform=None):
20
- super().__init__(label)
21
- self.data = data
22
- self.series_type = "image"
23
- self.transform = transform
24
-
25
- def serialize(self):
26
- ret = super().serialize()
27
- ret["data"] = self.data
28
- return ret
29
-
30
-
31
- class LinePlotSeries(PlotSeries):
32
- def __init__(self, x: list[float], y: list[float], label: str = "") -> None:
33
- super().__init__(label)
34
- self.series_type = "line"
35
- self.x = x
36
- self.y = y
37
-
38
- def serialize(self):
39
- ret = super().serialize()
40
- ret["x"] = self.x
41
- ret["y"] = self.y
42
- return ret
43
-
44
-
45
- class ScatterPlotSeries(PlotSeries):
46
- def __init__(self, data, label: str = ""):
47
- super().__init__(label)
48
- self.data = data
49
- self.series_type = "scatter"
50
-
51
- def serialize(self):
52
- ret = super().serialize()
53
- ret["data"] = self.data
54
- return ret
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes