Source code for topostats.tracing.dnacurvature
"""Module for calculating curvature statistics."""
import logging
# from pathlib import Path
import numpy as np
from topostats.logs.logs import LOGGER_NAME
LOGGER = logging.getLogger(LOGGER_NAME)
# Disable white space before colon
# black and flake8 conflict https://black.readthedocs.io/en/stable/faq.html#why-are-flake8-s-e203-and-w503-violated
# noqa: E203
[docs]
class Curvature:
"""
Class for determining the curvature of molecules.
Parameters
----------
molecule_coordinates : np.ndarray
Coordinates of the simplified splined trace of a molecule. These are returned by dnaTracing.
circular : bool
Whether the image is circular or not.
"""
def __init__(
self,
molecule_coordinates: np.ndarray,
circular: bool,
):
"""
Initialise the class.
Parameters
----------
molecule_coordinates : np.ndarray
Coordinates of the simplified splined trace of a molecule. These are returned by dnaTracing.
circular : bool
Whether the image is circular or not.
"""
self.molecule_coordinates = molecule_coordinates
self.circular = circular
self.n_points = len(molecule_coordinates)
self.first_derivative = None
self.second_derivative = None
self.local_curvature = None
[docs]
def calculate_derivatives(self, edge_order: int = 1) -> None:
"""
Find the curvature for an individual molecule.
Parameters
----------
edge_order : int
Gradient is passed to numpy.gradient and Gradient is calculated using N-th order accurate differences at
boundaries. Also used to expand the array by the necessary number of coordinates at either end to form a
loop for the calculations.
"""
# If circular we need gradients correctly calculated at the start and end and so the array has the number of
# points used in np.gradient(edge_order) from the end attached to the start and the same from the start attached
# to the end.
edge_order_boundary = edge_order + 1
if self.circular:
coordinates = np.vstack(
(
self.molecule_coordinates[-edge_order_boundary:],
self.molecule_coordinates,
self.molecule_coordinates[:edge_order_boundary],
)
)
else:
coordinates = self.molecule_coordinates
self.first_derivative = np.gradient(coordinates, edge_order, axis=0)
self.second_derivative = np.gradient(self.first_derivative, edge_order, axis=0)
# Now trim the arrays back to the appropriate size
if self.circular:
self.first_derivative = self.first_derivative[edge_order_boundary:-edge_order_boundary]
self.second_derivative = self.second_derivative[edge_order_boundary:-edge_order_boundary]
# def _extract_coordinates(molecule) -> np.ndarray:
# """Extract the coordinates for the points"""
# curve = []
# contour = 0
# # for i in len(molecule):
# pass
[docs]
def _calculate_local_curvature(self) -> float:
"""Calculate the local curvature between points."""
self.local_curvature = (
(self.second_derivative[:, 0] * self.first_derivative[:, 1])
- (self.first_derivative[:, 0] * self.second_derivative[:, 1])
) / ((self.first_derivative[:, 0] ** 2) + (self.first_derivative[:, 1] ** 2)) ** 1.5