Source code for qsarkit.representation.fingerprints._maccs
"""MACCS structural keys."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Optional, Sequence
import numpy as np
from qsarkit.representation.fingerprints._base import BaseFingerprintTransformer
if TYPE_CHECKING: # pragma: no cover
from rdkit.Chem import Mol
#: RDKit emits 167 bits: the 166 public MACCS keys plus an unused bit 0.
MACCS_N_BITS = 167
[docs]
class MACCSKeysFingerprint(BaseFingerprintTransformer):
"""166 public MACCS structural keys (RDKit implementation, 167 bits).
Each bit is a hand-curated SMARTS substructure query ("has a carbonyl",
"has 4 nitrogens", ...). Unlike the hashed fingerprints in this module
MACCS keys are directly interpretable, which makes them useful for
explainable QSAR and for coarse similarity screening.
Parameters
----------
drop_unused_bit : bool, default False
RDKit returns 167 bits, index 0 being an always-off placeholder so
that key ``k`` lands at index ``k``. Set to ``True`` to drop it and
return exactly the 166 defined keys.
Notes
-----
RDKit implements the 166 *public* MACCS key definitions; a handful of
keys that require proprietary MDL features are approximated, as
documented in ``rdkit.Chem.MACCSkeys``. The fingerprint has no
``n_bits``/``radius``/``use_counts`` parameters because the key set is
fixed and binary by definition.
Examples
--------
>>> from rdkit import Chem
>>> from qsarkit.representation.fingerprints import MACCSKeysFingerprint
>>> MACCSKeysFingerprint().fit_transform([Chem.MolFromSmiles("CCO")]).shape
(1, 167)
References
----------
- Durant, J. L., Leland, B. A., Henry, D. R. & Nourse, J. G. (2002).
"Reoptimization of MDL Keys for Use in Drug Discovery."
J. Chem. Inf. Comput. Sci., 42(6), 1273-1280.
https://doi.org/10.1021/ci010132r
- RDKit ``rdkit.Chem.MACCSkeys`` documentation:
https://www.rdkit.org/docs/source/rdkit.Chem.MACCSkeys.html
"""
def __init__(self, drop_unused_bit: bool = False):
self.drop_unused_bit = drop_unused_bit
@property
def n_features_out(self) -> int:
return MACCS_N_BITS - 1 if self.drop_unused_bit else MACCS_N_BITS
def _fingerprint(self, mol: "Mol") -> np.ndarray:
from rdkit import DataStructs
from rdkit.Chem import MACCSkeys
bit_vector = MACCSkeys.GenMACCSKeys(mol)
array = np.zeros(MACCS_N_BITS, dtype=np.uint8)
DataStructs.ConvertToNumpyArray(bit_vector, array)
return array[1:] if self.drop_unused_bit else array
[docs]
def get_feature_names_out(
self, input_features: Optional[Sequence[str]] = None
) -> np.ndarray:
start = 1 if self.drop_unused_bit else 0
return np.asarray(
[f"MACCS_{i}" for i in range(start, MACCS_N_BITS)], dtype=object
)