Source code for qsarkit.representation.descriptors._fragments
"""SMARTS-based functional-group fragment counts (``rdkit.Chem.Fragments``)."""
from __future__ import annotations
from typing import TYPE_CHECKING, Callable, List, Optional, Sequence, Tuple
from qsarkit.representation.descriptors._base import BaseDescriptorTransformer
if TYPE_CHECKING: # pragma: no cover
from rdkit.Chem import Mol
__all__ = ["FragmentDescriptors"]
[docs]
class FragmentDescriptors(BaseDescriptorTransformer):
"""SMARTS-based functional-group fragment counts.
``rdkit.Chem.Fragments`` defines roughly 85 hand-curated SMARTS
substructure counters (``fr_Al_COOH`` aliphatic carboxylic acid,
``fr_halogen``, ``fr_benzene``, ``fr_epoxide``, ...), each counting how
many times a specific functional group occurs in the molecule. They
are the interpretable, medicinal-chemistry counterpart to a hashed
fingerprint and are frequently used as toxicophore/reactivity alerts
or as an interpretable QSAR feature block.
Parameters
----------
fragment_names : sequence of str, optional
Names of the ``fr_*`` functions to compute (must exist on
``rdkit.Chem.Fragments``). ``None`` (default) computes every
``fr_*`` function RDKit registers, in alphabetical order.
missing_value : float, default nan
Value substituted when a fragment counter raises for a given
molecule.
Examples
--------
>>> from rdkit import Chem
>>> from qsarkit.representation.descriptors import FragmentDescriptors
>>> fd = FragmentDescriptors(fragment_names=["fr_benzene", "fr_halogen"])
>>> fd.fit_transform([Chem.MolFromSmiles("c1ccccc1Cl")]).tolist()
[[1.0, 1.0]]
References
----------
- Landrum, G. RDKit: Open-source cheminformatics. https://www.rdkit.org
- RDKit ``rdkit.Chem.Fragments`` documentation:
https://www.rdkit.org/docs/source/rdkit.Chem.Fragments.html
- Ertl, P. (2017). "An Algorithm to Identify Functional Groups in
Organic Molecules." J. Cheminform., 9, 36.
https://doi.org/10.1186/s13321-017-0225-z
"""
def __init__(
self,
fragment_names: Optional[Sequence[str]] = None,
missing_value: float = float("nan"),
) -> None:
self.fragment_names = fragment_names
self.missing_value = missing_value
def _descriptor_functions(self) -> List[Tuple[str, Callable[["Mol"], float]]]:
from rdkit.Chem import Fragments
catalogue = {
name: getattr(Fragments, name)
for name in dir(Fragments)
if name.startswith("fr_") and callable(getattr(Fragments, name))
}
if self.fragment_names is None:
return [(name, catalogue[name]) for name in sorted(catalogue)]
missing = [n for n in self.fragment_names if n not in catalogue]
if missing:
raise ValueError(
f"Unknown fragment descriptor name(s): {missing!r}. See "
"rdkit.Chem.Fragments for valid 'fr_*' names."
)
return [(name, catalogue[name]) for name in self.fragment_names]