Spaces:
Sleeping
Sleeping
File size: 3,616 Bytes
8f66d04 5b3b0dc 8f66d04 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | """
protein_feat.py — Protein feature processing for BioInteract Space demo.
Adapted from the training version: removed dependency on internal
src.utils.paths module so this file runs as a standalone module.
ESM-2 embeddings are computed on-the-fly via transformers in app.py.
"""
import os
import json
import torch
import numpy as np
from typing import Optional
# ============================================================
# Standard amino acid vocabulary
# ============================================================
AMINO_ACIDS = list('ACDEFGHIKLMNPQRSTVWY')
AA_TO_IDX = {aa: i for i, aa in enumerate(AMINO_ACIDS)}
UNK_AA_IDX = len(AMINO_ACIDS)
# ============================================================
# Residue-level physicochemical descriptors
# ============================================================
HYDROPHOBICITY = {
'A': 1.8, 'R': -4.5, 'N': -3.5, 'D': -3.5, 'C': 2.5,
'E': -3.5, 'Q': -3.5, 'G': -0.4, 'H': -3.2, 'I': 4.5,
'L': 3.8, 'K': -3.9, 'M': 1.9, 'F': 2.8, 'P': -1.6,
'S': -0.8, 'T': -0.7, 'W': -0.9, 'Y': -1.3, 'V': 4.2,
}
MOL_WEIGHT = {
'A': 89, 'R': 174, 'N': 132, 'D': 133, 'C': 121,
'E': 147, 'Q': 146, 'G': 75, 'H': 155, 'I': 131,
'L': 131, 'K': 146, 'M': 149, 'F': 165, 'P': 115,
'S': 105, 'T': 119, 'W': 204, 'Y': 181, 'V': 117,
}
CHARGE = {
'A': 0, 'R': 1, 'N': 0, 'D': -1, 'C': 0,
'E': -1, 'Q': 0, 'G': 0, 'H': 0.1, 'I': 0,
'L': 0, 'K': 1, 'M': 0, 'F': 0, 'P': 0,
'S': 0, 'T': 0, 'W': 0, 'Y': 0, 'V': 0,
}
POLARITY = {
'A': 0, 'R': 1, 'N': 1, 'D': 1, 'C': 0,
'E': 1, 'Q': 1, 'G': 0, 'H': 1, 'I': 0,
'L': 0, 'K': 1, 'M': 0, 'F': 0, 'P': 0,
'S': 1, 'T': 1, 'W': 0, 'Y': 1, 'V': 0,
}
def residue_physicochemical_features(sequence: str) -> torch.Tensor:
"""
Compute per-residue physicochemical feature vectors.
Returns tensor of shape (L, 4).
"""
features = []
for aa in sequence:
h = HYDROPHOBICITY.get(aa, 0.0) / 4.5
w = MOL_WEIGHT.get(aa, 130.0) / 204.0
c = CHARGE.get(aa, 0.0)
p = POLARITY.get(aa, 0.0)
features.append([h, w, c, p])
return torch.tensor(features, dtype=torch.float)
# ============================================================
# Domain annotation helpers
# ============================================================
DOMAIN_TYPES = [
'Pkinase', 'Pkinase_Tyr', '7tm_1', '7tm_2', '7tm_3',
'Trypsin', 'Peptidase_C1', 'Metallopep', 'zf-CCHH', 'SH2',
'SH3_1', 'PH', 'RRM_1', 'Ion_trans', 'Ank', 'WD40',
'LRR_1', 'EGF', 'Ig', 'ABC_tran', 'Hormone_recep',
'HATPase_c', 'Helicase_C', 'RVT_1', 'Topoisom_bac', 'NONE',
]
DOMAIN_TO_IDX = {d: i for i, d in enumerate(DOMAIN_TYPES)}
def residue_domain_labels(
sequence_length: int,
domain_info: Optional[dict] = None
) -> torch.LongTensor:
"""
Assign each residue a domain type index.
Returns LongTensor of shape (L,).
Residues without annotation get the 'NONE' label.
"""
labels = torch.full(
(sequence_length,), DOMAIN_TO_IDX['NONE'], dtype=torch.long
)
if domain_info is not None and 'domains' in domain_info:
for dom in domain_info['domains']:
dom_type = dom.get('type', 'NONE')
if dom_type not in DOMAIN_TO_IDX:
dom_type = 'NONE'
idx = DOMAIN_TO_IDX[dom_type]
start = dom.get('start', 0)
end = min(dom.get('end', sequence_length), sequence_length)
labels[start:end] = idx
return labels
|