Source code for libgunshotmatch_mpl.peakviewer

#!/usr/bin/env python3
#
#  peakviewer.py
"""
Common peak drawing functionality.
"""
#
#  Copyright © 2023-2024 Dominic Davis-Foster <dominic@davis-foster.co.uk>
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
#  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
#  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
#  OR OTHER DEALINGS IN THE SOFTWARE.
#

# stdlib
from typing import List

# 3rd party
import numpy
from domdf_python_tools.paths import PathLike
from libgunshotmatch.project import Project
from matplotlib.axes import Axes
from matplotlib.figure import Figure

# this package
from libgunshotmatch_mpl.chromatogram import draw_peak_vlines

__all__ = ("UnsupportedProject", "draw_peaks", "load_project")


[docs]class UnsupportedProject(ValueError): """ Exception raised when a project is missing certain attributes (such as the consolidated peak list). """
[docs]def load_project(filename: PathLike) -> Project: """ Load a project from disk and ensure it has all required attributes for displaying chromatograms. :param filename: """ project = Project.from_file(filename) # Validation of loaded datafile if project.consolidated_peaks is None: raise UnsupportedProject("Project.consolidated_peaks is unset") for (name, repeat) in project.datafile_data.items(): if repeat.qualified_peaks is None: raise UnsupportedProject(f"Repeat.qualified_peaks is unset for {name!r}") if repeat.datafile.intensity_matrix is None: raise UnsupportedProject(f"Datafile.intensity_matrix is unset for {name!r}") return project
[docs]def draw_peaks(project: Project, retention_times: List[float], figure: Figure, axes: List[Axes]) -> None: """ Draw the peaks at the given retention time for each repeat in the project. :param project: :param retention_times: List of retention times for each repeat in the project. :param figure: :param axes: .. versionchanged:: 0.6.0 Replaced ``peak_idx`` argument with ``retention_times`` """ min_rt: float = numpy.nanmin(retention_times) - 20 max_rt: float = numpy.nanmax(retention_times) + 20 for repeat_idx, (_, repeat) in enumerate(project.datafile_data.items()): assert repeat.datafile.intensity_matrix is not None im = repeat.datafile.intensity_matrix tic = im.tic # Get subset of timelist within RT range time_list = [] intensity_list = [] for rt, intensity in zip(tic.time_list, tic.intensity_array): if min_rt <= rt <= max_rt: time_list.append(rt / 60) intensity_list.append(intensity) axes[repeat_idx].plot(time_list, intensity_list) peak_rt = retention_times[repeat_idx] if not numpy.isnan(peak_rt): draw_peak_vlines(axes[repeat_idx], peak_rt / 60, intensity_list[time_list.index(peak_rt / 60)]) axes[repeat_idx].text( peak_rt / 60, axes[repeat_idx].get_ylim()[1] * 0.2, f" {peak_rt/60:0.3f}", ) figure.supylabel("Intensity", fontsize="medium") axes[0].autoscale() axes[-1].set_xlabel("Retention Time (mins)") for ax, repeat_name in zip(axes, project.datafile_data): ax.ticklabel_format(axis='y', scilimits=(0, 0), useMathText=True) ax.set_ylim(bottom=0) # xmin, xmax = ax.get_xlim() # ax.text(xmin + (xmax-xmin)*0.05, ax.get_ylim()[1] *0.8, repeat_name) ax.annotate(repeat_name, (0.01, 0.8), xycoords="axes fraction") axes[0].set_xlim(min_rt / 60, max_rt / 60)
# # figure.subplots_adjust(bottom=0, top=1, left=0, right=1, hspace=0, wspace=0) # # figure.subplots_adjust(top=0.95, right=0.95) # figure.subplots_adjust(bottom=0.1, left=0.1, top=0.95, right=0.98, hspace=0.3)