libgunshotmatch 0.11.2__tar.gz → 0.13.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of libgunshotmatch might be problematic. Click here for more details.

Files changed (22) hide show
  1. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/PKG-INFO +3 -2
  2. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/README.rst +1 -1
  3. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/__init__.py +1 -1
  4. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/consolidate/__init__.py +7 -3
  5. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/gzip_util.py +7 -2
  6. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/peak.py +111 -3
  7. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/pyproject.toml +2 -1
  8. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/requirements.txt +1 -0
  9. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/LICENSE +0 -0
  10. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/comparison/__init__.py +0 -0
  11. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/comparison/_utils.py +0 -0
  12. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/comparison/projects.py +0 -0
  13. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/comparison/unknowns.py +0 -0
  14. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/consolidate/_fields.py +0 -0
  15. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/consolidate/_spectra.py +0 -0
  16. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/datafile.py +0 -0
  17. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/method/__init__.py +0 -0
  18. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/method/_fields.py +0 -0
  19. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/project.py +0 -0
  20. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/py.typed +0 -0
  21. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/search.py +0 -0
  22. {libgunshotmatch-0.11.2 → libgunshotmatch-0.13.0}/libgunshotmatch/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: libgunshotmatch
3
- Version: 0.11.2
3
+ Version: 0.13.0
4
4
  Summary: Base library for GunShotMatch.
5
5
  Author-email: Dominic Davis-Foster <dominic@davis-foster.co.uk>
6
6
  License: MIT
@@ -39,6 +39,7 @@ Requires-Dist: scipy>=1.9.0
39
39
  Requires-Dist: sdjson>=0.4.0
40
40
  Requires-Dist: tomli>=1.2.3; python_version < "3.11"
41
41
  Requires-Dist: tomli-w>=1.0.0
42
+ Requires-Dist: typing-extensions>=4.12.2
42
43
  Description-Content-Type: text/x-rst
43
44
 
44
45
 
@@ -135,7 +136,7 @@ libgunshotmatch
135
136
  .. |language| image:: https://img.shields.io/github/languages/top/GunShotMatch/libgunshotmatch
136
137
  :alt: GitHub top language
137
138
 
138
- .. |commits-since| image:: https://img.shields.io/github/commits-since/GunShotMatch/libgunshotmatch/v0.11.2
139
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/GunShotMatch/libgunshotmatch/v0.13.0
139
140
  :target: https://github.com/GunShotMatch/libgunshotmatch/pulse
140
141
  :alt: GitHub commits since tagged version
141
142
 
@@ -91,7 +91,7 @@ libgunshotmatch
91
91
  .. |language| image:: https://img.shields.io/github/languages/top/GunShotMatch/libgunshotmatch
92
92
  :alt: GitHub top language
93
93
 
94
- .. |commits-since| image:: https://img.shields.io/github/commits-since/GunShotMatch/libgunshotmatch/v0.11.2
94
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/GunShotMatch/libgunshotmatch/v0.13.0
95
95
  :target: https://github.com/GunShotMatch/libgunshotmatch/pulse
96
96
  :alt: GitHub commits since tagged version
97
97
 
@@ -29,5 +29,5 @@ Base library for GunShotMatch.
29
29
  __author__: str = "Dominic Davis-Foster"
30
30
  __copyright__: str = "2020-2023 Dominic Davis-Foster"
31
31
  __license__: str = "MIT License"
32
- __version__: str = "0.11.2"
32
+ __version__: str = "0.13.0"
33
33
  __email__: str = "dominic@davis-foster.co.uk"
@@ -41,6 +41,7 @@ import pyms_nist_search
41
41
  from pyms.DPA.Alignment import Alignment
42
42
  from pyms.Spectrum import MassSpectrum
43
43
  from pyms_nist_search import ReferenceData, SearchResult
44
+ from typing_extensions import Self
44
45
 
45
46
  # this package
46
47
  from libgunshotmatch.consolidate._fields import (
@@ -484,9 +485,12 @@ class ConsolidatedPeak:
484
485
  else:
485
486
  ms_list.append(MassSpectrum.from_dict(msd))
486
487
 
488
+ rt_list = [float("nan") if hn == -65535 else hn for hn in d["rt_list"]]
489
+ area_list = [float("nan") if hn == -65535 else hn for hn in d["area_list"]]
490
+
487
491
  return cls(
488
- rt_list=d["rt_list"],
489
- area_list=d["area_list"],
492
+ rt_list=rt_list,
493
+ area_list=area_list,
490
494
  ms_list=ms_list,
491
495
  meta=d["meta"],
492
496
  ms_comparison=d["ms_comparison"],
@@ -712,7 +716,7 @@ class ConsolidatedPeakFilter:
712
716
  verbose: bool = False
713
717
 
714
718
  @classmethod
715
- def from_method(cls: Type["ConsolidatedPeakFilter"], method: ConsolidateMethod) -> "ConsolidatedPeakFilter":
719
+ def from_method(cls: Type[Self], method: ConsolidateMethod) -> Self:
716
720
  """
717
721
  Construct a :class:`~.ConsolidatedPeakFilter` from a :class:`~.ConsolidateMethod`.
718
722
 
@@ -60,16 +60,21 @@ def read_gzip_json(path: PathLike) -> JSONOutput:
60
60
  return sdjson.load(f)
61
61
 
62
62
 
63
- def write_gzip_json(path: PathLike, data: JSONInput, indent: Optional[int] = 2) -> None:
63
+ def write_gzip_json(path: PathLike, data: JSONInput, indent: Optional[int] = 2, mtime: int = 0) -> None:
64
64
  """
65
65
  Write JSON to a gzip file.
66
66
 
67
67
  :param path: The filename to write to.
68
68
  :param data: The JSON-serializable data to output.
69
69
  :param indent: Number of spaces used to indent JSON.
70
+ :param mtime: Modification time for gzip header
71
+
72
+ :rtype:
73
+
74
+ .. versionchanged:: 0.12.0 Added ``mtime`` argument.
70
75
  """
71
76
 
72
77
  json_data = sdjson.dumps(data, indent=indent)
73
78
 
74
- with gzip.open(PathPlus(path), 'w') as f:
79
+ with gzip.GzipFile(PathPlus(path), 'w', mtime=mtime) as f:
75
80
  f.write(json_data.encode("utf-8"))
@@ -27,13 +27,14 @@ Classes representing peaks, and functions for peak filtering.
27
27
  #
28
28
 
29
29
  # stdlib
30
- from typing import Any, Collection, Dict, List, Mapping, Optional, Sequence, Type, Union
30
+ from typing import TYPE_CHECKING, Any, Collection, Dict, List, Mapping, Optional, Sequence, Type, Union
31
31
 
32
32
  # 3rd party
33
33
  import numpy
34
34
  import pandas # type: ignore[import-untyped]
35
35
  import sdjson
36
36
  from domdf_python_tools.paths import PathPlus
37
+ from domdf_python_tools.stringlist import StringList
37
38
  from domdf_python_tools.typing import PathLike
38
39
  from pyms.BillerBiemann import num_ions_threshold
39
40
  from pyms.DPA.Alignment import exprl2alignment
@@ -42,10 +43,15 @@ from pyms.Experiment import Experiment
42
43
  from pyms.IonChromatogram import IonChromatogram
43
44
  from pyms.Noise.Analysis import window_analyzer
44
45
  from pyms.Peak.Class import AbstractPeak, ICPeak, Peak
46
+ from pyms.Peak.List import composite_peak
45
47
  from pyms.Peak.List.Function import sele_peaks_by_rt
46
48
  from pyms.Spectrum import MassSpectrum
47
49
  from pyms_nist_search import SearchResult
48
50
 
51
+ if TYPE_CHECKING:
52
+ # this package
53
+ from libgunshotmatch.project import Project
54
+
49
55
  __all__ = (
50
56
  "PeakList",
51
57
  "QualifiedPeak",
@@ -55,6 +61,7 @@ __all__ = (
55
61
  "filter_peaks",
56
62
  "peak_from_dict",
57
63
  "write_alignment",
64
+ "write_project_alignment",
58
65
  "base_peak_mass",
59
66
  )
60
67
 
@@ -401,6 +408,107 @@ def align_peaks(
401
408
  return A1
402
409
 
403
410
 
411
+ def _format_rt(rt: Optional[float]) -> str:
412
+ return "NA" if rt is None or numpy.isnan(rt) else f"{rt:.3f}"
413
+
414
+
415
+ def _format_area(area: Optional[float]) -> str:
416
+ return "NA" if area is None else f"{area:.0f}"
417
+
418
+
419
+ def _alignment_write_csv(
420
+ project: "Project",
421
+ output_dir_p: PathPlus,
422
+ ) -> None:
423
+
424
+ # Sort expr_code and peakpos into order from datafile_data
425
+ desired_order = list(project.datafile_data)
426
+ sort_map = [project.alignment.expr_code.index(code) for code in desired_order]
427
+ expr_code = [project.alignment.expr_code[idx] for idx in sort_map]
428
+ peakpos = [project.alignment.peakpos[idx] for idx in sort_map]
429
+ assert desired_order == expr_code
430
+
431
+ # write headers
432
+ header = ["UID", "RTavg", *(f'"{item}"' for item in project.datafile_data)]
433
+
434
+ rt_stringlist = StringList([','.join(header)])
435
+ area_stringlist = StringList([','.join(header)])
436
+
437
+ # for each alignment position write alignment's peak and area
438
+ for peak_idx in range(len(peakpos[0])): # loop through peak lists (rows)
439
+ rts, areas, new_peak_list = [], [], []
440
+
441
+ for row in peakpos:
442
+ peak = row[peak_idx]
443
+
444
+ if peak is None:
445
+ rts.append(None)
446
+ areas.append(None)
447
+ else:
448
+ rts.append(peak.rt / 60)
449
+ areas.append(peak.area)
450
+ new_peak_list.append(peak)
451
+
452
+ compo_peak = composite_peak(new_peak_list)
453
+
454
+ if compo_peak is None:
455
+ continue
456
+
457
+ uid, mean_rt = compo_peak.UID, f"{float(compo_peak.rt / 60):.3f}"
458
+ rt_stringlist.append(','.join([uid, mean_rt, *map(_format_rt, rts)]))
459
+ area_stringlist.append(','.join([uid, mean_rt, *map(_format_area, areas)]))
460
+
461
+ (output_dir_p / f"{project.name}_alignment_rt.csv").write_lines(rt_stringlist)
462
+ (output_dir_p / f"{project.name}_alignment_area.csv").write_lines(area_stringlist)
463
+
464
+
465
+ def write_project_alignment(
466
+ project: "Project",
467
+ output_dir: PathLike,
468
+ require_all_datafiles: bool = False,
469
+ ) -> None:
470
+ """
471
+ Write the alignment data (retention times, peak areas, mass spectra) to disk.
472
+
473
+ The output files are as follows:
474
+
475
+ * :file:`{{project.name}}_alignment_rt.csv`, containing the aligned retention times.
476
+ * :file:`{{project.name}}_alignment_area.csv`, containing the peak areas for the corresponding aligned retention times.
477
+ * :file:`{{project.name}}_alignment_rt.json`, containing the aligned retention times.
478
+ * :file:`{{project.name}}_alignment_area.json`, containing the peak areas for the corresponding aligned retention times.
479
+ * :file:`{{project.name}}_alignment_ms.json`, containing the mass spectra for the corresponding aligned retention times.
480
+
481
+ :param project:
482
+ :param output_dir: Directory to store the output files in.
483
+ :param require_all_datafiles: Whether the peak must be present in all experiments to be included in the data frame.
484
+
485
+ :rtype:
486
+
487
+ .. versionadded:: 0.12.0 Added as an alternative to :func:`~.write_alignment`. This function sorts the columns to match the order of ``project.datafile_data``.
488
+ """
489
+
490
+ output_dir_p = PathPlus(output_dir)
491
+
492
+ _alignment_write_csv(project, output_dir_p)
493
+
494
+ rt_alignment = project.alignment.get_peak_alignment(require_all_expr=require_all_datafiles)
495
+ rt_alignment_filename = output_dir_p / f"{project.name}_alignment_rt.json"
496
+ rt_alignment_filename.write_clean(rt_alignment.to_json(indent=2))
497
+
498
+ area_alignment = project.alignment.get_area_alignment(require_all_expr=require_all_datafiles)
499
+ area_alignment_filename = output_dir_p / f"{project.name}_alignment_area.json"
500
+ area_alignment_filename.write_clean(area_alignment.to_json(indent=2))
501
+
502
+ ms_alignment = project.alignment.get_ms_alignment(require_all_expr=require_all_datafiles)
503
+ # ms_alignment.to_json(output_dir_p / 'alignment_ms.json')
504
+ alignment_ms_filename = (output_dir_p / f"{project.name}_alignment_ms.json")
505
+ alignment_ms_filename.dump_json(
506
+ ms_alignment.to_dict(),
507
+ json_library=sdjson, # type: ignore[arg-type]
508
+ indent=2,
509
+ )
510
+
511
+
404
512
  def write_alignment(
405
513
  alignment: Alignment,
406
514
  project_name: str,
@@ -434,11 +542,11 @@ def write_alignment(
434
542
 
435
543
  rt_alignment = alignment.get_peak_alignment(require_all_expr=require_all_datafiles)
436
544
  rt_alignment_filename = output_dir_p / f"{project_name}_alignment_rt.json"
437
- rt_alignment_filename.write_clean(rt_alignment.to_json())
545
+ rt_alignment_filename.write_clean(rt_alignment.to_json(indent=2))
438
546
 
439
547
  area_alignment = alignment.get_area_alignment(require_all_expr=require_all_datafiles)
440
548
  area_alignment_filename = output_dir_p / f"{project_name}_alignment_area.json"
441
- area_alignment_filename.write_clean(area_alignment.to_json())
549
+ area_alignment_filename.write_clean(area_alignment.to_json(indent=2))
442
550
 
443
551
  ms_alignment = alignment.get_ms_alignment(require_all_expr=require_all_datafiles)
444
552
  # ms_alignment.to_json(output_dir_p / 'alignment_ms.json')
@@ -4,7 +4,7 @@ build-backend = "whey"
4
4
 
5
5
  [project]
6
6
  name = "libgunshotmatch"
7
- version = "0.11.2"
7
+ version = "0.13.0"
8
8
  description = "Base library for GunShotMatch."
9
9
  readme = "README.rst"
10
10
  keywords = []
@@ -23,6 +23,7 @@ dependencies = [
23
23
  "sdjson>=0.4.0",
24
24
  'tomli>=1.2.3; python_version < "3.11"',
25
25
  "tomli-w>=1.0.0",
26
+ "typing-extensions>=4.12.2",
26
27
  ]
27
28
  classifiers = [
28
29
  "Development Status :: 3 - Alpha",
@@ -11,3 +11,4 @@ scipy>=1.9.0
11
11
  sdjson>=0.4.0
12
12
  tomli>=1.2.3; python_version < "3.11"
13
13
  tomli-w>=1.0.0
14
+ typing-extensions>=4.12.2