From 80986d8be02afeec2891fece248476523e53dd08 Mon Sep 17 00:00:00 2001 From: aletgn Date: Sat, 25 May 2024 17:18:03 +0200 Subject: [PATCH] bump version 0.0.0rc5 modified: src/pytom3d/scan.py (minor changes) modified: src/pytom3d/util.py (add printer decorator) modified: src/pytom3d/viewer.py (add contour view, scan comparison) --- buildPackageAndDocs.sh | 2 +- dist/pytom3d-0.0.0rc5-py3-none-any.whl | Bin 0 -> 30369 bytes dist/pytom3d-0.0.0rc5.tar.gz | Bin 0 -> 29114 bytes docs/conf.py | 2 +- setup.py | 2 +- src/pytom3d/scan.py | 14 +- src/pytom3d/util.py | 35 ++++ src/pytom3d/viewer.py | 278 ++++++++++++++++++++++--- 8 files changed, 299 insertions(+), 34 deletions(-) create mode 100644 dist/pytom3d-0.0.0rc5-py3-none-any.whl create mode 100644 dist/pytom3d-0.0.0rc5.tar.gz diff --git a/buildPackageAndDocs.sh b/buildPackageAndDocs.sh index eebec98..7b8ddca 100644 --- a/buildPackageAndDocs.sh +++ b/buildPackageAndDocs.sh @@ -1,6 +1,6 @@ pip3 uninstall pytom3d -y python3 -m build -pip3 install dist/pytom3d-0.0.0rc4-py3-none-any.whl +pip3 install dist/pytom3d-0.0.0rc5-py3-none-any.whl sphinx-apidoc -o ./docs/ ./src/ cd docs/ make clean & make html diff --git a/dist/pytom3d-0.0.0rc5-py3-none-any.whl b/dist/pytom3d-0.0.0rc5-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..53a9290834cf64e1524881d4339555c7eecb9c5d GIT binary patch literal 30369 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 BQK|p{ literal 0 HcmV?d00001 diff --git a/dist/pytom3d-0.0.0rc5.tar.gz b/dist/pytom3d-0.0.0rc5.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..3be4dd09344b2461f14f321094dc230b23f9d711 GIT binary patch literal 29114 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 z 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 J96tb90sx=Rutfj> literal 0 HcmV?d00001 diff --git a/docs/conf.py b/docs/conf.py index 5c73bd6..ab40657 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,7 +9,7 @@ project = 'PyToM-3D' copyright = '2024, Alessandro Tognan' author = 'Alessandro Tognan' -release = '0.0.0rc4' +release = '0.0.0rc5' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/setup.py b/setup.py index b69f3fb..cd10e63 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="pytom3d", - version="0.0.0rc4", + version="0.0.0rc5", description="PyToM-3D: Python Topography Manipulator in 3D", long_description=long_description, long_description_content_type="text/markdown", diff --git a/src/pytom3d/scan.py b/src/pytom3d/scan.py index 6fdc1c2..443fab0 100644 --- a/src/pytom3d/scan.py +++ b/src/pytom3d/scan.py @@ -98,14 +98,20 @@ def export_line_scan(reader: callable, path: str, *scans: List, **kwargs: Dict[ data.to_excel(path, index=False) -def scan_stat_factory(*scan: List): +def scan_stat_factory(name: str = "av", color: str = "k", linestyle: str = "-", *scan: List): """ Create a scan object representing the statistical summary of multiple scans. Parameters ---------- - *scan : variable number of Scan objects - Scan objects to be summarized. + name: str + Name to be assigned to the output scan + color: str + Color to be assigned to the output scan + linestyle: + Linestyle to be assigned to the output scan + scan : variable number of Scan objects + Scan objects to be processed. Returns ------- @@ -120,7 +126,7 @@ def scan_stat_factory(*scan: List): mean = values.mean(axis=0) quad = np.sqrt(squared_uncertainty.sum(axis=0))/len(scan) - av_scan = Scan(name="av") + av_scan = Scan(name=name, line=linestyle, color=color) av_scan.load_data(x, mean, quad) return av_scan \ No newline at end of file diff --git a/src/pytom3d/util.py b/src/pytom3d/util.py index 090cfb7..db7c640 100644 --- a/src/pytom3d/util.py +++ b/src/pytom3d/util.py @@ -6,6 +6,7 @@ import pandas as pd import re from typing import Tuple, List, Any +from matplotlib import pyplot as plt from pytom3d.stats import running_mean, running_std @@ -503,3 +504,37 @@ def scan_data_wrapper(path: str, match: str) -> Tuple[np.ndarray]: x = get_coordinates([0], *data) return x.reshape(-1), mean, std + + +def printer(func: callable): + """ + A decorator for class methods that saves a figure if 'save' is True. + + Borrowed from https://github.com/aletgn/b-fade/blob/master/src/bfade/util.py + + This decorator wraps a method that generates a figure and a title, + and it saves the figure to the specified location if 'save' is True. + + Parameters + ---------- + func : callable + The function to be decorated, which generates a figure and a title. + + Returns + ------- + callable + The decorated function. + """ + @functools.wraps(func) # <- preserve function signature + def saver(self, *args, **kwargs): + fig, title = func(self, *args, **kwargs) + if self.save == True: + fig.savefig(self.folder + title + "." + self.fmt, + format = self.fmt, + dpi = self.dpi, + bbox_inches='tight') + print(f"SAVE: {title}") + else: + print(f"SHOW: {title}") + plt.show() + return saver diff --git a/src/pytom3d/viewer.py b/src/pytom3d/viewer.py index 094be55..4317a0d 100644 --- a/src/pytom3d/viewer.py +++ b/src/pytom3d/viewer.py @@ -1,4 +1,5 @@ from pytom3d.core import Topography +from pytom3d.util import printer # from util import summation, distance, distance2 from matplotlib import pyplot as plt from typing import List, Tuple @@ -41,7 +42,88 @@ def __init__(self, name: str = "unnamed") -> None: self.x_lim = None self.y_lim = None self.z_lim = None - + self.config() + self.config_3d() + + def config(self, save: bool = False, folder: str = "./", fmt: str = "png", dpi: int = 300) -> None: + """ + Configure settings for saving plots. + + Borrowed from https://github.com/aletgn/b-fade/ + + Parameters + ---------- + save : bool, optional + Flag indicating whether to save plots. The default is False. + folder : str, optional + Folder path where plots will be saved. The default is "./". + fmt : str, optional + Format for saving plots. The default is "png". + dpi : int, optional + Dots per inch for saving plots. The default is 300. + + Returns + ------- + None + + """ + self.save = save + self.folder = folder + self.fmt = fmt + self.dpi = dpi + + def config_3d(self, point_size: float = 0.3, cmap: str = "RdYlBu_r", + xlabel: str = r'$x_g$ [mm]', + ylabel: str = r'$y_g$ [mm]', + zlabel: str = r'$u(x_g, y_g)$ [mm]', + x_lim: List = [-110, 110], + y_lim: List = [-38, 38], + z_lim: List = None, + zticks: int = 10, + zoom: float = 1) -> None: + """ + Configure the 3D plot parameters. + + Parameters + ---------- + point_size : float, optional + Size of the points in the plot (default is 0.3). + cmap : str, optional + Colormap for the plot (default is "RdYlBu_r"). + xlabel : str, optional + Label for the x-axis (default is r'$x_g$ [mm]'). + ylabel : str, optional + Label for the y-axis (default is r'$y_g$ [mm]'). + zlabel : str, optional + Label for the z-axis (default is r'$u(x_g, y_g)$ [mm]'). + x_lim : list of int, optional + Limits for the x-axis (default is [-110, 110]). + y_lim : list of int, optional + Limits for the y-axis (default is [-38, 38]). + z_lim : list of int or None, optional + Limits for the z-axis (default is None). + zticks : int, optional + Number of ticks on the z-axis (default is 10). + zoom : float, optional + Zoom level for the plot (default is 1). + + Returns + ------- + None + + """ + self.point_size = point_size + self.cmap = cmap + self.xlabel = xlabel + self.ylabel = ylabel + self.zlabel = zlabel + self.x_lim = x_lim + self.y_lim = y_lim + self.z_lim = z_lim + self.zticks = zticks + self.zoom = zoom + + def set_limits(self, x: List[float] = None, y: List[float] = None, z: List[float] = None) -> None: """ Set the limits for the x, y, and z axes. @@ -103,6 +185,7 @@ def views2D(self, *data: List[Topography]) -> None: plt.gcf().tight_layout(pad=1) plt.show() + @printer def scatter3D(self, *data: List[Topography]) -> None: """ Generate a 3D scatter plot for the given Topography data. @@ -139,9 +222,9 @@ def scatter3D(self, *data: List[Topography]) -> None: vmax = np.array([h.M[2] for h in data]).max() ax.set_zlim([vmin, vmax]) - ax.set_xlabel("x [mm]") - ax.set_ylabel("y [mm]") - ax.set_zlabel("z [mm]") + ax.set_xlabel(self.xlabel) + ax.set_ylabel(self.ylabel) + ax.set_zlabel(self.zlabel) ax.xaxis.pane.set_color('w') ax.yaxis.pane.set_edgecolor('w') @@ -151,24 +234,28 @@ def scatter3D(self, *data: List[Topography]) -> None: ax.zaxis.pane.set_color('w') plt.gca().xaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.0f}")) - plt.gca().yaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.1f}")) + plt.gca().yaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.0f}")) plt.gca().zaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.2f}")) - ax.grid(True) + ax.xaxis._axinfo['grid'].update(color = 'grey', linestyle = ':', linewidth = 0.5) + ax.yaxis._axinfo['grid'].update(color = 'grey', linestyle = ':', linewidth = 0.5) + ax.zaxis._axinfo['grid'].update(color = 'grey', linestyle = ':', linewidth = 0.5) for d in data: - sc = ax.scatter3D(d.P[:, 0], d.P[:, 1], d.P[:, 2], s=0.5, alpha=1, - vmin=vmin, vmax=vmax, c=d.P[:, 2]) - - cbar = fig.colorbar(sc, ax=ax, orientation="vertical", - pad=0.12, format="%.2f", - ticks=list(np.linspace(vmin, - vmax, 11)), - label='Altitude') - cbar.ax.tick_params(direction='in', right=1, left=1, size=2.5) + sc = ax.scatter(d.P[:, 0], d.P[:, 1], d.P[:, 2], s=self.point_size, alpha=1, + vmin=vmin, vmax=vmax, c=d.P[:, 2], marker="o", + cmap=self.cmap) - ax.axis('tight') - plt.show() + # cbar = fig.colorbar(sc, ax=ax, orientation="vertical", + # pad=0.12, format="%.2f", + # ticks=list(np.linspace(vmin, + # vmax, 11)), + # label=self.zlabel) + # cbar.ax.tick_params(direction='in', right=1, left=1, size=2.5) + + # ax.axis('tight') + ax.set_box_aspect(None, zoom=self.zoom) + return fig, self.name def scatter3DRegression(self, regression: Topography, reference: Topography = None) -> None: fig = plt.figure(dpi=300) @@ -227,10 +314,47 @@ def __init__(self, **kwargs) -> None: except KeyError: self.name = "Untitled" + self.config() + self.config_scan_view() self.config_canvas() + def config(self, save: bool = False, folder: str = "./", fmt: str = "png", dpi: int = 300) -> None: + """ + Configure settings for saving plots. + + Borrowed from https://github.com/aletgn/b-fade/ + + Parameters + ---------- + save : bool, optional + Flag indicating whether to save plots. The default is False. + folder : str, optional + Folder path where plots will be saved. The default is "./". + fmt : str, optional + Format for saving plots. The default is "png". + dpi : int, optional + Dots per inch for saving plots. The default is 300. + + Returns + ------- + None + + """ + self.save = save + self.folder = folder + self.fmt = fmt + self.dpi = dpi + + def config_scan_view(self, xlabel=r'$x$ [mm]', ylabel=r'$y$ [mm]', x_lim=[-20, 20], + y_lim=[-70, 70], legend_config = None): + + self.xlabel = xlabel + self.ylabel = ylabel + self.x_lim = x_lim + self.y_lim = y_lim + self.legend_config = legend_config - def config_canvas(self, dpi: int = 300, cmap: str = "RdYlBu_r", levels: int = 8, + def config_canvas(self, cmap: str = "RdYlBu_r", levels: int = 8, x_lim: List[float] = [-100, 100], y_lim_top: List[float] = [10,20], y_lim_bot: List[float] = [-10,-20], y_lim_scan: str = [-140, 140], cbar_lim: List[float] = [-140, 140], loc: str = "best", @@ -240,8 +364,6 @@ def config_canvas(self, dpi: int = 300, cmap: str = "RdYlBu_r", levels: int = 8, Parameters ---------- - dpi : int, optional - Dots per inch for the plot resolution. Default is 300. cmap : str, optional Colormap to use for plotting. Default is "RdYlBu_r". levels : int, optional @@ -266,8 +388,6 @@ def config_canvas(self, dpi: int = 300, cmap: str = "RdYlBu_r", levels: int = 8, None This function does not return anything. It only sets instance attributes. """ - self.dpi = dpi - self.cmap = cmap self.levels = levels @@ -358,6 +478,7 @@ def scan_view_and_fill(self, swap: bool = False, *scan: List) -> None: plt.show() + @printer def scan_view_and_bar(self, swap: bool = False, *scan: List) -> None: """ Plot scan data with error bars. @@ -366,7 +487,7 @@ def scan_view_and_bar(self, swap: bool = False, *scan: List) -> None: ---------- swap : bool, optional If True, swap x and y axes in the plot. Default is False. - *scan : List + scan : List List of scan data to plot. Each scan data should be provided as a list-like object. Returns @@ -378,16 +499,116 @@ def scan_view_and_bar(self, swap: bool = False, *scan: List) -> None: for s in scan: if swap: if s.y_err is not None: - ax.errorbar(s.y, s.x, xerr=s.y_err, yerr=None, fmt="-o", + ax.errorbar(s.y, s.x, xerr=s.y_err, yerr=None, fmt="o", markersize=3, capsize=3, capthick=1, linewidth=0.8) pass else: if s.y_err is not None: - ax.errorbar(s.x, s.y, xerr=None, yerr=s.y_err, fmt="-o", - markersize=3, capsize=3, capthick=1, linewidth=0.8) + ax.errorbar(s.x, s.y, xerr=None, yerr=s.y_err, fmt="o", c=s.color, + linestyle=s.line, markersize=3, capsize=3, capthick=1, linewidth=0.8, label=s.name) - plt.show() + ax.set_xlabel(self.xlabel) + ax.set_ylabel(self.ylabel) + ax.set_xlim(self.x_lim) + ax.set_ylim(self.y_lim) + try: + ax.legend(**self.legend_config) + except: + pass + + return fig, self.name + + @printer + def scan_compare(self, fills: List, bars: List) -> Tuple: + """ + Compare scans by plotting filled regions and error bars. + + Parameters + ---------- + fills : list of scans + List of data for the filled regions. + bars : list of scans + List of data for the error bars. + + Returns + ------- + fig : Figure + The matplotlib figure object. + name : str + The name associated with the plot. + + """ + fig, ax = plt.subplots(dpi=300) + for f in fills: + ax.fill_between(f.x, f.y-f.y_err, f.y+f.y_err, color=f.color, alpha=f.alpha, edgecolor="none") + ax.plot(f.x, f.y, color=f.color, label=f.name) + for b in bars: + ax.errorbar(b.x, b.y, xerr=None, yerr=b.y_err, fmt="o", c=b.color, + linestyle=b.line, markersize=3, capsize=3, + capthick=1, linewidth=0.8, label=b.name) + + ax.legend(**self.legend_config) + ax.set_xlim(self.x_lim) + ax.set_ylim(self.y_lim) + ax.set_xlabel(self.xlabel) + ax.set_ylabel(self.ylabel) + + return fig, self.name + + @printer + def contour(self, top_cnt, bot_cnt) -> Tuple: + """ + Create contour plots for the provided data. + + Parameters + ---------- + top_cnt : Topography object + Data for the top contour plot. + bot_cnt : Topography object + Data for the bottom contour plot. + + Returns + ------- + fig : Figure + The matplotlib figure object. + name : str + The name associated with the plot. + + """ + fig = plt.figure(dpi=self.dpi, figsize=(4,4)) + gs = GridSpec(4, 2, figure=fig, + width_ratios=[0.975, 0.025], + height_ratios=[0.25, 0.25, 0.25, 0.25]) + ax1 = fig.add_subplot(gs[0, 0]) + ax2 = fig.add_subplot(gs[1, 0]) + ax3 = fig.add_subplot(gs[0:2, 1]) + ax4 = fig.add_subplot(gs[2, 0]) + ax5 = fig.add_subplot(gs[3, 0]) + ax6 = fig.add_subplot(gs[2:4, 1]) + + im12, norm12 = discrete_colorbar(self.cmap, self.mean_lim[0], self.mean_lim[1], self.levels) + a1 = ax1.tricontourf(top_cnt.P[:,0], top_cnt.P[:,1], top_cnt.P[:,2], levels=self.levels, cmap=self.cmap, norm=norm12) + a2 = ax2.tricontourf(bot_cnt.P[:,0], bot_cnt.P[:,1], bot_cnt.P[:,2], levels=self.levels, cmap=self.cmap, norm=norm12) + cb1 = fig.colorbar(im12, ax=[a1,a2], cax=ax3, orientation='vertical', label="Expected Value", format="%.0f", pad=0.1, fraction=0.5, + ticks=(np.linspace(self.mean_lim[0], self.mean_lim[1], self.levels))) + + im45, norm45 = discrete_colorbar(self.cmap, self.std_lim[0], self.std_lim[1], self.levels) + a4 = ax4.tricontourf(top_cnt.P[:,0], top_cnt.P[:,1], top_cnt.unc, levels=self.levels, cmap=self.cmap, norm=norm45) + a5 = ax5.tricontourf(bot_cnt.P[:,0], bot_cnt.P[:,1], bot_cnt.unc, levels=self.levels, cmap=self.cmap, norm=norm45) + cb2 = fig.colorbar(im45, cax=ax6, orientation='vertical', label="Uncertainty", format="%.0f", pad=0.1, + ticks=list(np.linspace(self.std_lim[0], self.std_lim[1], self.levels))) + + for a in [ax1, ax2, ax4, ax5]: + a.tick_params(direction="in", top=1, right=1, color="k") # pad=5 + a.set_xlabel(r"$x$ [mm]") + a.set_ylabel(r"$y$ [mm]") + + for c in [cb1, cb2]: + c.ax.tick_params(direction='in', right=1, left=1, size=1.5, labelsize=8) + + plt.tight_layout() + return fig, self.name def contour_and_scan_2(self, top_cnt, bot_cnt, top_scan = None, bot_scan = None, top_err = None, bot_err = None) -> None: @@ -499,6 +720,8 @@ def cfg_matplotlib(font_size: int = 12, font_family: str = 'sans-serif', use_lat """ Set Matplotlib RC parameters for font size, font family, and LaTeX usage. + Borrowed from https://github.com/aletgn/b-fade/blob/master/src/bfade/util.py + Parameters ---------- font_size : int, optional @@ -521,6 +744,7 @@ def cfg_matplotlib(font_size: int = 12, font_family: str = 'sans-serif', use_lat matplotlib.rcParams['font.size'] = font_size matplotlib.rcParams['font.family'] = font_family matplotlib.rcParams['text.usetex'] = use_latex + matplotlib.rcParams["interactive"] = interactive def cbar_bounds(v: np.ndarray, w: np.ndarray) -> Tuple[float]: