bandera(“[2/9] MINERÍA DE BIOACTIVIDAD (actividades ChEMBL -> pIC50)”) def pull_activities(tid, cap): url, filas = f”{BASE}/actividad”, []
params = {“target_chembl_id”: tid, “standard_type”: “IC50”, “pchembl_value__isnull”: “false”, “limit”: 1000, “format”: “json”} js = http_json(url, params) páginas = 0 mientras que js y páginas < 60: rows.extend(js.get("activities", [])) páginas += 1 si len(filas) >= cap: break nxt = js.get(“page_meta”, {}).get(“next”) si no nxt: break nurl = nxt if nxt.startswith(“http”) else “https://www.ebi.ac.uk” + nxt js = http_json(nurl) devolver filas[:cap]
raw = pull_activities(target_id, MAX_ACTIVITIES) print(f” Extraje {len(raw)} registros IC50 sin procesar con un valor pChEMBL seleccionado.”) recs = []
para a en raw: smi, pv = a.get(“canonical_smiles”), a.get(“pchembl_value”) si no es smi o pv en (Ninguno, “”): continuar si a.get(“standard_relation”) != “=”: continuar si a.get(“standard_units”) no en (“nM”, Ninguno): continuar intente: pv = float(pv) excepto Excepción: continuar recs.append({“chembl_id”: a.get(“molecule_chembl_id”), “smiles”: smi, “pIC50”: pv}) raw_df = pd.DataFrame(recs, columns=[“chembl_id”, “smiles”, “pIC50″]) print(f” Después de los filtros de calidad: {len(raw_df)} mediciones.”) if len(raw_df) == 0: print(“\n STOP: no se recuperaron datos IC50 utilizables para este objetivo.\n” ” Solución: establezca TARGET_CHEMBL_ID en un objetivo que tenga datos inhibidores\n” ” (por ejemplo, CHEMBL203=EGFR, CHEMBL5251=BTK, CHEMBL2971=JAK2),\n” ” o configure TARGET_CHEMBL_ID=\”\” para resolver automáticamente TARGET_QUERY por volumen de datos.”) rise SystemExit(“No hay datos de bioactividad para el objetivo seleccionado.”) banner(“[3/9] CURACIÓN MOLECULAR (estandarizar, desalar, agregar)”) _lfc = rdMolStandardize.LargestFragmentChooser() si _HAS_STD else Ninguno _unc = rdMolStandardize.Uncharger() if _HAS_STD else Ninguno def estandarizar(smi): m = Chem.MolFromSmiles(smi) si m es Ninguno: devolver Ninguno, Ninguno intentar: si _HAS_STD: m = _lfc.choose(m); m = _unc.uncharge(m) else: frags = Chem.GetMolFrags(m, asMols=True, sanitizeFrags=True) if frags: m = max(frags, key=lambda x: x.GetNumHeavyAtoms()) devuelve m, Chem.MolToSmiles(m) excepto Excepción: devolver Ninguno, Ninguno canon, keep_mol = []{} para _, r en raw_df.iterrows(): m, cs = estandarizar(r[“smiles”]) si cs es Ninguno o m.GetNumHeavyAtoms() < 6: continúe canon.append({"smiles": cs, "pIC50": r["pIC50"]"chembl_id":r["chembl_id"]}) mantener_mol[cs] = m cdf = pd.DataFrame(canon, columnas=["smiles", "pIC50", "chembl_id"]) datos = (cdf.groupby("sonrisas") .agg(pIC50=("pIC50", "median"), n=("pIC50", "tamaño"), chembl_id=("chembl_id", "first")).reset_index()) if len(data) > MAX_UNIQUE: data = data.sample(MAX_UNIQUE, random_state=RANDOM_STATE).reset_index(drop=True) datos[“mol”] = datos[“smiles”].map(keep_mol) n_active = int((datos[“pIC50″] >= ACTIVE_PIC50).sum()) print(f” Moléculas seleccionadas únicas: {len(data)}”) print(f” Activos potentes (IC50<=100nM): {n_active} ({100*n_active/len(data):.1f}%)") print(f" Rango pIC50: {data.pIC50.min():.2f} - {data.pIC50.max():.2f} " f"(mediana {data.pIC50.median():.2f})") mfpgen = rdFingerprintGenerator.GetMorganGenerator(radio=RADIUS, fpSize=NBITS) DESC = [("MolWt", Descriptors.MolWt), ("MolLogP", Descriptors.MolLogP), ("TPSA", Descriptors.TPSA), ("HBD", Descriptors.NumHDonors), ("HBA", Descriptors.NumHAcceptors), ("RotB", Descriptors.NumRotatableBonds), ("AromRings", Descriptors.NumAromaticRings), ("FracCSP3", Descriptors.FractionCSP3), ("HeavyAtoms", Descriptors.HeavyAtomCount), ("NumRings", lambda m: rdMolDescriptors.CalcNumRings(m))] FEAT_NAMES = [f"bit_{i}" for i in range(NBITS)] + [n for n, _ in DESC] def fp_array(m): a = np.zeros((NBITS,), dtype=np.int8) DataStructs.ConvertToNumpyArray(mfpgen.GetFingerprint(m), a) devuelve a def featurize(mols): Xb = np.zeros((len(mols), NBITS), dtype=np.int8) Xd = np.zeros((len(mols), len(DESC)), dtype=np.float32) para i, m en enumerate(mols): Xb[i] = fp_array(m) para j, (_, fn) en enumerar(DESC): prueba: Xd[i, j] = fn(m) excepto Excepción: Xd[i, j] = 0.0 devolver np.nan_to_num(np.hstack([Xb, Xd]).astype(np.float32)) X = caracterizar(lista(datos["mol"])) y = datos["pIC50"].values print(f" Matriz de características: {X.shape[0]} moléculas x {forma X[1]} presenta "f"({NBITS} bits ECFP + {len(DESC)} descriptores)")
params = {“target_chembl_id”: tid, “standard_type”: “IC50”, “pchembl_value__isnull”: “false”, “limit”: 1000, “format”: “json”} js = http_json(url, params) páginas = 0 mientras que js y páginas < 60: rows.extend(js.get("activities", [])) páginas += 1 si len(filas) >= cap: break nxt = js.get(“page_meta”, {}).get(“next”) si no nxt: break nurl = nxt if nxt.startswith(“http”) else “https://www.ebi.ac.uk” + nxt js = http_json(nurl) devolver filas[:cap]
raw = pull_activities(target_id, MAX_ACTIVITIES) print(f” Extraje {len(raw)} registros IC50 sin procesar con un valor pChEMBL seleccionado.”) recs = []
para a en raw: smi, pv = a.get(“canonical_smiles”), a.get(“pchembl_value”) si no es smi o pv en (Ninguno, “”): continuar si a.get(“standard_relation”) != “=”: continuar si a.get(“standard_units”) no en (“nM”, Ninguno): continuar intente: pv = float(pv) excepto Excepción: continuar recs.append({“chembl_id”: a.get(“molecule_chembl_id”), “smiles”: smi, “pIC50”: pv}) raw_df = pd.DataFrame(recs, columns=[“chembl_id”, “smiles”, “pIC50″]) print(f” Después de los filtros de calidad: {len(raw_df)} mediciones.”) if len(raw_df) == 0: print(“\n STOP: no se recuperaron datos IC50 utilizables para este objetivo.\n” ” Solución: establezca TARGET_CHEMBL_ID en un objetivo que tenga datos inhibidores\n” ” (por ejemplo, CHEMBL203=EGFR, CHEMBL5251=BTK, CHEMBL2971=JAK2),\n” ” o configure TARGET_CHEMBL_ID=\”\” para resolver automáticamente TARGET_QUERY por volumen de datos.”) rise SystemExit(“No hay datos de bioactividad para el objetivo seleccionado.”) banner(“[3/9] CURACIÓN MOLECULAR (estandarizar, desalar, agregar)”) _lfc = rdMolStandardize.LargestFragmentChooser() si _HAS_STD else Ninguno _unc = rdMolStandardize.Uncharger() if _HAS_STD else Ninguno def estandarizar(smi): m = Chem.MolFromSmiles(smi) si m es Ninguno: devolver Ninguno, Ninguno intentar: si _HAS_STD: m = _lfc.choose(m); m = _unc.uncharge(m) else: frags = Chem.GetMolFrags(m, asMols=True, sanitizeFrags=True) if frags: m = max(frags, key=lambda x: x.GetNumHeavyAtoms()) devuelve m, Chem.MolToSmiles(m) excepto Excepción: devolver Ninguno, Ninguno canon, keep_mol = []{} para _, r en raw_df.iterrows(): m, cs = estandarizar(r[“smiles”]) si cs es Ninguno o m.GetNumHeavyAtoms() < 6: continúe canon.append({"smiles": cs, "pIC50": r["pIC50"]"chembl_id":r["chembl_id"]}) mantener_mol[cs] = m cdf = pd.DataFrame(canon, columnas=["smiles", "pIC50", "chembl_id"]) datos = (cdf.groupby("sonrisas") .agg(pIC50=("pIC50", "median"), n=("pIC50", "tamaño"), chembl_id=("chembl_id", "first")).reset_index()) if len(data) > MAX_UNIQUE: data = data.sample(MAX_UNIQUE, random_state=RANDOM_STATE).reset_index(drop=True) datos[“mol”] = datos[“smiles”].map(keep_mol) n_active = int((datos[“pIC50″] >= ACTIVE_PIC50).sum()) print(f” Moléculas seleccionadas únicas: {len(data)}”) print(f” Activos potentes (IC50<=100nM): {n_active} ({100*n_active/len(data):.1f}%)") print(f" Rango pIC50: {data.pIC50.min():.2f} - {data.pIC50.max():.2f} " f"(mediana {data.pIC50.median():.2f})") mfpgen = rdFingerprintGenerator.GetMorganGenerator(radio=RADIUS, fpSize=NBITS) DESC = [("MolWt", Descriptors.MolWt), ("MolLogP", Descriptors.MolLogP), ("TPSA", Descriptors.TPSA), ("HBD", Descriptors.NumHDonors), ("HBA", Descriptors.NumHAcceptors), ("RotB", Descriptors.NumRotatableBonds), ("AromRings", Descriptors.NumAromaticRings), ("FracCSP3", Descriptors.FractionCSP3), ("HeavyAtoms", Descriptors.HeavyAtomCount), ("NumRings", lambda m: rdMolDescriptors.CalcNumRings(m))] FEAT_NAMES = [f"bit_{i}" for i in range(NBITS)] + [n for n, _ in DESC] def fp_array(m): a = np.zeros((NBITS,), dtype=np.int8) DataStructs.ConvertToNumpyArray(mfpgen.GetFingerprint(m), a) devuelve a def featurize(mols): Xb = np.zeros((len(mols), NBITS), dtype=np.int8) Xd = np.zeros((len(mols), len(DESC)), dtype=np.float32) para i, m en enumerate(mols): Xb[i] = fp_array(m) para j, (_, fn) en enumerar(DESC): prueba: Xd[i, j] = fn(m) excepto Excepción: Xd[i, j] = 0.0 devolver np.nan_to_num(np.hstack([Xb, Xd]).astype(np.float32)) X = caracterizar(lista(datos["mol"])) y = datos["pIC50"].values print(f" Matriz de características: {X.shape[0]} moléculas x {forma X[1]} presenta "f"({NBITS} bits ECFP + {len(DESC)} descriptores)")