Molecular Descriptors¶
The fr5.descriptors module provides comprehensive tools for generating molecular descriptors for various chemical systems. These descriptors serve as numerical representations of molecular structures and properties, enabling machine learning models to work with chemical data.
Overview¶
Molecular descriptors are essential for:
- Structure-property relationships (QSPR/QSAR modeling)
- Chemical similarity analysis and clustering
- Machine learning feature engineering
- Virtual screening of chemical libraries
- Property prediction and optimization
Supported Systems¶
🧪 Ionic Liquids (ILs)¶
Generate descriptors for ionic liquid systems consisting of cations and anions:
from fr5.descriptors import generate_il_descriptors
# Single ionic liquid
il_descriptors = generate_il_descriptors(
cation='EMIM', # 1-ethyl-3-methylimidazolium
anion='BF4', # tetrafluoroborate
temperature=298.15 # Optional: temperature-dependent descriptors
)
print(f"Generated {len(il_descriptors)} descriptors")
print(f"Descriptor names: {list(il_descriptors.keys())[:5]}...") # First 5
Available IL Descriptor Types: - Fragment-based descriptors: Functional group counts and connectivity - Property-based descriptors: Estimated physicochemical properties - Topological descriptors: Molecular graph properties - Electronic descriptors: Charge distribution and electronic properties
🏗️ Metal-Organic Frameworks (MOFs)¶
Generate descriptors for MOF structures:
from fr5.descriptors import generate_mof_descriptors
# From MOF structure file
mof_descriptors = generate_mof_descriptors(
structure_file="HKUST-1.cif", # CIF file path
include_pore_descriptors=True, # Include pore analysis
include_electronic=True # Include electronic properties
)
print(f"MOF descriptors: {list(mof_descriptors.keys())[:10]}...")
Available MOF Descriptor Types: - Geometric descriptors: Pore size, surface area, volume - Topological descriptors: Network topology and connectivity - Chemical descriptors: Elemental composition and bonding - Electronic descriptors: Band gap, density of states
🧬 General Molecules¶
Generate RDKit-based descriptors for arbitrary molecules:
from fr5.descriptors import generate_molecular_descriptors
# From SMILES string
mol_descriptors = generate_molecular_descriptors(
smiles="CCO", # Ethanol
descriptor_types=['morgan', 'rdkit', 'physicochemical']
)
# From molecule file
mol_descriptors = generate_molecular_descriptors(
mol_file="molecule.sdf",
descriptor_types=['all'] # Generate all available descriptors
)
Descriptor Types in Detail¶
Fragment-Based Descriptors¶
Count occurrences of structural fragments:
from fr5.descriptors import FragmentDescriptors
descriptor_gen = FragmentDescriptors()
# IL fragments
il_fragments = descriptor_gen.get_il_fragments(
cation="EMIM",
anion="BF4"
)
# Print fragment counts
for fragment, count in il_fragments.items():
if count > 0:
print(f"{fragment}: {count}")
Common IL Fragments:
- aromatic_rings: Number of aromatic rings
- alkyl_chains: Alkyl chain lengths
- functional_groups: OH, NH2, COOH, etc.
- heteroatoms: N, O, S, P counts
Property-Based Descriptors¶
Estimate physicochemical properties:
from fr5.descriptors import PropertyDescriptors
prop_gen = PropertyDescriptors()
# Calculate molecular properties
properties = prop_gen.calculate_properties(
cation_smiles="CCn1ccnc1", # EMIM cation
anion_smiles="[B-](F)(F)(F)F", # BF4 anion
temperature=298.15
)
# Common properties
print(f"Molecular weight: {properties['mw']:.2f}")
print(f"LogP: {properties['logp']:.2f}")
print(f"Polar surface area: {properties['psa']:.2f}")
Topological Descriptors¶
Analyze molecular connectivity:
from fr5.descriptors import TopologicalDescriptors
topo_gen = TopologicalDescriptors()
# Calculate topological indices
topo_descriptors = topo_gen.calculate_indices(
smiles="CCn1ccnc1" # EMIM cation
)
# Wiener index, Zagreb indices, etc.
for descriptor, value in topo_descriptors.items():
print(f"{descriptor}: {value}")
Advanced Features¶
Autoencoder-Based Descriptors¶
Generate low-dimensional latent representations:
from fr5.descriptors import AutoencoderDescriptors
# Load pre-trained autoencoder
ae_gen = AutoencoderDescriptors.load_pretrained(
model_path="models/il_autoencoder.pth"
)
# Generate latent descriptors
latent_descriptors = ae_gen.encode(
molecular_descriptors=high_dim_descriptors,
latent_dim=32 # Compressed to 32 dimensions
)
print(f"Compressed from {len(high_dim_descriptors)} to {len(latent_descriptors)} dimensions")
Custom Descriptor Sets¶
Define custom descriptor combinations:
from fr5.descriptors import DescriptorSet
# Create custom descriptor set for specific application
custom_set = DescriptorSet(
name="viscosity_prediction",
descriptors=[
"fragment_aromatic_rings",
"fragment_alkyl_chains",
"property_mw",
"property_logp",
"topological_wiener"
]
)
# Generate only selected descriptors
selected_descriptors = custom_set.generate(
cation="BMIM",
anion="Tf2N"
)
Batch Processing¶
Process multiple molecules efficiently:
from fr5.descriptors import batch_generate_descriptors
# List of ionic liquids
il_list = [
{"cation": "EMIM", "anion": "BF4"},
{"cation": "BMIM", "anion": "PF6"},
{"cation": "HMIM", "anion": "Tf2N"}
]
# Batch generation with parallel processing
all_descriptors = batch_generate_descriptors(
molecules=il_list,
descriptor_types=["fragment", "property"],
n_jobs=4, # Parallel workers
verbose=True
)
print(f"Generated descriptors for {len(all_descriptors)} molecules")
Data Management¶
Descriptor Databases¶
Store and manage descriptor datasets:
from fr5.descriptors import DescriptorDatabase
# Create database
db = DescriptorDatabase("descriptors.db")
# Store descriptors
db.store_descriptors(
molecule_id="EMIM_BF4",
descriptors=il_descriptors,
metadata={
"cation": "EMIM",
"anion": "BF4",
"temperature": 298.15,
"generated_date": "2024-08-12"
}
)
# Query descriptors
stored_descriptors = db.query_descriptors(
filters={"cation": "EMIM", "temperature": 298.15}
)
Export and Import¶
Save descriptors in various formats:
from fr5.descriptors import export_descriptors, import_descriptors
# Export to CSV
export_descriptors(
descriptors_dict=all_descriptors,
output_path="descriptors.csv",
format="csv",
include_metadata=True
)
# Export to HDF5 for large datasets
export_descriptors(
descriptors_dict=all_descriptors,
output_path="descriptors.h5",
format="hdf5",
compression="gzip"
)
# Import back
loaded_descriptors = import_descriptors("descriptors.csv")
Performance Optimization¶
Caching¶
Enable caching for expensive calculations:
from fr5.descriptors import enable_caching
# Enable persistent caching
enable_caching(
cache_dir="./descriptor_cache",
cache_size_mb=1000, # 1GB cache
compression=True
)
# Subsequent calls will use cached results
descriptors1 = generate_il_descriptors("EMIM", "BF4") # Calculated
descriptors2 = generate_il_descriptors("EMIM", "BF4") # From cache
Memory Management¶
Handle large descriptor sets efficiently:
from fr5.descriptors import DescriptorStream
# Stream processing for large datasets
descriptor_stream = DescriptorStream(
molecule_list=large_molecule_list,
batch_size=100, # Process 100 at a time
descriptor_types=["fragment", "property"]
)
# Process in batches
for batch_idx, batch_descriptors in enumerate(descriptor_stream):
print(f"Processing batch {batch_idx}: {len(batch_descriptors)} molecules")
# Process batch...
Validation and Quality Control¶
Descriptor Validation¶
Check descriptor quality and consistency:
from fr5.descriptors import validate_descriptors
validation_report = validate_descriptors(
descriptors=all_descriptors,
checks=[
"missing_values",
"constant_features",
"correlated_features",
"outliers"
]
)
print(f"Validation results:")
for check, result in validation_report.items():
print(f"- {check}: {result['status']}")
if result['warnings']:
for warning in result['warnings']:
print(f" Warning: {warning}")
Feature Selection¶
Select most informative descriptors:
from fr5.descriptors import select_descriptors
# Select descriptors based on importance
selected_descriptors = select_descriptors(
X=descriptor_matrix,
y=target_values,
method="mutual_info", # mutual_info, f_regression, rfe
n_features=50, # Select top 50 features
cv_folds=5 # Cross-validation folds
)
print(f"Selected {len(selected_descriptors)} most informative descriptors")
Integration with Machine Learning¶
Scikit-learn Compatibility¶
Descriptors work seamlessly with scikit-learn:
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import r2_score
# Prepare data
X = np.array([list(desc.values()) for desc in all_descriptors])
y = target_properties
# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Train model
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Evaluate
y_pred = model.predict(X_test)
r2 = r2_score(y_test, y_pred)
print(f"Model R²: {r2:.3f}")
Feature Importance Analysis¶
Analyze descriptor importance:
from fr5.descriptors import analyze_feature_importance
# Get feature names
feature_names = list(all_descriptors[0].keys())
# Analyze importance
importance_analysis = analyze_feature_importance(
model=trained_model,
feature_names=feature_names,
X_test=X_test,
y_test=y_test
)
# Plot top features
importance_analysis.plot_top_features(n_features=20)
Best Practices¶
1. Descriptor Selection¶
- Start with domain knowledge to select relevant descriptor types
- Use feature selection methods to reduce dimensionality
- Validate descriptor importance for your specific application
2. Data Preprocessing¶
- Check for missing values and outliers
- Scale descriptors appropriately for your ML algorithm
- Remove constant and highly correlated features
3. Validation¶
- Always validate descriptors on held-out test sets
- Use cross-validation for robust performance estimates
- Check for data leakage between train and test sets
4. Performance¶
- Use caching for repeated calculations
- Consider batch processing for large datasets
- Profile code to identify bottlenecks
Troubleshooting¶
Common Issues¶
Memory errors with large datasets: Use streaming or batch processing
Slow descriptor generation: Enable caching and parallel processing
Missing values: Check input molecule validity and descriptor implementation
Poor model performance: Try feature selection and different descriptor types