Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,69 @@ License
4. Any academic or scholarly publication arising from the use of this Software or any derivative works thereof will include the following acknowledgment: The Software used in this research was created by [INSERT AUTHOR NAMES] of UC Riverside. © 2024 UCR.

Commercial entities: please contact [email protected] or [email protected] for licensing opportunities.

Useful Utility Functions
------------------------
ModiFinder includes several useful utility functions for mass spectrometry data analysis and visualization, exposed under `modifinder.utilities`.

**Reading MGF Files**
You can easily read MGF files into a pandas DataFrame using `read_mgf`.

```python
from modifinder.utilities import read_mgf

# Read MGF file
df = read_mgf("path/to/your/spectrum.mgf")
print(df.head())
```

**Visualization**
The `vis` module provides powerful visualization tools.

```python
from modifinder.utilities import vis
import matplotlib.pyplot as plt

# Draw a molecule
img = vis.draw_molecule("C1=CC=C(C=C1)O", output_type="png")
plt.imshow(img)
plt.show()

# Draw a spectrum
# spectrum_data is a list of (mz, intensity) tuples
spectrum_data = df.iloc[0]['spectrum'].T.tolist()
vis.draw_spectrum(spectrum_data)
plt.show()
```

Developer Guide
---------------

### Running Tests
To run the automated tests, ensure you have `pytest` installed. Then run:

```bash
pytest
```
or specifically for utilities:
```bash
pytest modifinder/utilities/tests/
```

### Release Process
ModiFinder uses GitHub Actions for automated releases and documentation deployment.

**Creating a New Release (PyPI)**
1. Update the version number in `modifinder/__init__.py`, `pyproject.toml` and `docs/source/conf.py`.
2. Create and push a new tag starting with `v` (e.g., `v1.2.3`).
```bash
git tag v1.2.3
git push origin v1.2.3
```
3. This triggers the `pypi_publish.yml` workflow, which:
- Builds the package.
- Publishes it to PyPI (and TestPyPI).
- Creates a GitHub Release.

**Updating Documentation**
Documentation is automatically rebuilt and deployed to GitHub Pages whenever changes are pushed to the `main` branch (via `documentation.yml`).
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

project = 'ModiFinder'
author = 'Reza Shahneh'
release = '1.4'
release = '1.5'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
56 changes: 53 additions & 3 deletions docs/source/tutorials/basics.ipynb

Large diffs are not rendered by default.

34 changes: 33 additions & 1 deletion docs/source/tutorials/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,38 @@ plt.show()



### Oracle

to use ModiFinder in the oracle mode, you can just set the is_known variable of your target compound to True and reannotate the network, this way, the annotation engine will use the structure of the unknown to further refine the peak annotations


```python
oracle_sample_known = Compound("CCMSLIB00011906190", id="known", **args)
oracle_sample_modified = Compound("CCMSLIB00011906105", id="modified", **args)
mf = ModiFinder(oracle_sample_known, oracle_sample_modified, helpers=helpers_array, **args)
probs = mf.generate_probabilities()
img_prediction1 = mf.draw_prediction(probs, oracle_sample_known.id, show_legend=True, show_labels=True, shrink_labels=True, size=(1000, 1000), annotation_scale = 0.6)
mf.network.nodes[oracle_sample_modified.id]["compound"].is_known = True
mf.re_annotate(mf.annotationEngine)
probs = mf.generate_probabilities()
img_prediction2 = mf.draw_prediction(probs, oracle_sample_known.id, show_legend=True, show_labels=True, shrink_labels=True, size=(1000, 1000), annotation_scale = 0.6)
fig, ax = plt.subplots(1, 2, figsize=(20, 10))
ax[0].imshow(img_prediction1)
ax[0].set_title('ModiFinder', fontsize=20)
ax[1].imshow(img_prediction2)
ax[1].set_title('ModiFinder Oracle', fontsize=20)
for a in ax:
a.axis('off')
plt.show()

```



![png](basics_files/basics_17_0.png)



### Create with your data
You can also create your compounds by passing a dictionary

Expand Down Expand Up @@ -167,7 +199,7 @@ plt.show()



![png](basics_files/basics_16_0.png)
![png](basics_files/basics_19_0.png)



Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 41 additions & 11 deletions modifinder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,46 @@
ModiFinder is a Python package for the identification of modifications in mass spectrometry data.
"""

from modifinder import convert
from modifinder.convert import *
__version__ = "1.5.0"

from modifinder.exceptions import *
import modifinder.convert as convert
from modifinder.classes import Compound, Spectrum, ModiFinder, EdgeDetail, MatchType, StructureMeta
from modifinder.convert import (
to_compound,
to_spectrum,
compound_to_dict,
spectrum_to_dict,
)
from modifinder.exceptions import (
ModiFinderError,
ModiFinderException,
ModiFinderNetworkError,
ModiFinderNotImplementedError,
ModiFinderNotSolvableError,
)
from modifinder.engines.alignment.CosineAlignmentEngine import CosineAlignmentEngine
from modifinder.engines.annotation.MAGMaAnnotationEngine import MAGMaAnnotationEngine
from modifinder.engines.evaluation.BasicEvaluationEngine import BasicEvaluationEngine

from modifinder import classes
from modifinder.classes import *

from modifinder import utilities
from modifinder.utilities import *

from modifinder import engines
from modifinder.engines import *
__all__ = [
"__version__",
"Compound",
"Spectrum",
"ModiFinder",
"EdgeDetail",
"MatchType",
"StructureMeta",
"convert",
"to_compound",
"to_spectrum",
"compound_to_dict",
"spectrum_to_dict",
"ModiFinderException",
"ModiFinderError",
"ModiFinderNetworkError",
"ModiFinderNotImplementedError",
"ModiFinderNotSolvableError",
"CosineAlignmentEngine",
"MAGMaAnnotationEngine",
"BasicEvaluationEngine",
]
2 changes: 1 addition & 1 deletion modifinder/classes/Compound.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# import modifinder as mf
from modifinder.classes.Spectrum import Spectrum
from modifinder.classes.StructureMeta import StructureMeta
from modifinder import convert
from .. import convert


class Compound:
Expand Down
2 changes: 1 addition & 1 deletion modifinder/classes/ModiFinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
This class Builds and maintains a network of compounds where the nodes are compounds (known and unknown)
"""

from modifinder import convert as convert
from .. import convert as convert
from modifinder.classes.Compound import Compound
from modifinder.classes.EdgeDetail import EdgeDetail, MatchType
from modifinder.engines import AlignmentEngine, AnnotationEngine
Expand Down
9 changes: 7 additions & 2 deletions modifinder/classes/Spectrum.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
from modifinder.utilities.gnps_types import adduct_mapping
import modifinder.utilities.general_utils as general_utils
from modifinder import convert
from .. import convert
import numpy as np
import bisect
import uuid
Expand Down Expand Up @@ -162,6 +162,12 @@ def update(self, peaks = None, peaks_json = None, mz=None, intensity=None, precu
if self.spectrum_id is None:
self.spectrum_id = str(uuid.uuid4())

# make sure types are correct
if self.precursor_mz is not None:
self.precursor_mz = float(self.precursor_mz)
if self.precursor_charge is not None:
self.precursor_charge = int(self.precursor_charge)


def __str__(self):
object_dict = self.__dict__
Expand Down Expand Up @@ -356,4 +362,3 @@ def get_peak_indexes(self, mz, mz_tolerance = 0.02, ppm_tolerance = 40.0, **kwar
return list(range(left_index, right_index))



Loading
Loading