Source code for pycellid.core

# !/usr/bin/env python

# -*- coding: utf-8 -*-

# This file is part of the
#   PyCellID Project (
#     https://github.com/pyCellID,
#     https://github.com/darksideoftheshmoo
# ).
# Copyright (c) 2021. Clemente, Jose
# License: MIT
#   Full Text: https://github.com/pyCellID/pyCellID/blob/main/LICENSE

# =============================================================================
# DOCS
# =============================================================================

"""Merge and analyze tables of characteristics of cells and find images."""

# =============================================================================
# IMPORTS
# =============================================================================
import warnings
from pathlib import Path

import attr

import matplotlib.pyplot as plt

import pandas as pd

from pycellid import images as img
from pycellid.io import merge_tables


# =============================================================================
# CellData Class
# =============================================================================


def _check_path(self, attribute, value):
    """
    Check the existence of a path.

    If the path provided does not exist, it returns a ``FileNotFoundError``.
    """
    if not Path(value).exists():
        raise FileNotFoundError(f"Path < {value} > not exist")


[docs]@attr.s(cmp=False, repr=False) class CellData(object): """ Collapse your data into a single data frame. Recursively inspect the path, create a unique identifier per cell, and inspect related images. Parameters ---------- _path: str global path to output ``CellID`` tables. _df : ``pandas.DataFrame`` Dataframe (output of ``CellID``) containing all the measured parameters of each cell. Return ------ An instance of ``CellData`` containing all the information of microscopy experiment. Examples -------- >>> from pycellid.core import CellData >>> df = CellData( path = '../my_experiment', df = my_dataframe ) """ _path = attr.ib(validator=_check_path) _df = attr.ib()
[docs] @classmethod def from_csv(cls, path, **kwargs): """ Build a data frame from csv files contained in path. A ``CellData`` class will be instantiated. """ return cls(path=path, df=merge_tables(path, **kwargs))
@property def plot(self): """ Represent set of ``cells_image`` or numerical data. For ``cimage`` method you must specify an identifier ``id={}``. """ return CellsPloter(self) def __eq__(self, other): """ Implement '``==``' operator. x == a <=> x.__eq(a) => bool. """ return self._df == other def __ne__(self, other): """ Implement '``!=``' operator. x != a <=> x.__ne(a) => bool. """ return not self == other def __lt__(self, other): """ Implement '``<``' operator. x < a <=> x.__lt(a) => bool. """ return self._df < other def __le__(self, other): """ Implement '``<=``' operator. x <= a <=> x.__lt(a) => bool. """ return self._df <= other def __gt__(self, other): """ Implement '``>``' operator. x > a <=> x.__lt(a) => bool. """ return self._df > other def __ge__(self, other): """ Implement '``>=``' operator. x >= a <=> x.__lt(a) => bool. """ return self._df >= other def __lshift__(self, other): """ Return a shifted left by b. operator.__lshift__(a, b). """ return self._df.__lshift__(other) def __rshift__(self, other): """ Return a shifted right by b. operator.__rshift__(a, b). """ return self._df.__rshift__(other) def __getitem__(self, slices): """ Return the item of the object at index ``k``. x[k] <=> x.__getitem__(k). """ sliced = self._df.__getitem__(slices) return CellData(path=self._path, df=sliced) def __getattr__(self, a): """ Call when the default attribute access fails (``AttributeError``). getattr(x, y) <==> x.__getattr__(y) <==> getattr(x, y). """ return self._df.__getattr__(a) def __setitem__(self, idx, values): """Call to implement assignment to ``self[key]``.""" return self._df.__setitem__(idx, values) def __iter__(self): """Call when an iterator is required for a container. iter(x) <=> x.__iter__(). """ return iter(self._df) def __len__(self): """Implement the built-in function ``len()``. len(x) <=> x.__len__(). """ return len(self._df) def __repr__(self): """Print a representation of your object.""" return repr(self._df) def _repr_html_(self): """Print a rich HTML version of your object.""" ad_id = id(self) if isinstance(self._df, pd.DataFrame) or \ isinstance(self._df, self.__class__): with pd.option_context("display.show_dimensions", False): df_html = self._df._repr_html_() rows = f"{self._df.shape[0]} rows" columns = f"{self._df.shape[1]} columns" footer = f"PyCellID.core.CellData - {rows} x {columns}" parts = [ f'<div class="PyCellID.core.CellData" id={ad_id}>', df_html, footer, "</div>", ] html = "".join(parts) return html else: self._df.__repr__()
[docs] def get_dataframe(self): """Return a copy of the internal ``_df``.""" return self._df.copy()
# ============================================================================= # CellsPloter Class # =============================================================================
[docs]@attr.s(repr=False) class CellsPloter: """ Accessor to plotter class. Create a representation of each cell within a grid, inspect an entire image or create a snippet of a single cell. Provide a wrapper of pandas methods for plotting. Return axes to plot. Attributes ---------- cells : CellData An instance of ``CellData`` containing all the information of microscopy experiment. """ cells = attr.ib() def __call__(self, kind="cells_image", **kwargs): """ Call instance as a function. ``plot() <==> plot.__call__()``. """ if kind.startswith("_"): raise AttributeError(f"Invalid plot method '{kind}'") method = getattr(self, kind, None) if not callable(method): raise AttributeError(f"Invalid plot method '{kind}'") if method is None: method = getattr(self.cells._df.plot, kind) return method(**kwargs) def __getattr__(self, a): """ Call when the default attribute access fails (``AttributeError``). getattr(x, y) <==> x.__getattr__(y) <==> getattr(x, y). """ return getattr(self.cells._df.plot, a) def __repr__(self): """ Compute the "official" string representation of an object. repr(x) <=> x.__repr__(). """ return f"CellsPloter(cells={hex(id(self.cells))})"
[docs] def cells_image(self, array_img_kws=None, imshow_kws=None, ax=None): r"""Display a random selection of cells on a square grid. By default it represents a :math:`4\times 4` matrix chosen at random. Returns ------- ax to plot or figure. Other Parameters ---------------- array_img_kws : dict Set the ``pycellid.images.img_array`` parameters. ``n`` : number of cells. ``channels`` : "``TFP``" or another that you have encoded. imshow_kws : dict If you use matplotlib set equal to ``plt.imshow``. ax: Use your axes to plot. """ data_c = self.cells ax = plt.gca() if ax is None else ax imshow_kws = {} if imshow_kws is None else imshow_kws array_img_kws = {} if array_img_kws is None else array_img_kws imshow_kws.setdefault("cmap", "Greys") arr_c = img.array_img(data=data_c, path=data_c._path, **array_img_kws) ax.imshow(arr_c, **imshow_kws) ax.axis("off") return ax
[docs] def cimage(self, identifier, box_img_kws=None, imshow_kws=None, ax=None): """Show a sigle cell or complete image. '``identifier``' param is required. Reference to a valid image or position. By default, an image with a size of ``(1392 X 1040)px`` will be rendered. Parameters ---------- identifier : path or dict path to an image file ``dict = { "channel":str, "UCID":int, t_frame":int }``. Returns ------- ax to plot or figure. Other Parameters ---------------- box_img_kws : dict Set the ``pycellid.images.box_img`` parameters. ``im`` : numpy.array A full fluorescence microscopy image. ``x_pos`` : int :math:`x`-coordinate of the center of the cell of interest. ``y_pos`` : int :math:`y`-coordinate of the center of the cell of interest. ``radius`` : int lenght (in px) between the center of the image and each edge. Default = ``90``. ``mark_center`` : ``bool`` mark a black point. Default = ``False``. imshow_kws : dict If you use matplotlib set equal to ``plt.imshow``. ax: Use your axes to plot. """ data_c = self.cells ax = plt.gca() if ax is None else ax imshow_kws = {} if imshow_kws is None else imshow_kws box_img_kws = {} if box_img_kws is None else box_img_kws imshow_kws.setdefault("cmap", "Greys") if isinstance(identifier, dict): ucid = identifier["ucid"] t_frame = identifier["t_frame"] try: [[x, y]] = data_c[ (data_c.ucid == ucid) & (data_c.t_frame == t_frame) ][["xpos", "ypos"]].values.tolist() r = 90 except ValueError: x, y, r = int(1392 / 2), int(1040 / 2), int(1040 / 2) message = "not match ucid and t_frame. See picture!" warnings.warn(message) identifier = img.img_name(data_c._path, **identifier) else: x, y, r = int(1392 / 2), int(1040 / 2), int(1040 / 2) box_img_kws.setdefault("x_pos", x) box_img_kws.setdefault("y_pos", y) box_img_kws.setdefault("radius", r) arr = plt.imread(identifier) arr_c = img.box_img(im=arr, **box_img_kws) ax.imshow(arr_c, **imshow_kws) ax.axis("off") return ax