Source code for impy.arrays.specials

from __future__ import annotations
from typing import Callable
import numpy as np
from inspect import signature
from scipy import optimize as opt
from .bases import MetaArray
from ..axes import ImageAxesError
from ..frame import AxesFrame
from ..utils.axesop import switch_slice, complement_axes, find_first_appeared, del_axis
from ..collections import DataDict

SCALAR_PROP = (
    "area", "bbox_area", "convex_area", "eccentricity", "equivalent_diameter", "euler_number",
    "extent", "feret_diameter_max", "filled_area", "label", "major_axis_length", "max_intensity",
    "mean_intensity", "min_intensity", "minor_axis_length", "orientation", "perimeter",
    "perimeter_crofton", "solidity", "phase_mean", "phase_std")


[docs]class PropArray(MetaArray): additional_props = ["dirpath", "metadata", "name", "propname"] def __new__(cls, obj, *, name=None, axes=None, dirpath=None, metadata=None, propname=None, dtype=None): if propname is None: propname = "User_Defined" if dtype is None and propname in SCALAR_PROP: dtype = np.float32 elif dtype is None: dtype = object else: dtype = dtype self = super().__new__(cls, obj, name, axes, dirpath, metadata, dtype=dtype) self.propname = propname return self def __repr__(self): return f"\n"\ f" shape : {self.shape_info}\n"\ f" dtype : {self.dtype}\n"\ f" directory : {self.dirpath}\n"\ f"original image: {self.name}\n"\ f"property name : {self.propname}\n" def _repr_dict_(self): return {" shape ": self.shape_info, " dtype ": self.dtype, " directory ": self.dirpath, "original image": self.name, "property name ": self.propname}
[docs] def plot(self, along=None, cmap="jet", cmap_range=(0, 1)): """ Plot all the results with x-axis defined by `along`. Parameters ---------- along : str, optional Which axis will be the x-axis of plot, by default None cmap : str, default is "jet" Colormap of each graph. cmap_range : tuple, default is (0, 1) Range of float for colormap iteration. """ import matplotlib.pyplot as plt if self.dtype == object: raise TypeError(f"Cannot call plot_profile for {self.propname} " "because dtype == object.") if along is None: along = find_first_appeared("tzp<yxc", include=self.axes) iteraxes = del_axis(self.axes, self.axisof(along)) cmap = plt.get_cmap(cmap) positions = np.linspace(*cmap_range, self.size//self.sizeof(along), endpoint=False) x = np.arange(self.sizeof(along))*self.scale[along] for i, (sl, y) in enumerate(self.iter(iteraxes)): plt.plot(x, y, color=cmap(positions[i])) plt.title(f"{self.propname}") plt.xlabel(along) plt.show() return self
[docs] def hist(self, along: str = "p", bins: int = None, cmap: str = "jet", cmap_range: tuple[float, float] = (0., 1.)) -> PropArray: """ Plot histogram. Parameters ---------- along : str, optional Which axis will be the x-axis of plot, by default None bins : int, optional Bin number of histogram. cmap : str, default is "jet" Colormap of each graph. cmap_range : tuple, default is (0, 1) Range of float for colormap iteration. Returns ------- self """ import matplotlib.pyplot as plt if self.dtype == object: raise TypeError(f"Cannot call plot_profile for {self.propname} " "because dtype == object.") if along is None: along = find_first_appeared("pxyzt<c", include=self.axes) iteraxes = del_axis(self.axes, self.axisof(along)) cmap = plt.get_cmap(cmap) positions = np.linspace(*cmap_range, self.size//self.sizeof(along), endpoint=False) for i, (sl, y) in enumerate(self.iter(iteraxes)): plt.hist(y, color=cmap(positions[i]), bins=bins, alpha=0.5) plt.title(f"{self.propname}") plt.show() return self
[docs] def curve_fit(self, f: Callable, p0 = None, dims = "t", return_fit: bool = True) -> DataDict[str, PropArray]: """ Run scipy.optimize.curve_fit for each dimesion. Parameters ---------- f : callable Model function. p0 : array or callable, optional Initial parameter. Callable object that estimate initial paramter can also be passed here. dims : str, by default "t" Along which axes fitting algorithms are conducted. return_fit : bool, by default True If fitting trajectories are returned. If the input array is large, this will save much memory. Returns ------- DataDict params : Fitting parameters covs : Covariances fit : fitting trajectories (if return_fit==True) """ c_axes = complement_axes(dims, all_axes=self.axes) if len(dims)!=1: raise NotImplementedError("Only 1-dimensional fitting is implemented.") n_params = len(signature(f).parameters)-1 params = np.empty(self.sizesof(c_axes) + (n_params,), dtype=np.float32) errs = np.empty(self.sizesof(c_axes) + (n_params,), dtype=np.float32) if return_fit: fit = np.empty(self.sizesof(c_axes) + (self.sizeof(dims),), dtype=self.dtype) xdata = np.arange(self.sizeof(dims))*self.scale[dims] for sl, data in self.iter(c_axes, exclude=dims): p0_ = p0 if not callable(p0) else p0(data) result = opt.curve_fit(f, xdata, data, p0_) params[sl], cov = result errs[sl] = np.sqrt(np.diag(cov)) if return_fit: fit[sl] = f(xdata, *(result[0])) # set infos params = params.view(self.__class__) errs = errs.view(self.__class__) params._set_info(self, new_axes=del_axis(self.axes, dims)+"m") errs._set_info(self, new_axes=del_axis(self.axes, dims)+"m") if return_fit: fit = fit.view(self.__class__) fit._set_info(self, new_axes=del_axis(self.axes, dims)+dims) if return_fit: return DataDict(params=params, errs=errs, fit=fit) else: return DataDict(params=params, errs=errs)
[docs] def as_frame(self, colname: str = "f") -> AxesFrame: """ N-dimensional data to DataFrame. The intensity data is stored in the `colname` column. Parameters ---------- colname : str, default is "f" The name of new column. Returns ------- AxesFrame DataFrame with PropArray data. """ if colname in self.axes: raise ImageAxesError(f"Axis {colname} already exists.") if self.dtype == object: raise TypeError("Cannot make AxesFrame with dtype object.") indices = np.indices(self.shape) new_axes = str(self.axes) + colname data_list = [ind.ravel() for ind in indices] + [self.value.ravel()] data_dict = {a: data for a, data in zip(new_axes, data_list)} df = AxesFrame(data_dict, columns=new_axes) df.set_scale(self) return df