kplot 1.0.6__tar.gz → 1.1.1__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 (63) hide show
  1. {kplot-1.0.6 → kplot-1.1.1}/PKG-INFO +2 -2
  2. {kplot-1.0.6 → kplot-1.1.1}/pyproject.toml +2 -2
  3. kplot-1.1.1/src/kplot/axes.py +63 -0
  4. kplot-1.1.1/src/kplot/cmaps.py +98 -0
  5. {kplot-1.0.6 → kplot-1.1.1}/src/kplot/hist.py +3 -7
  6. kplot-1.1.1/src/kplot/image.py +377 -0
  7. kplot-1.1.1/src/kplot/movie.py +173 -0
  8. {kplot-1.0.6 → kplot-1.1.1}/src/kplot/plot.py +28 -16
  9. {kplot-1.0.6 → kplot-1.1.1}/src/kplot/threeD/slider_images.py +20 -2
  10. {kplot-1.0.6 → kplot-1.1.1}/src/kplot/utils.py +20 -4
  11. kplot-1.0.6/src/kplot/.git/COMMIT_EDITMSG +0 -1
  12. kplot-1.0.6/src/kplot/.git/HEAD +0 -1
  13. kplot-1.0.6/src/kplot/.git/config +0 -13
  14. kplot-1.0.6/src/kplot/.git/description +0 -1
  15. kplot-1.0.6/src/kplot/.git/hooks/applypatch-msg.sample +0 -15
  16. kplot-1.0.6/src/kplot/.git/hooks/commit-msg.sample +0 -24
  17. kplot-1.0.6/src/kplot/.git/hooks/fsmonitor-watchman.sample +0 -174
  18. kplot-1.0.6/src/kplot/.git/hooks/post-update.sample +0 -8
  19. kplot-1.0.6/src/kplot/.git/hooks/pre-applypatch.sample +0 -14
  20. kplot-1.0.6/src/kplot/.git/hooks/pre-commit.sample +0 -49
  21. kplot-1.0.6/src/kplot/.git/hooks/pre-merge-commit.sample +0 -13
  22. kplot-1.0.6/src/kplot/.git/hooks/pre-push.sample +0 -53
  23. kplot-1.0.6/src/kplot/.git/hooks/pre-rebase.sample +0 -169
  24. kplot-1.0.6/src/kplot/.git/hooks/pre-receive.sample +0 -24
  25. kplot-1.0.6/src/kplot/.git/hooks/prepare-commit-msg.sample +0 -42
  26. kplot-1.0.6/src/kplot/.git/hooks/push-to-checkout.sample +0 -78
  27. kplot-1.0.6/src/kplot/.git/hooks/sendemail-validate.sample +0 -77
  28. kplot-1.0.6/src/kplot/.git/hooks/update.sample +0 -128
  29. kplot-1.0.6/src/kplot/.git/index +0 -0
  30. kplot-1.0.6/src/kplot/.git/info/exclude +0 -6
  31. kplot-1.0.6/src/kplot/.git/logs/HEAD +0 -1
  32. kplot-1.0.6/src/kplot/.git/logs/refs/heads/master +0 -1
  33. kplot-1.0.6/src/kplot/.git/logs/refs/remotes/origin/master +0 -1
  34. kplot-1.0.6/src/kplot/.git/objects/07/58a12cf558f1c1052d5d85fc11183eaa8c5a0e +0 -1
  35. kplot-1.0.6/src/kplot/.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
  36. kplot-1.0.6/src/kplot/.git/objects/4b/ec2657a7dbd45dbe73e37138d116f523faa959 +0 -0
  37. kplot-1.0.6/src/kplot/.git/objects/4f/5409d618f7e38d537b978d5f41a3631b32b711 +0 -0
  38. kplot-1.0.6/src/kplot/.git/objects/51/3c1f584825f2ef997c0b00889ad1e6bb8bc8b1 +0 -0
  39. kplot-1.0.6/src/kplot/.git/objects/53/95a83111291f805f440b8fed0dad2591e90c4b +0 -2
  40. kplot-1.0.6/src/kplot/.git/objects/66/594a4f54fe76d07b6cc028ea590f8cfa3d43f6 +0 -0
  41. kplot-1.0.6/src/kplot/.git/objects/73/77c78423a1b6d14a6d87a8748bf57f229ce10f +0 -0
  42. kplot-1.0.6/src/kplot/.git/objects/7e/836e7020b269a8d4f8fd1add726cf6c32ffa61 +0 -0
  43. kplot-1.0.6/src/kplot/.git/objects/b0/df8909fba782cf7f1bda3436475eca0f3246d4 +0 -0
  44. kplot-1.0.6/src/kplot/.git/objects/b2/98659e19d2219f0191793d2d22f41e58f70a32 +0 -0
  45. kplot-1.0.6/src/kplot/.git/objects/d5/8e4034057f2585ea9dd383bfca171cbe9d6842 +0 -0
  46. kplot-1.0.6/src/kplot/.git/objects/dd/527a8b9e974453467c66d8f54053eb0f220435 +0 -0
  47. kplot-1.0.6/src/kplot/.git/objects/dd/ab85719ab389187ded217518e30e0f1b6d6bf9 +0 -0
  48. kplot-1.0.6/src/kplot/.git/objects/e2/c2413a8beac9a9fb63279a8c01bcc0d63db27a +0 -0
  49. kplot-1.0.6/src/kplot/.git/objects/e4/6a79e332a3be7fe4514bab28f14acae9f4f3bd +0 -0
  50. kplot-1.0.6/src/kplot/.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  51. kplot-1.0.6/src/kplot/.git/objects/fe/004a1dce30bd84414e6ce55f7ff17dd098e37c +0 -0
  52. kplot-1.0.6/src/kplot/.git/refs/heads/master +0 -1
  53. kplot-1.0.6/src/kplot/.git/refs/remotes/origin/master +0 -1
  54. kplot-1.0.6/src/kplot/axes.py +0 -45
  55. kplot-1.0.6/src/kplot/cmaps.py +0 -10
  56. kplot-1.0.6/src/kplot/image.py +0 -214
  57. kplot-1.0.6/src/kplot/movie.py +0 -41
  58. kplot-1.0.6/src/kplot/py.typed +0 -0
  59. {kplot-1.0.6 → kplot-1.1.1}/README.md +0 -0
  60. {kplot-1.0.6 → kplot-1.1.1}/src/kplot/__init__.py +0 -0
  61. {kplot-1.0.6 → kplot-1.1.1}/src/kplot/styles/example.mpl +0 -0
  62. {kplot-1.0.6 → kplot-1.1.1}/src/kplot/styles/publication.mpl +0 -0
  63. {kplot-1.0.6 → kplot-1.1.1}/src/kplot/threeD/__init__.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: kplot
3
- Version: 1.0.6
4
- Summary: Add your description here
3
+ Version: 1.1.1
4
+ Summary: a matplotlib wrapper for keyan :-)
5
5
  Requires-Dist: matplotlib>=3.10.8
6
6
  Requires-Dist: numpy>=2.4.2
7
7
  Requires-Python: >=3.11
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "kplot"
3
- version = "1.0.6"
4
- description = "Add your description here"
3
+ version = "1.1.1"
4
+ description = "a matplotlib wrapper for keyan :-)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
7
7
  dependencies = [
@@ -0,0 +1,63 @@
1
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
2
+ # >-|===|> Imports <|===|-<
3
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
4
+ from kplot.utils import column_width, two_column_width
5
+
6
+ from matplotlib.pyplot import subplots as matplotlib_subplots
7
+ from matplotlib.pyplot import subplot_mosaic
8
+ from matplotlib.figure import Figure
9
+ from matplotlib.axes import Axes
10
+ import numpy as np
11
+
12
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
13
+ # >-|===|> Types <|===|-<
14
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
15
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
16
+ # >-|===|> Definitions <|===|-<
17
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
18
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
19
+ # >-|===|> Functions <|===|-<
20
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
21
+ def decode_subplots_args(*args, **kwargs):
22
+ if 'figsize' in kwargs: kwargs['figsize'] = tuple(
23
+ column_width if kwargs['figsize'][i]==1 else two_column_width if kwargs['figsize'][i]==2 else kwargs['figsize'][i]
24
+ for i in range(2))
25
+ match args:
26
+ case (int(), int())|((int(), int())):
27
+ return matplotlib_subplots(*args, **kwargs)
28
+ case (str(),):
29
+ return subplot_mosaic(*args, **kwargs)
30
+ case ():
31
+ return matplotlib_subplots(**kwargs)
32
+ def subplots(*args, **kwargs): return decode_subplots_args(*args, **kwargs)
33
+ def access_subplots(
34
+ fig: None|Figure = None,
35
+ axes: None|Axes|list = None,
36
+ figsize: None|tuple = None
37
+ ) -> tuple[Figure, list]:
38
+ match fig, axes:
39
+ # if given nothing
40
+ case None, None:
41
+ fig, axes = subplots(figsize=figsize)
42
+ axes = [axes]
43
+ case Figure(), None:
44
+ axes = fig.get_axes()
45
+ axes = axes if len(axes) > 0 else [fig.add_subplot(111)]
46
+ case None, Axes():
47
+ fig = axes.get_figure()
48
+ axes = [axes]
49
+ case None, list()|np.ndarray():
50
+ axes = axes.flatten()
51
+ fig = axes[0].get_figure()
52
+ case Figure(), Axes():
53
+ axes = [axes]
54
+ case Figure(), list()|np.ndarray():
55
+ return fig, axes
56
+ return fig, axes
57
+
58
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
59
+ # >-|===|> Decorators <|===|-<
60
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
61
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
62
+ # >-|===|> Classes <|===|-<
63
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
@@ -0,0 +1,98 @@
1
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
2
+ # >-|===|> Imports <|===|-<
3
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
4
+ from kbasic.array import tile
5
+ from functools import wraps
6
+ from glob import glob
7
+ import matplotlib.pyplot as plt
8
+ from matplotlib.pyplot import cm
9
+ from matplotlib.colors import Colormap, LinearSegmentedColormap, ListedColormap, hex2color, Normalize, LogNorm, FuncNorm, AsinhNorm, PowerNorm, SymLogNorm, BoundaryNorm, CenteredNorm, TwoSlopeNorm
10
+ from mpl_toolkits.axes_grid1 import make_axes_locatable
11
+ import numpy as np
12
+
13
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
14
+ # >-|===|> Types <|===|-<
15
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
16
+ class Norm:
17
+ types: list = [Normalize, LogNorm, FuncNorm, AsinhNorm, PowerNorm, SymLogNorm, BoundaryNorm, CenteredNorm, TwoSlopeNorm]
18
+ class Cmap:
19
+ types: list = [Colormap, ListedColormap, LinearSegmentedColormap]
20
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
21
+ # >-|===|> Definitions <|===|-<
22
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
23
+ pink = "#E34F68"
24
+ lightpink = "#E39FAA"
25
+ blue = "#7350E6"
26
+ lightblue = "#AE9FE3"
27
+ shadow = "#B8B7B8"
28
+ manoaskies = LinearSegmentedColormap.from_list("manoaskies", [pink, blue])
29
+ manoaskies_centered = LinearSegmentedColormap.from_list("manoaskies_centered", [lightpink, pink, "#000000", blue, lightblue])
30
+ manoaskies_background_blue = "#0C0524"
31
+ pink2grey = LinearSegmentedColormap.from_list("p2g", [pink, shadow])
32
+ grey2black = LinearSegmentedColormap.from_list("g2b", [shadow, "#000000"])
33
+ colors_list = np.zeros((256, 4))
34
+ colors_list[:128] = list(hex2color(manoaskies_background_blue))+[1]
35
+ for i in range(28): colors_list[128+i] = pink2grey(i/28)
36
+ for i in range(100): colors_list[156+i] = grey2black(i/150)
37
+ manoaskies_beauty = ListedColormap(colors_list)
38
+ default_cmap = cm.plasma
39
+
40
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
41
+ # >-|===|> Functions <|===|-<
42
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
43
+ # # Ploting utils
44
+ def auto_norm(
45
+ norm: str,
46
+ frames: np.ndarray,
47
+ linear_threshold: float|None = None,
48
+ center: float|None = None,
49
+ saturate: float|None = None
50
+ ) -> Norm:
51
+ """A function to create a matplotlib normalization given a set images.
52
+
53
+ Args:
54
+ norm (str): what type of scale to use, e.g. lognorm or centerednorm
55
+ frames (np.ndarray): the images to base the normalization on
56
+ linear_threshold (float | None, optional): for symlognorm. Defaults to None.
57
+ center (float | None, optional): for centered normalizations. Defaults to None.
58
+ saturate (float | None, optional): the level at which to saturate the norm, e.g. if saturate=0.01 then the
59
+ max is the 99th percentile. Defaults to None.
60
+
61
+ Returns:
62
+ matplotlib normalization
63
+ """
64
+ frames = frames[(-np.inf < frames)&(frames < np.inf)]
65
+ # set min/max IF saturate is None or IF saturate is a tuple ELSE assume its a float
66
+ low = np.nanmin(frames) if saturate is None else np.nanquantile(frames, 1-saturate[0]) if isinstance(saturate, tuple) else np.nanquantile(frames, 1-saturate)
67
+ high = np.nanmax(frames) if saturate is None else np.nanquantile(frames, 0+saturate[1]) if isinstance(saturate, tuple) else np.nanquantile(frames, 0+saturate)
68
+ match norm.lower():
69
+ case "lognorm"|"log":
70
+ if low < 0: raise ValueError(f"minimum is {low}, LogNorm only takes positive values")
71
+ if low==0: low=np.nanmin(frames[frames!=0])
72
+ return LogNorm(vmin=low, vmax=high)
73
+ case "symlognorm"|"symlog"|"sym":
74
+ sig = np.nanstd(frames)
75
+ mu = np.nanmean(frames)
76
+ if np.abs(mu)-sig > 0: raise TypeError("SymLogNorm is only designed for stuff close to zero!")
77
+ return SymLogNorm(sig if linear_threshold is None else linear_threshold, vmin=low, vmax=high)
78
+ case n if n in ["centerednorm", "twoslope", "twoslopenorm"]:
79
+ sig = np.nanstd(frames)
80
+ mu = np.nanmean(frames)
81
+ # for the center use center if give otherwise use 0 if mean is small, else use mean
82
+ vcenter = center if not center is None else 0 if np.abs(mu)-sig > 0 else mu
83
+ return TwoSlopeNorm(vmin=low, vcenter=vcenter, vmax=high)
84
+ case _: return Normalize(vmin=low, vmax=high)
85
+ def align_algorithm(x: list|np.ndarray, mode: str):
86
+ match mode:
87
+ case 'mid'|'m'|'center'|'c':
88
+ return [(x[i] + x[i+1])/2 for i in range(len(x)-1)]
89
+ case 'logmid'|'lm':
90
+ return [np.log10((10**x[i] + 10**x[i+1])/ 2) for i in range(len(x)-1)]
91
+ case 'left'|'l':
92
+ return x[:-1]
93
+ case 'right'|'r':
94
+ return x[1:]
95
+
96
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
97
+ # >-|===|> Decorators <|===|-<
98
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
@@ -5,11 +5,9 @@
5
5
  from kplot.utils import alias_kwarg, column_width
6
6
  from kplot.axes import access_subplots
7
7
  from kplot.utils import alias_kwarg, column_width, parse_multiax_params
8
-
8
+ from kplot.cmaps import Cmap
9
9
  import numpy as np
10
10
  import matplotlib.pyplot as plt
11
- from matplotlib.colors import ListedColormap, LinearSegmentedColormap, Colormap
12
- CmapTypes = [ListedColormap, LinearSegmentedColormap, Colormap]
13
11
 
14
12
  # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
15
13
  # >-|===|> Definitions <|===|-<
@@ -21,8 +19,6 @@ CmapTypes = [ListedColormap, LinearSegmentedColormap, Colormap]
21
19
  def decode_hist_src(src) -> tuple:
22
20
  match src:
23
21
  case np.ndarray()|list(): return src
24
-
25
-
26
22
  def hist(
27
23
  *src,
28
24
  #figure setup
@@ -32,8 +28,8 @@ def hist(
32
28
  show: bool = False,
33
29
  close: bool = False,
34
30
  #line formating
35
- color: str|Colormap|list[int] = "black",
36
- cmap: str|Colormap = plt.cm.plasma,
31
+ color: str|Cmap|list[int] = "black",
32
+ cmap: str|Cmap = plt.cm.plasma,
37
33
  linewidth: int = None, lw: int = None,
38
34
  linestyle: str = None, ls: str = None,
39
35
  #plot formating
@@ -0,0 +1,377 @@
1
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
2
+ # >-|===|> Imports <|===|-<
3
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
4
+ from kplot.axes import access_subplots
5
+ from kplot.utils import alias_kwarg, parse_multiax_params, column_width, two_column_width
6
+ from kplot.cmaps import Norm, Cmap
7
+
8
+ from kbasic.array import tile
9
+ import numpy as np
10
+ import matplotlib.pyplot as plt
11
+ from mpl_toolkits.axes_grid1 import make_axes_locatable
12
+ from matplotlib.colors import ListedColormap, LinearSegmentedColormap, Colormap
13
+
14
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
15
+ # >-|===|> Types <|===|-<
16
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
17
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
18
+ # >-|===|> Definitions <|===|-<
19
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
20
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
21
+ # >-|===|> Functions <|===|-<
22
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
23
+ def decode_image_src(src) -> np.ndarray:
24
+ match src:
25
+ case str():
26
+ return np.loadtxt(src)
27
+ case np.ndarray():
28
+ return src
29
+ case list():
30
+ return np.array(src)
31
+ def _show_one_frame(plot_dict, axes_dict, cbar_dict, **kwargs):
32
+ # populate name space
33
+ x, y, image = plot_dict['x'], plot_dict['y'], plot_dict['image']
34
+ fig, ax, cmap, norm = plot_dict['fig'], plot_dict['axes'][0], plot_dict['cmap'], plot_dict['norm']
35
+ # plot
36
+ img = ax.pcolormesh(x, y, image, cmap=cmap, norm=norm, **kwargs)
37
+ # label axes
38
+ ax.set_xlabel(axes_dict['xlabel'])
39
+ ax.set_ylabel(axes_dict['ylabel'])
40
+ # set limits
41
+ ax.set_xlim(axes_dict['xlim'])
42
+ ax.set_ylim(axes_dict['ylim'])
43
+ # set title
44
+ ax.set_title(axes_dict['title'])
45
+ # set aspect
46
+ ax.set_aspect(axes_dict['aspect'])
47
+ if cbar_dict['colorbar']:
48
+ divider = make_axes_locatable(ax)
49
+ cax = divider.append_axes(
50
+ cbar_dict['location'],
51
+ size=cbar_dict['size'],
52
+ pad=cbar_dict['pad']
53
+ )
54
+ fig.colorbar(
55
+ img,
56
+ cax=cax, ax=ax,
57
+ ticks=cbar_dict['cticks'],
58
+ label=cbar_dict['units']
59
+ )
60
+ return [img]
61
+ def construct_mass_norm(images: np.ndarray, norm):
62
+ match norm:
63
+ # linear norms
64
+ case Normalize():
65
+ vmin = np.nanmin(images) if norm.vmin is None else norm.vmin
66
+ vmax = np.nanmax(images) if norm.vmax is None else norm.vmax
67
+ return Normalize(vmin=vmin, vmax=vmax, clip=norm.clip)
68
+
69
+ case CenteredNorm():
70
+ vmin = np.nanmin(images) if norm.vmin is None else norm.vmin
71
+ vmax = np.nanmax(images) if norm.vmax is None else norm.vmax
72
+ # only center at 0 if the mean is close to 0 to avoid weird balance
73
+ vcen = norm.vcenter if norm.vcenter else 0 if np.nanmean(images) - np.nanstd(images)/2 else np.nanmean(images)
74
+ hrng = norm.halfrange if norm.halfrange else vmax/2 if vcen==0 else abs(vmax-vmin)/2
75
+ return CenteredNorm(vcenter=vcen, halfrange=hrng, clip=norm.clip)
76
+
77
+ case TwoSlopeNorm():
78
+ vmin = np.nanmin(images) if norm.vmin is None else norm.vmin
79
+ vmax = np.nanmax(images) if norm.vmax is None else norm.vmax
80
+ vcen = norm.vcenter if norm.vcenter else 0 if np.nanmean(images) - np.nanstd(images)/2 else np.nanmean(images)
81
+ return TwoSlopeNorm(vcenter=vcen, vmin=vmin, vmax=vmax)
82
+
83
+ case BoundaryNorm():
84
+ return BoundaryNorm(norm.boundaries, norm.ncolors, clip=norm.clip, extend=norm.extend)
85
+
86
+ # log-like norms
87
+ case LogNorm():
88
+ vmin = np.nanmin(images) if norm.vmin is None else norm.vmin
89
+ vmax = np.nanmax(images) if norm.vmax is None else norm.vmax
90
+ return LogNorm(vmin=vmin, vmax=vmax, clip=norm.clip)
91
+
92
+ case SymLogNorm():
93
+ vmin = np.nanmin(images) if norm.vmin is None else norm.vmin
94
+ vmax = np.nanmax(images) if norm.vmax is None else norm.vmax
95
+ #check that this isn't trivially the same as LogNorm
96
+ if vmin > 0: return LogNorm(vmin=vmin, vmax=vmax, clip=norm.clip)
97
+ linthresh = np.nanquantile(images, .1) if norm.linthresh is None else norm.linthresh
98
+ return SymLogNorm(linthresh, linscale=norm.linscale, vmin=vmin, vmax=vmax, clip=norm.clip)
99
+
100
+ case AsinhNorm():
101
+ vmin = np.nanmin(images) if norm.vmin is None else norm.vmin
102
+ vmax = np.nanmax(images) if norm.vmax is None else norm.vmax
103
+ linwidth = np.nanquantile(images, .1) if norm.linear_width is None else norm.linear_width
104
+ return AsinhNorm(linear_width=linwidth, vmin=vmin, vmax=vmax, clip=norm.clip)
105
+
106
+ case PowerNorm():
107
+ vmin = np.nanmin(images) if norm.vmin is None else norm.vmin
108
+ vmax = np.nanmax(images) if norm.vmax is None else norm.vmax
109
+ return PowerNorm(norm.gamma, vmin=vmin, vmax=vmax, clip=norm.clip)
110
+
111
+ case FuncNorm():
112
+ vmin = np.nanmin(images) if norm.vmin is None else norm.vmin
113
+ vmax = np.nanmax(images) if norm.vmax is None else norm.vmax
114
+ return FuncNorm(norm.functions, vmin=vmin, vmax=vmax, clip=norm.clip)
115
+
116
+ pass
117
+ def _show_multiple_frames(plot_dict: dict, axes_dict: dict, cbar_dict: dict, N: int, **kwargs):
118
+ fig, axes, images = plot_dict['fig'], plot_dict['axes'], plot_dict['image'] # these are set as single and multiple before arriving to this function
119
+ xs = parse_multiax_params(plot_dict['x'], [np.ndarray], N)
120
+ ys = parse_multiax_params(plot_dict['y'], [np.ndarray], N)
121
+ cmaps = parse_multiax_params(plot_dict['cmap'], Cmap.types, N)
122
+ colorbars = 'single' if cbar_dict['colorbar']=='single' else parse_multiax_params(cbar_dict['colorbar'], [bool], N)
123
+ norms = 'single' if colorbars=='single' else parse_multiax_params(plot_dict['norm'], Norm.types, N)
124
+ cbar_locations = parse_multiax_params(cbar_dict['location'], [str], N)
125
+ cbar_sizes = parse_multiax_params(cbar_dict['size'], [str], N)
126
+ cbar_pads = parse_multiax_params(cbar_dict['pad'], [float], N)
127
+ cticks = parse_multiax_params(cbar_dict['cticks'], [list], N)
128
+ units = parse_multiax_params(cbar_dict['units'], [str], N)
129
+ xlims = parse_multiax_params(axes_dict['xlim'], [tuple], N)
130
+ ylims = parse_multiax_params(axes_dict['ylim'], [tuple], N)
131
+ xlabels = 'single' if type(axes_dict['xlabel'])==str else parse_multiax_params(axes_dict['xlabel'], [str], N)
132
+ ylabels = 'single' if type(axes_dict['ylabel'])==str else parse_multiax_params(axes_dict['ylabel'], [str], N)
133
+ titles = 'single' if type(axes_dict['title'])==str else parse_multiax_params(axes_dict['title'], [str], N)
134
+ aspects = parse_multiax_params(axes_dict['aspect'], [str], N)
135
+ imgs = []
136
+ for i in range(N):
137
+ ax, image = axes[i], images[i]
138
+ imgs.append(ax.pcolormesh(xs[i], ys[i], image, cmap=cmaps[i], norm=norms[i], **kwargs))
139
+ return imgs
140
+ def show(
141
+ field: np.ndarray,
142
+ tile_image: bool = False,
143
+ #x/y axes
144
+ x: np.ndarray|None = None,
145
+ y: np.ndarray|None = None,
146
+ #figure setup
147
+ fig = None,
148
+ ax = None,
149
+ axis: bool = True,
150
+ figsize: tuple[float, float] = (10, 10),
151
+ show: bool = False,
152
+ #plot parameters
153
+ cmap = plt.cm.plasma,
154
+ colorbar: bool = True,
155
+ colorbar_style: dict = {'location':'right', "size":"7%", "pad":0.05},
156
+ cticks: list|None = None,
157
+ units: str|None = None,
158
+ #contour options
159
+ contour: np.ndarray|None = None,
160
+ contour_style: dict = {'levels': 10, 'colors': 'black'},
161
+ #plot formating
162
+ xlim: tuple = (None, None),
163
+ ylim: tuple = (None, None),
164
+ title: str|None = None,
165
+ #presentation parameters
166
+ save: str = "",
167
+ dpi: int = 100,
168
+ #everything else goes into pcolormesh
169
+ **kwargs
170
+ ):
171
+ """a convinient way to plt.imshow and arrange it nicely
172
+
173
+ Args:
174
+ field (np.ndarray): The image to be plotted
175
+ tile_image (bool, optional): whether to tile the image. Defaults to False.
176
+ x (np.ndarray | None, optional): x coordinates of pixels. Defaults to None.
177
+ y (np.ndarray | None, optional): y coordinates of pixels. Defaults to None.
178
+ fig (plt.Figure, optional): the figure on which to plot. Defaults to None.
179
+ ax (plt.Axes, optional): the ax on which to plot this image. Defaults to None.
180
+ axis (bool, optional): whether to include the whole axis, if False then only the plot area will be shown.
181
+ figsize (tuple[float, float], optional): unless fig and ax are given plot on a figure of this size. Defaults to (10, 10).
182
+ show (bool, optional): whether to use the plt.show() command at the end. Defaults to True.
183
+ cmap (plt.ColorMap, optional): the colormap to use for the image. Defaults to plasma.
184
+ colorbar (bool, optional): whether to add a colorbar. Defaults to True.
185
+ colorbar_style (dict, optional): dictionary containing kwargs for the colorbar command. Defaults to {'location':'right', "size":"7%", "pad":0.05}.
186
+ cticks (list | None, optional): the ticks to use on the colorbar. Defaults to None.
187
+ units (str | None, optional): label for the colorbar. Defaults to None.
188
+ contour (np.ndarray | None, optional): whether to put a contour plot on top. Defaults to None.
189
+ contour_style (dict, optional): kwargs for contour plotting command. Defaults to {'levels': 10, 'colors': 'black'}.
190
+ xlim (tuple, optional): the limits on the x axis. Defaults to (None, None).
191
+ ylim (tuple, optional): the limits on the y axis. Defaults to (None, None).
192
+ title (str | None, optional): the plot title. Defaults to None.
193
+ save (str, optional): if given save the plot to this path. Defaults to "".
194
+ dpi (int, optional): dots per inch to save at. Defaults to 100.
195
+
196
+ Returns:
197
+ fig (plt.Figure): The figure on which the plot was put
198
+ ax (plt.Axes): The ax on which the plot was put
199
+ img (plt.Artist): the plot artist
200
+ """
201
+ # prep data
202
+ assert (ndims:=len(field.shape))==2, f"show was given an image with {ndims} dimensions, please provide a 2d array"
203
+ image = tile(field) if tile_image else field
204
+ # check axes
205
+ if x is None: x, y = np.mgrid[:image.shape[0], :image.shape[1]]
206
+ if tile_image: x, y = np.r_[x, x+x[-1], x+2*x[-1]], np.r_[y, y+y[-1], y+2*y[-1]]
207
+ else: assert (len(x), len(y)) == image.shape, f"Given x of shape {len(x)} and y of shape {len(y)} but image is of shape {image.shape}"
208
+ # prep figure
209
+ if ax is None: (fig, ax) = plt.subplots(figsize=figsize)
210
+ fig = ax.get_figure()
211
+ if not axis:
212
+ ax.axis('off')
213
+ ax.set_position([0, 0, 1, 1])
214
+ colorbar = False
215
+ # plot data
216
+ img = ax.pcolormesh(x, y, image, cmap=cmap, **kwargs)
217
+ # colorbar
218
+ if colorbar:
219
+ divider = make_axes_locatable(ax)
220
+ colorbar_location = colorbar_style.pop("location") if "location" in colorbar_style.keys() else "right"
221
+ cax = divider.append_axes(colorbar_location, **colorbar_style)
222
+ fig.colorbar(img, cax=cax, ax=ax, ticks=cticks, label=units, orientation = 'vertical' if colorbar_location in ('right', 'left') else 'horizontal')
223
+ if colorbar_location=='top':
224
+ cax.xaxis.set_ticks_position('top')
225
+ cax.xaxis.set_label_position('top')
226
+ # contour
227
+ if not contour is None: ax.contour(contour, **contour_style)
228
+ # set limits
229
+ ax.set_xlim(xlim)
230
+ ax.set_ylim(ylim)
231
+ # set title
232
+ ax.set_title(title)
233
+ # set aspect
234
+ ax.set_aspect('equal')
235
+ # present the figure
236
+ if len(save)>0:
237
+ if not '.' in save: save +=".jpeg"
238
+ plt.savefig(save, dpi=dpi, bbox_inches='tight')
239
+ if show: plt.show()
240
+ return fig, ax, img
241
+ def contour(
242
+ Z: np.ndarray,
243
+ #x/y axes
244
+ x: np.ndarray|None = None,
245
+ y: np.ndarray|None = None,
246
+ #figure setup
247
+ fig = None,
248
+ ax = None,
249
+ axis: bool = True,
250
+ figsize: tuple[float, float] = (10, 10),
251
+ show: bool = False,
252
+ #plot parameters
253
+ levels = 10,
254
+ color = 'black',
255
+ colors = None,
256
+ cmap = None,
257
+ negative_linestyles = '-',
258
+ linestyles = '-',
259
+ #plot formating
260
+ xlim: tuple = (None, None),
261
+ ylim: tuple = (None, None),
262
+ title: str|None = None,
263
+ #presentation parameters
264
+ save: str = "",
265
+ dpi: int = 100,
266
+ #throw the rest in a dict
267
+ **kwds
268
+ ):
269
+ # prep data
270
+ assert (ndims:=len(Z.shape))==2, f"show was given an image with {ndims} dimensions, please provide a 2d array"
271
+ # check axes
272
+ if x is None: x, y = np.mgrid[:Z.shape[0], :Z.shape[1]]
273
+ else: assert (len(x), len(y)) == Z.shape, f"Given x of shape {len(x)} and y of shape {len(y)} but image is of shape {Z.shape}"
274
+ # prep figure
275
+ if ax is None: (fig, ax) = plt.subplots(figsize=figsize)
276
+ fig = ax.get_figure()
277
+ if not axis:
278
+ ax.axis('off')
279
+ ax.set_position([0, 0, 1, 1])
280
+ colorbar = False
281
+ # plot data
282
+ contour_dict = {'levels':levels,"linestyles":linestyles,'negative_linestyles':negative_linestyles}
283
+ if cmap: #plot with a cmap
284
+ contour_dict['cmap'] = cmap
285
+ else: #plot with a color
286
+ contour_dict['colors'] = color if not colors else colors
287
+ img = ax.contour(
288
+ x, y, Z,
289
+ **contour_dict,
290
+ **kwds
291
+ )
292
+ # set limits
293
+ ax.set_xlim(xlim)
294
+ ax.set_ylim(ylim)
295
+ # set title
296
+ ax.set_title(title)
297
+ # set aspect
298
+ ax.set_aspect('equal')
299
+ # present the figure
300
+ if len(save)>0:
301
+ if not '.' in save: save +=".jpeg"
302
+ plt.savefig(save, dpi=dpi)
303
+ if show: plt.show()
304
+ return fig, ax, img
305
+ def contourf(
306
+ Z: np.ndarray,
307
+ #x/y axes
308
+ x: np.ndarray|None = None,
309
+ y: np.ndarray|None = None,
310
+ #figure setup
311
+ fig = None,
312
+ ax = None,
313
+ axis: bool = True,
314
+ figsize: tuple[float, float] = (10, 10),
315
+ show: bool = False,
316
+ #plot parameters
317
+ levels = 10,
318
+ cmap = plt.cm.plasma,
319
+ colorbar: bool = True,
320
+ colorbar_style: dict = {'location':'right', "size":"7%", "pad":0.05},
321
+ cticks: list|None = None,
322
+ units: str|None = None,
323
+ #plot formating
324
+ xlim: tuple = (None, None),
325
+ ylim: tuple = (None, None),
326
+ title: str|None = None,
327
+ #presentation parameters
328
+ save: str = "",
329
+ dpi: int = 100,
330
+ #throw the rest in a dict
331
+ **kwds
332
+ ):
333
+ # prep data
334
+ assert (ndims:=len(Z.shape))==2, f"show was given an image with {ndims} dimensions, please provide a 2d array"
335
+ # check axes
336
+ if x is None: x, y = np.mgrid[:Z.shape[0], :Z.shape[1]]
337
+ else: assert (len(x), len(y)) == Z.shape, f"Given x of shape {len(x)} and y of shape {len(y)} but image is of shape {Z.shape}"
338
+ # prep figure
339
+ if ax is None: (fig, ax) = plt.subplots(figsize=figsize)
340
+ fig = ax.get_figure()
341
+ if not axis:
342
+ ax.axis('off')
343
+ ax.set_position([0, 0, 1, 1])
344
+ colorbar = False
345
+ # plot data
346
+ contour_dict = {'levels':levels,"cmap":cmap}
347
+ img = ax.contourf(
348
+ x, y, Z,
349
+ **contour_dict,
350
+ **kwds
351
+ )
352
+ # colorbar
353
+ if colorbar:
354
+ divider = make_axes_locatable(ax)
355
+ colorbar_location = colorbar_style.pop("location") if "location" in colorbar_style.keys() else "right"
356
+ cax = divider.append_axes(colorbar_location, **colorbar_style)
357
+ fig.colorbar(img, cax=cax, ax=ax, ticks=cticks, label=units)
358
+ # set limits
359
+ ax.set_xlim(xlim)
360
+ ax.set_ylim(ylim)
361
+ # set title
362
+ ax.set_title(title)
363
+ # set aspect
364
+ ax.set_aspect('equal')
365
+ # present the figure
366
+ if len(save)>0:
367
+ if not '.' in save: save +=".jpeg"
368
+ plt.savefig(save, dpi=dpi)
369
+ if show: plt.show()
370
+ return fig, ax, img
371
+
372
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
373
+ # >-|===|> Decorators <|===|-<
374
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
375
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
376
+ # >-|===|> Classes <|===|-<
377
+ # !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==