{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": [
"# CHEBI Slimmer\n",
"\n",
"Creates a simplified version of CHEBI by conflating all members of a conjugate clique."
],
"id": "882b63db7629f552"
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"## Initial setup\n",
"\n",
"Imports and use OAK to get an adapter to CHEBI sqlite database."
],
"id": "74787c2d9005dc21"
},
{
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2024-11-16T01:30:31.668987Z",
"start_time": "2024-11-16T01:30:29.432380Z"
}
},
"cell_type": "code",
"source": [
"from typing import Optional, List\n",
"from collections import defaultdict\n",
"\n",
"import pandas as pd\n",
"\n",
"from oaklib import get_adapter\n",
"from oaklib.utilities.obograph_utils import reflexive\n",
"from tests.test_converters.test_obo_format import canonical_path\n",
"\n",
"chebi = get_adapter(\"sqlite:obo:chebi\")\n",
"# session = get_adapter(\"sqlite:obo:chebi\").session\n",
"session = chebi.session"
],
"id": "initial_id",
"outputs": [],
"execution_count": 1
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:30:31.718018Z",
"start_time": "2024-11-16T01:30:31.710177Z"
}
},
"cell_type": "code",
"source": [
"from oaklib.datamodels.vocabulary import IS_A, HAS_PART\n",
"from oaklib.interfaces import OboGraphInterface\n",
"\n",
"assert isinstance(chebi, OboGraphInterface)\n"
],
"id": "237c5d0906ac560c",
"outputs": [],
"execution_count": 2
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Set up vocabulary constants",
"id": "64e69d5dbfbdc79b"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:30:31.795374Z",
"start_time": "2024-11-16T01:30:31.792642Z"
}
},
"cell_type": "code",
"source": [
"# Relations\n",
"CBO = \"obo:chebi#is_conjugate_base_of\"\n",
"CAO = \"obo:chebi#is_conjugate_acid_of\"\n",
"TAUTOMER_OF = \"obo:chebi#is_tautomer_of\"\n",
"ENANTIOMER_OF = \"obo:chebi#is_enantiomer_of\"\n",
"HAS_ROLE = \"RO:0000087\""
],
"id": "703ad334757e40df",
"outputs": [],
"execution_count": 3
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:30:31.831712Z",
"start_time": "2024-11-16T01:30:31.829565Z"
}
},
"cell_type": "code",
"source": [
"\n",
"CHEMICAL_ENTITY = \"CHEBI:24431\""
],
"id": "4538a10374439f3b",
"outputs": [],
"execution_count": 4
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:30:31.926589Z",
"start_time": "2024-11-16T01:30:31.885548Z"
}
},
"cell_type": "code",
"source": [
"# modify this for testing\n",
"# ROOT = AMINO_ACID\n",
"ROOT = CHEMICAL_ENTITY"
],
"id": "d9a11b7b7e44919a",
"outputs": [],
"execution_count": 5
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:30:32.257077Z",
"start_time": "2024-11-16T01:30:32.249103Z"
}
},
"cell_type": "code",
"source": [
"AMINO_ACID = \"CHEBI:33709\"\n",
"AMINO_ACID_ANION = \"CHEBI:37022\"\n",
"ION = \"CHEBI:24870\" \n",
"ALPHA_AMINO_ACID = \"CHEBI:33704\"\n",
"ALPHA_AMINO_ACID_ANION = \"CHEBI:33558\"\n",
"ALPHA_AMINO_ACID_ZWITTERION = \"CHEBI:78608\"\n",
"CYSTEINE_ZWITTERION = \"CHEBI:35237\"\n",
"L_CYSTEINE_ZWITTERION = \"CHEBI:35235\"\n",
"CYSTEINATE_1_MINUS = \"CHEBI:32456\"\n",
"CYSTEINIUM = \"CHEBI:32458\"\n",
"CORD_E = \"CHEBI:213754\"\n",
"AAAE = \"CHEBI:46874\"\n",
"CITRIC_ACID = \"CHEBI:30769\"\n",
"\n",
"AMMONIA=\"CHEBI:16134\"\n",
"AMMONIUM=\"CHEBI:28938\"\n",
"AZANIDE=\"CHEBI:29337\"\n",
"HYRDRIDONITRATE_2M = \"CHEBI:29340\"\n",
"PECTIN = \"CHEBI:17309\"\n",
"WATER = \"CHEBI:15377\"\n",
"\n",
"CONJ_EXCLUDES = {\n",
" AMMONIA, AMMONIUM, AZANIDE, HYRDRIDONITRATE_2M, PECTIN\n",
"}\n",
"\n"
],
"id": "6b59749828d83578",
"outputs": [],
"execution_count": 6
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## All labels",
"id": "16ad1bd007f1dd6f"
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:30:51.852164Z",
"start_time": "2024-11-16T01:30:32.346045Z"
}
},
"cell_type": "code",
"source": [
"labels = {k: v for k, v in chebi.labels(chebi.entities())}\n",
"len(labels)"
],
"id": "bb9da82d69bf53ff",
"outputs": [
{
"data": {
"text/plain": [
"200959"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 7
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Mappings",
"id": "ab80728e19185a45"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:01.875034Z",
"start_time": "2024-11-16T01:30:51.888994Z"
}
},
"cell_type": "code",
"source": [
"from semsql.sqla.semsql import Statements, HasDbxrefStatement\n",
"q = session.query(HasDbxrefStatement)\n",
"xrefs = defaultdict(list)\n",
"for row in q:\n",
" if row.subject.startswith(\"CHEBI:\"):\n",
" xrefs[row.subject].append(row.value)\n",
"len(xrefs)"
],
"id": "9f9bba0c78606429",
"outputs": [
{
"data": {
"text/plain": [
"161158"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 8
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:03.840582Z",
"start_time": "2024-11-16T01:31:01.983823Z"
}
},
"cell_type": "code",
"source": [
"q = session.query(Statements).filter(Statements.predicate == \"obo:chebi/inchi\")\n",
"inchis = {row.subject: row.value for row in q}\n",
"len(inchis)"
],
"id": "8bb68491d1485660",
"outputs": [
{
"data": {
"text/plain": [
"177528"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 9
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:03.966190Z",
"start_time": "2024-11-16T01:31:03.962683Z"
}
},
"cell_type": "code",
"source": [
"S3H = \"CHEBI:113373\"\n",
"inchis[S3H]"
],
"id": "12371488c8dad5e6",
"outputs": [
{
"data": {
"text/plain": [
"'InChI=1S/C4H8O3.Na/c1-3(5)2-4(6)7;/h3,5H,2H2,1H3,(H,6,7);/q;+1/p-1'"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 10
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Various Relationships\n",
"id": "a56da86f0437f78f"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:07.972053Z",
"start_time": "2024-11-16T01:31:04.161443Z"
}
},
"cell_type": "code",
"source": [
"PMAP = {ENANTIOMER_OF: \"RO:0018039\"}\n",
"preserved_rels = list(chebi.relationships(predicates=[HAS_PART, HAS_ROLE, ENANTIOMER_OF]))\n",
"preserved_rels_by_subject = defaultdict(list)\n",
"for s, p, o in preserved_rels:\n",
" p_mapped = PMAP.get(p, p)\n",
" preserved_rels_by_subject[s].append((p_mapped, o))\n",
"assert len(preserved_rels_by_subject) > 1000"
],
"id": "1b818c23743b2dd8",
"outputs": [],
"execution_count": 11
},
{
"metadata": {},
"cell_type": "markdown",
"source": "",
"id": "79ff715a10f5ee8c"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Retrieve all Charge States\n",
"id": "f7763be85ede7b0a"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:08.113257Z",
"start_time": "2024-11-16T01:31:08.111324Z"
}
},
"cell_type": "code",
"source": "",
"id": "2c57db8dcdc42d30",
"outputs": [],
"execution_count": null
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:10.946952Z",
"start_time": "2024-11-16T01:31:08.254299Z"
}
},
"cell_type": "code",
"source": [
"from semsql.sqla.semsql import Statements, HasDbxrefStatement\n",
"\n",
"session = get_adapter(\"sqlite:obo:chebi\").session\n",
"q = session.query(Statements).filter(Statements.predicate == \"obo:chebi/charge\")\n",
"charges = {row.subject: int(row.value) for row in q if row.value is not None}"
],
"id": "481dce997828261a",
"outputs": [],
"execution_count": 12
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:11.099072Z",
"start_time": "2024-11-16T01:31:11.095765Z"
}
},
"cell_type": "code",
"source": "len(charges)",
"id": "5ee9f24a01966ac0",
"outputs": [
{
"data": {
"text/plain": [
"189127"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 13
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:11.252359Z",
"start_time": "2024-11-16T01:31:11.249651Z"
}
},
"cell_type": "code",
"source": "assert charges[L_CYSTEINE_ZWITTERION] == 0",
"id": "6c387597c60290a2",
"outputs": [],
"execution_count": 14
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:11.402898Z",
"start_time": "2024-11-16T01:31:11.400351Z"
}
},
"cell_type": "code",
"source": "assert charges[CYSTEINATE_1_MINUS] == -1",
"id": "58b92eac4c3aea65",
"outputs": [],
"execution_count": 15
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:11.553674Z",
"start_time": "2024-11-16T01:31:11.551180Z"
}
},
"cell_type": "code",
"source": "assert charges[CITRIC_ACID] == 0",
"id": "c55e6cb12a37ea60",
"outputs": [],
"execution_count": 16
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:11.704268Z",
"start_time": "2024-11-16T01:31:11.701723Z"
}
},
"cell_type": "code",
"source": "assert AMINO_ACID_ANION not in charges, \"X anion terms are agnostic to a SPECIFIC charge\"",
"id": "e7d7762146973a0e",
"outputs": [],
"execution_count": 17
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:12.218497Z",
"start_time": "2024-11-16T01:31:11.857704Z"
}
},
"cell_type": "code",
"source": [
"# check against inchis\n",
"inchi_skip = {}\n",
"charges_by_inchi = {}\n",
"for id, inchi in inchis.items():\n",
" toks = inchi.split(\"/\")\n",
" # q is charge\n",
" qtoks = [tok for tok in toks if tok.startswith(\"q\")]\n",
" if qtoks:\n",
" qtok = qtoks[0]\n",
" if \";\" in qtok:\n",
" qtok = qtok.replace(\";\", \"\")\n",
" inchi_skip[id] = qtok\n",
" continue\n",
" if \"*\" in qtok:\n",
" # print(qtok)\n",
" mparts= qtok[1:].split(\"*\")\n",
" charge = 1\n",
" try:\n",
" for mpart in mparts:\n",
" charge *= int(mpart)\n",
" except:\n",
" odd.append(id)\n",
" continue\n",
" else:\n",
" try:\n",
" charge = int(qtok[1:])\n",
" charges_by_inchi[id] = charge\n",
" except:\n",
" odd.append(id)\n",
" continue\n",
" # p is protonation\n",
" ptoks = [tok for tok in toks if tok.startswith(\"p\")]\n",
" if ptoks and True:\n",
" ptok = ptoks[0]\n",
" try:\n",
" charge = int(ptok[1:])\n",
" \n",
" if id in charges_by_inchi:\n",
" charges_by_inchi[id] = charge + charges_by_inchi[id]\n",
" else:\n",
" charges_by_inchi[id] = charge\n",
" except:\n",
" pass\n",
" \n",
"len(charges_by_inchi)"
],
"id": "da085426a91c079a",
"outputs": [
{
"data": {
"text/plain": [
"10701"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 18
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:12.390567Z",
"start_time": "2024-11-16T01:31:12.372527Z"
}
},
"cell_type": "code",
"source": [
"errs = []\n",
"for id, charge in charges_by_inchi.items():\n",
" if id not in charges:\n",
" errs.append({\"id\": id, \"inchi_charge\": charge, \"asserted_charge\": None, \"type\": \"MISSING\"})\n",
" elif charges[id] != charge:\n",
" errs.append({\"id\": id, \"inchi_charge\": charge, \"asserted_charge\": charges[id], \"type\": \"MISMATCH\"})\n",
"\n",
"cedf = pd.DataFrame(errs)\n",
"cedf"
],
"id": "b46c92961495ac6c",
"outputs": [
{
"data": {
"text/plain": [
"Empty DataFrame\n",
"Columns: []\n",
"Index: []"
],
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
"
\n",
" \n",
" \n",
" \n",
"
\n",
"
"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 19
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:12.576946Z",
"start_time": "2024-11-16T01:31:12.574952Z"
}
},
"cell_type": "code",
"source": "# charges_by_inchi[L_CYSTEINE_ZWITTERION]",
"id": "4b25096456c81bdf",
"outputs": [],
"execution_count": 20
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Find Conjugate Cliques",
"id": "9e690eb32a60624"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:15.101877Z",
"start_time": "2024-11-16T01:31:12.580466Z"
}
},
"cell_type": "code",
"source": [
"conjrels = list(chebi.relationships(predicates=[CBO, CAO, TAUTOMER_OF]))\n",
"\n",
"conjrels = [(s, p, o) for s, p, o in conjrels if not s in CONJ_EXCLUDES and not o in CONJ_EXCLUDES]\n",
"\n",
"assert len(conjrels) > 15000\n",
"assert len([r for r in conjrels if r[1] == CBO]) > 8000\n",
"assert len([r for r in conjrels if r[1] == TAUTOMER_OF]) > 1500"
],
"id": "3cb9284c0f7ba8b1",
"outputs": [],
"execution_count": 21
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:15.116942Z",
"start_time": "2024-11-16T01:31:15.106922Z"
}
},
"cell_type": "code",
"source": [
"conjrels_by_subject = defaultdict(list)\n",
"for s, p, o in conjrels:\n",
" conjrels_by_subject[s].append((p, o))"
],
"id": "73446b9fa82786fd",
"outputs": [],
"execution_count": 22
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:15.280816Z",
"start_time": "2024-11-16T01:31:15.279140Z"
}
},
"cell_type": "code",
"source": "",
"id": "12ccc4842a4a2204",
"outputs": [],
"execution_count": null
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:15.446767Z",
"start_time": "2024-11-16T01:31:15.443711Z"
}
},
"cell_type": "code",
"source": "conjrels_by_subject[\"CHEBI:142854\"]",
"id": "efb8614663ba2054",
"outputs": [
{
"data": {
"text/plain": [
"[('obo:chebi#is_conjugate_acid_of', 'CHEBI:142858'),\n",
" ('obo:chebi#is_tautomer_of', 'CHEBI:142853')]"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 23
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Calculate conjugate graph and strongly connected components",
"id": "13e159ee4a9c8964"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:15.833277Z",
"start_time": "2024-11-16T01:31:15.611565Z"
}
},
"cell_type": "code",
"source": [
"from typing import Tuple\n",
"import networkx as nx\n",
"# find strongly connected components using cbos\n",
"\n",
"def calculate_conj_graph(conjrels: List[Tuple[str, str, str]]) -> nx.DiGraph:\n",
" conj_graph = nx.DiGraph()\n",
" for s, _, o in conjrels:\n",
" conj_graph.add_edge(s, o)\n",
" conj_graph.add_edge(o, s)\n",
" return conj_graph\n",
"\n",
"conj_graph = calculate_conj_graph(conjrels)\n",
"sccs = list(nx.strongly_connected_components(conj_graph))\n",
"asserted_sccs = sccs\n",
"assert len(sccs) > 8000\n",
"\n"
],
"id": "5d1bc9a29166f020",
"outputs": [],
"execution_count": 24
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:16.005371Z",
"start_time": "2024-11-16T01:31:16.002227Z"
}
},
"cell_type": "code",
"source": "len(asserted_sccs)",
"id": "a0fb3be95eeb9eec",
"outputs": [
{
"data": {
"text/plain": [
"8555"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 25
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"## Lexical analysis\n",
"\n",
"The CHEBI conjugate relationships are incomplete - here we aim to complete them doing a lexical analysis of the labels.\n",
"\n",
"For example,\n",
"\n",
"- foo acid anion\n",
"- foo acid(1-)\n",
"- foo acid zwitterion\n",
"\n",
"should be in a clique"
],
"id": "c84b70bcca329c02"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:16.179810Z",
"start_time": "2024-11-16T01:31:16.173920Z"
}
},
"cell_type": "code",
"source": [
"# ensure ordering is such that greedy matching works\n",
"suffixes = {\n",
" # odd edge cases - e.g. (2R)-glufosinate zwitterion(1-)\n",
" \"zwitterion(1-)\": -1,\n",
" \"zwitterion(2-)\": -2,\n",
" \"anion(1-)\": -1,\n",
" # standard\n",
" \"zwitterion\": None, \"anion\": (-99, -1), \"cation\": (1, 99), \"ion\": None, \n",
" \"ate\": None,\n",
" \"acid\": None,\n",
" \n",
"}\n",
"for i in range(1, 10):\n",
" for sign in [\"+\", \"-\"]:\n",
" suffixes[f\"({i}{sign})\"] = int(f\"{sign}{i}\")\n",
"suffixes"
],
"id": "5fd4bf68445f7b11",
"outputs": [
{
"data": {
"text/plain": [
"{'zwitterion(1-)': -1,\n",
" 'zwitterion(2-)': -2,\n",
" 'anion(1-)': -1,\n",
" 'zwitterion': None,\n",
" 'anion': (-99, -1),\n",
" 'cation': (1, 99),\n",
" 'ion': None,\n",
" 'ate': None,\n",
" 'acid': None,\n",
" '(1+)': 1,\n",
" '(1-)': -1,\n",
" '(2+)': 2,\n",
" '(2-)': -2,\n",
" '(3+)': 3,\n",
" '(3-)': -3,\n",
" '(4+)': 4,\n",
" '(4-)': -4,\n",
" '(5+)': 5,\n",
" '(5-)': -5,\n",
" '(6+)': 6,\n",
" '(6-)': -6,\n",
" '(7+)': 7,\n",
" '(7-)': -7,\n",
" '(8+)': 8,\n",
" '(8-)': -8,\n",
" '(9+)': 9,\n",
" '(9-)': -9}"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 26
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:19.602618Z",
"start_time": "2024-11-16T01:31:16.349402Z"
}
},
"cell_type": "code",
"source": "roles = list(chebi.descendants(\"CHEBI:50906\", [IS_A]))",
"id": "c10c64db4580a7ee",
"outputs": [],
"execution_count": 27
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:19.774294Z",
"start_time": "2024-11-16T01:31:19.772106Z"
}
},
"cell_type": "code",
"source": [
"# https://github.com/ebi-chebi/ChEBI/issues/4528\n",
"EXCLUDE_STEMS = [\"disulfide\", \"tartr\", \"tartar\", \"oxide\", \"oxo\"]"
],
"id": "8400b3213240d61b",
"outputs": [],
"execution_count": 28
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:25.527666Z",
"start_time": "2024-11-16T01:31:19.942542Z"
}
},
"cell_type": "code",
"source": [
"from typing import Dict\n",
"\n",
"# stem to chem is a mapping between a stem (e.g. \"L-lysine\")\n",
"# and a dictionary of suffixes to CHEBI IDs\n",
"stem_to_chem: Dict[str, Dict[str, str]] = {}\n",
"stem_to_chem = defaultdict(dict)\n",
"\n",
"def _norm(label: str) -> str:\n",
" # CHEBI is inconsistent, e.g. \"amino-acid\" vs \"amino acid\"\n",
" label = label.replace(\"-acid\", \" acid\")\n",
" # label = label.replace(\" acid\", \"\")\n",
" return label\n",
"\n",
"def _de_acid(label: str) -> str:\n",
" if label.endswith(\" acid\"):\n",
" label = label.replace(\" acid\", \"\")\n",
" if label.endswith(\"ic\"):\n",
" label = label.replace(\"ic\", \"\")\n",
" if label.endswith(\"ate\"):\n",
" label = label.replace(\"ate\", \"\")\n",
" return label\n",
"\n",
"for id, label in labels.items():\n",
" if not label:\n",
" # TODO: eliminate non-classes\n",
" continue\n",
" if id in roles:\n",
" continue\n",
" label = _norm(label)\n",
" for suffix in suffixes.keys():\n",
" if label.endswith(suffix):\n",
" stem = label.replace(suffix, \"\")\n",
" stem = stem.strip()\n",
" stem_to_chem[_de_acid(stem)][suffix] = id\n",
" break\n",
"for id, label in labels.items():\n",
" if not label:\n",
" continue\n",
" label = _norm(label)\n",
" if label in stem_to_chem:\n",
" stem_to_chem[_de_acid(label)][\"\"] = id\n",
" \n",
"for stem in EXCLUDE_STEMS:\n",
" if stem in stem_to_chem:\n",
" del stem_to_chem[stem]\n",
"\n",
"assert len(stem_to_chem) > 30000"
],
"id": "191d0cca5fcbbcaf",
"outputs": [],
"execution_count": 29
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:25.734334Z",
"start_time": "2024-11-16T01:31:25.731139Z"
}
},
"cell_type": "code",
"source": "stem_to_chem[\"amino\"]",
"id": "8655db99a49e3963",
"outputs": [
{
"data": {
"text/plain": [
"{'cation': 'CHEBI:33703',\n",
" 'acid': 'CHEBI:33709',\n",
" 'zwitterion': 'CHEBI:35238',\n",
" 'anion': 'CHEBI:37022'}"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 30
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:25.943943Z",
"start_time": "2024-11-16T01:31:25.939811Z"
}
},
"cell_type": "code",
"source": "stem_to_chem[\"oxo\"]",
"id": "18ad460573de1981",
"outputs": [
{
"data": {
"text/plain": [
"{}"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 31
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:26.174167Z",
"start_time": "2024-11-16T01:31:26.169880Z"
}
},
"cell_type": "code",
"source": "stem_to_chem[\"citr\"]",
"id": "e9866728e7812eb3",
"outputs": [
{
"data": {
"text/plain": [
"{'(4-)': 'CHEBI:132362',\n",
" 'anion': 'CHEBI:133748',\n",
" '(3-)': 'CHEBI:16947',\n",
" 'acid': 'CHEBI:30769',\n",
" '(1-)': 'CHEBI:35804',\n",
" '(2-)': 'CHEBI:35808'}"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 32
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:26.432704Z",
"start_time": "2024-11-16T01:31:26.429980Z"
}
},
"cell_type": "code",
"source": "assert not stem_to_chem[\"citrate\"]",
"id": "218adb893354594a",
"outputs": [],
"execution_count": 33
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:26.649678Z",
"start_time": "2024-11-16T01:31:26.646255Z"
}
},
"cell_type": "code",
"source": "stem_to_chem[\"(2R)-glufosin\"]",
"id": "7f2dc8de7ba44920",
"outputs": [
{
"data": {
"text/plain": [
"{'ate': 'CHEBI:142853',\n",
" 'zwitterion': 'CHEBI:142854',\n",
" 'zwitterion(1-)': 'CHEBI:142858'}"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 34
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:26.863908Z",
"start_time": "2024-11-16T01:31:26.861202Z"
}
},
"cell_type": "code",
"source": [
"# ensure edge case of (2R)-glufosinate zwitterion(1-) is taken care of\n",
"assert not stem_to_chem[\"(2R)-glufosinate zwitterion\"]"
],
"id": "4c64ccc7e2241c4c",
"outputs": [],
"execution_count": 35
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:27.071697Z",
"start_time": "2024-11-16T01:31:27.069657Z"
}
},
"cell_type": "code",
"source": "",
"id": "91405e702f57aac8",
"outputs": [],
"execution_count": null
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:27.282471Z",
"start_time": "2024-11-16T01:31:27.280694Z"
}
},
"cell_type": "code",
"source": "",
"id": "8c0413a76dcfd32b",
"outputs": [],
"execution_count": null
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"## Analysis: Consistency check between lexically inferred cliques and asserted relationships\n",
"\n",
"Some of this is reported here:\n",
"\n",
"- https://github.com/ebi-chebi/ChEBI/issues/4524\n"
],
"id": "d18ca068f686eb8e"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:27.717425Z",
"start_time": "2024-11-16T01:31:27.698402Z"
}
},
"cell_type": "code",
"source": [
"import numpy as np\n",
"\n",
"NO_REL = \"NO_REL\"\n",
"INVERSES = {\n",
" CBO: CAO,\n",
" CAO: CBO,\n",
" TAUTOMER_OF: TAUTOMER_OF,\n",
" NO_REL: NO_REL\n",
"}\n",
"\n",
"charge_problems = []\n",
"def make_conjrefs(clique_suffix_dict: dict, stem=None) -> List:\n",
" results = []\n",
" for suffix1, chem1 in clique_suffix_dict.items():\n",
" ch1 = suffixes.get(suffix1, None)\n",
" actual_ch1 = charges.get(chem1, None)\n",
" chem1_label = labels.get(chem1, chem1)\n",
" if isinstance(ch1, int):\n",
" if actual_ch1 is None:\n",
" charge_problems.append({\"id\": chem1, \"label\": chem1_label, \"expected\": ch1, \"asserted\": None, \"problem\": \"MISSING_CHARGE\"})\n",
" # raise ValueError(f\"Missing charge for {chem1}\")\n",
" elif ch1 != actual_ch1:\n",
" charge_problems.append({\"id\": chem1, \"label\": chem1_label, \"expected\": ch1, \"asserted\": actual_ch1, \"problem\": \"CONFLICTING_CHARGE\"})\n",
" # raise ValueError(f\"Charge mismatch for {chem1}: {ch1} vs {actual_ch1}\")\n",
" elif isinstance(ch1, tuple):\n",
" if actual_ch1 is not None:\n",
" if actual_ch1 < ch1[0] or actual_ch1 > ch1[1]:\n",
" charge_problems.append({\"id\": chem1, \"label\": chem1_label, \"expected\": ch1, \"asserted\": actual_ch1, \"problem\": \"OUTSIDE_RANGE\"})\n",
" # raise ValueError(f\"Charge mismatch for {chem1}: {ch1} vs {actual_ch1}\")\n",
" rels = conjrels_by_subject.get(chem1, [])\n",
" for suffix2, chem2 in clique_suffix_dict.items():\n",
" if suffix1 == suffix2:\n",
" continue\n",
" messages = []\n",
" matched_preds = set()\n",
" actual_p = \"NO_REL\"\n",
" for p, o in rels:\n",
" if o == chem2:\n",
" actual_p = p\n",
" matched_preds.add(p)\n",
" if len(matched_preds) > 1:\n",
" messages.append(f\"Multiple matched preds: {matched_preds}\")\n",
" \n",
" rev_matched_preds = set()\n",
" rels2 = conjrels_by_subject.get(chem2, [])\n",
" for p, o in rels2:\n",
" if o == chem1:\n",
" inv_p = INVERSES[p]\n",
" rev_matched_preds.add(inv_p)\n",
" if actual_p:\n",
" if inv_p != actual_p:\n",
" messages.append(f\"Preds mismatch: {actual_p} vs {inv_p}\")\n",
" else:\n",
" actual_p = inv_p\n",
" if len(rev_matched_preds) > 1:\n",
" messages.append(f\"Multiple matched inv preds: {rev_matched_preds}\")\n",
" if matched_preds != rev_matched_preds:\n",
" messages.append(f\"Preds mismatch: {matched_preds} vs {rev_matched_preds}\")\n",
" if messages:\n",
" raise ValueError(f\"Error in clique {clique_suffix_dict}: {messages}\")\n",
" ch2 = suffixes.get(suffix2, None)\n",
" if ch1 is None or ch2 is None:\n",
" charge_diff = None\n",
" charge_diff_sign = None\n",
" else:\n",
" if isinstance(ch1, int) and isinstance(ch2, int):\n",
" charge_diff = ch1 - ch2\n",
" charge_diff_sign = np.sign(charge_diff)\n",
" elif isinstance(ch1, int) and isinstance(ch2, tuple):\n",
" charge_diff = None\n",
" if ch1 < ch2[0]:\n",
" charge_diff_sign = -1\n",
" elif ch1 > ch2[1]:\n",
" charge_diff_sign = 1\n",
" else:\n",
" charge_diff_sign = 0\n",
" elif isinstance(ch1, tuple) and isinstance(ch2, int):\n",
" charge_diff = None\n",
" if ch1[0] < ch2:\n",
" charge_diff_sign = -1\n",
" elif ch1[1] > ch2:\n",
" charge_diff_sign = 1\n",
" else:\n",
" charge_diff_sign = 0\n",
" else:\n",
" charge_diff = None\n",
" charge_diff_sign = None\n",
" results.append({\"suffix1\": suffix1 or \"NO_SUFFIX\",\n",
" \"suffix2\": suffix2 or \"NO_SUFFIX\",\n",
" \"predicate\": actual_p,\n",
" \"chem1\": chem1,\n",
" \"chem2\": chem2,\n",
" \"charge_diff\": charge_diff,\n",
" \"charge_diff_sign\": charge_diff_sign,\n",
" \"stem\": stem,\n",
" })\n",
" return results\n",
" \n",
"make_conjrefs(stem_to_chem[\"amino\"])"
],
"id": "f82ad334be85fe9f",
"outputs": [
{
"data": {
"text/plain": [
"[{'suffix1': 'cation',\n",
" 'suffix2': 'acid',\n",
" 'predicate': 'obo:chebi#is_conjugate_acid_of',\n",
" 'chem1': 'CHEBI:33703',\n",
" 'chem2': 'CHEBI:33709',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'cation',\n",
" 'suffix2': 'zwitterion',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:33703',\n",
" 'chem2': 'CHEBI:35238',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'cation',\n",
" 'suffix2': 'anion',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:33703',\n",
" 'chem2': 'CHEBI:37022',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'acid',\n",
" 'suffix2': 'cation',\n",
" 'predicate': 'obo:chebi#is_conjugate_base_of',\n",
" 'chem1': 'CHEBI:33709',\n",
" 'chem2': 'CHEBI:33703',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'acid',\n",
" 'suffix2': 'zwitterion',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:33709',\n",
" 'chem2': 'CHEBI:35238',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'acid',\n",
" 'suffix2': 'anion',\n",
" 'predicate': 'obo:chebi#is_conjugate_acid_of',\n",
" 'chem1': 'CHEBI:33709',\n",
" 'chem2': 'CHEBI:37022',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'zwitterion',\n",
" 'suffix2': 'cation',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:35238',\n",
" 'chem2': 'CHEBI:33703',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'zwitterion',\n",
" 'suffix2': 'acid',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:35238',\n",
" 'chem2': 'CHEBI:33709',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'zwitterion',\n",
" 'suffix2': 'anion',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:35238',\n",
" 'chem2': 'CHEBI:37022',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'anion',\n",
" 'suffix2': 'cation',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:37022',\n",
" 'chem2': 'CHEBI:33703',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'anion',\n",
" 'suffix2': 'acid',\n",
" 'predicate': 'obo:chebi#is_conjugate_base_of',\n",
" 'chem1': 'CHEBI:37022',\n",
" 'chem2': 'CHEBI:33709',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'anion',\n",
" 'suffix2': 'zwitterion',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:37022',\n",
" 'chem2': 'CHEBI:35238',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None}]"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 36
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:27.757292Z",
"start_time": "2024-11-16T01:31:27.752910Z"
}
},
"cell_type": "code",
"source": "make_conjrefs(stem_to_chem[\"(2R)-glufosin\"])",
"id": "be092eb65d52d565",
"outputs": [
{
"data": {
"text/plain": [
"[{'suffix1': 'ate',\n",
" 'suffix2': 'zwitterion',\n",
" 'predicate': 'obo:chebi#is_tautomer_of',\n",
" 'chem1': 'CHEBI:142853',\n",
" 'chem2': 'CHEBI:142854',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'ate',\n",
" 'suffix2': 'zwitterion(1-)',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:142853',\n",
" 'chem2': 'CHEBI:142858',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'zwitterion',\n",
" 'suffix2': 'ate',\n",
" 'predicate': 'obo:chebi#is_tautomer_of',\n",
" 'chem1': 'CHEBI:142854',\n",
" 'chem2': 'CHEBI:142853',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'zwitterion',\n",
" 'suffix2': 'zwitterion(1-)',\n",
" 'predicate': 'obo:chebi#is_conjugate_acid_of',\n",
" 'chem1': 'CHEBI:142854',\n",
" 'chem2': 'CHEBI:142858',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'zwitterion(1-)',\n",
" 'suffix2': 'ate',\n",
" 'predicate': 'NO_REL',\n",
" 'chem1': 'CHEBI:142858',\n",
" 'chem2': 'CHEBI:142853',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None},\n",
" {'suffix1': 'zwitterion(1-)',\n",
" 'suffix2': 'zwitterion',\n",
" 'predicate': 'obo:chebi#is_conjugate_base_of',\n",
" 'chem1': 'CHEBI:142858',\n",
" 'chem2': 'CHEBI:142854',\n",
" 'charge_diff': None,\n",
" 'charge_diff_sign': None,\n",
" 'stem': None}]"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 37
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:26.798272Z",
"start_time": "2024-11-16T01:57:26.634185Z"
}
},
"cell_type": "code",
"source": "!mkdir -p tmp",
"id": "66516f31cdfd6ef5",
"outputs": [],
"execution_count": 39
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:27.594776Z",
"start_time": "2024-11-16T01:57:27.489243Z"
}
},
"cell_type": "code",
"source": [
"charge_problems = [] ## warning - global\n",
"lexical_conj_pairs = []\n",
"for stem, clique in stem_to_chem.items():\n",
" lexical_conj_pairs += make_conjrefs(clique, stem)\n",
"\n",
"# Reported here: https://github.com/ebi-chebi/ChEBI/issues/4525\n",
"pd.DataFrame(charge_problems).to_csv(\"tmp/charge_problems.csv\", index=False)\n",
"\n",
"\n",
"len(lexical_conj_pairs)"
],
"id": "c5ef02e4ed64a4d3",
"outputs": [
{
"data": {
"text/plain": [
"17170"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 40
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:28.245587Z",
"start_time": "2024-11-16T01:57:28.229896Z"
}
},
"cell_type": "code",
"source": [
"chem_to_stem: Dict[str, str] = {}\n",
"for row in lexical_conj_pairs:\n",
" def _assign(chem: str, stem: str):\n",
" if chem in chem_to_stem:\n",
" if chem_to_stem[chem] != stem:\n",
" raise ValueError(f\"Conflicting stems for {chem}: {chem_to_stem[chem]} vs {stem}\")\n",
" else:\n",
" chem_to_stem[chem] = stem\n",
" stem = row[\"stem\"]\n",
" _assign(row[\"chem1\"], stem)\n",
" _assign(row[\"chem2\"], stem)"
],
"id": "3088a79bf38863f5",
"outputs": [],
"execution_count": 41
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:28.772650Z",
"start_time": "2024-11-16T01:57:28.478678Z"
}
},
"cell_type": "code",
"source": [
"g = calculate_conj_graph([(row[\"chem1\"], \"?\", row[\"chem2\"]) for row in lexical_conj_pairs])\n",
"lexical_sccs = list(nx.strongly_connected_components(g))\n",
"len(lexical_sccs)"
],
"id": "b0612535c952ce23",
"outputs": [
{
"data": {
"text/plain": [
"7519"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 42
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:28.871885Z",
"start_time": "2024-11-16T01:57:28.869895Z"
}
},
"cell_type": "code",
"source": "",
"id": "fd3af467efa5e894",
"outputs": [],
"execution_count": null
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:29.331493Z",
"start_time": "2024-11-16T01:57:29.302776Z"
}
},
"cell_type": "code",
"source": [
"# venn diagram of overlaps between\n",
"# - lexical_sccs\n",
"# - asserted_sccs\n",
"# - rhea_sccs\n",
"# - full_sccs\n",
"\n",
"from matplotlib_venn import venn3\n",
"from matplotlib_venn import venn3_unweighted\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"def hashable_scc(scc):\n",
" return set([tuple(sorted(list(x))) for x in scc])\n",
"\n",
"def my_venn3(sccs, *args, **kwargs):\n",
" scc_sets = [hashable_scc(scc) for scc in sccs]\n",
" venn3_unweighted(scc_sets, *args, **kwargs)\n",
"\n",
"#venn3([set(lexical_sccs), set(asserted_sccs), set(rhea_sccs)], (\"Lexical\", \"Asserted\", \"Rhea\"))\n",
"#venn3([{1}, {1,2}, {1,2,tuple(\"a\" \"b\")}])\n",
"my_venn3([lexical_sccs, asserted_sccs, rhea_sccs], (\"Lexical\", \"Asserted\", \"Rhea\"))\n",
"plt.show()"
],
"id": "2305e056b7c20879",
"outputs": [
{
"ename": "NameError",
"evalue": "name 'rhea_sccs' is not defined",
"output_type": "error",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)",
"Cell \u001B[0;32mIn[43], line 21\u001B[0m\n\u001B[1;32m 17\u001B[0m venn3_unweighted(scc_sets, \u001B[38;5;241m*\u001B[39margs, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs)\n\u001B[1;32m 19\u001B[0m \u001B[38;5;66;03m#venn3([set(lexical_sccs), set(asserted_sccs), set(rhea_sccs)], (\"Lexical\", \"Asserted\", \"Rhea\"))\u001B[39;00m\n\u001B[1;32m 20\u001B[0m \u001B[38;5;66;03m#venn3([{1}, {1,2}, {1,2,tuple(\"a\" \"b\")}])\u001B[39;00m\n\u001B[0;32m---> 21\u001B[0m my_venn3([lexical_sccs, asserted_sccs, \u001B[43mrhea_sccs\u001B[49m], (\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mLexical\u001B[39m\u001B[38;5;124m\"\u001B[39m, \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mAsserted\u001B[39m\u001B[38;5;124m\"\u001B[39m, \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mRhea\u001B[39m\u001B[38;5;124m\"\u001B[39m))\n\u001B[1;32m 22\u001B[0m plt\u001B[38;5;241m.\u001B[39mshow()\n",
"\u001B[0;31mNameError\u001B[0m: name 'rhea_sccs' is not defined"
]
}
],
"execution_count": 43
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": [
"# same as Euler diagram\n",
"from matplotlib_venn import venn3_unweighted\n"
],
"id": "96f038a31ac729b0"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:29.414228Z",
"start_time": "2024-08-24T00:15:17.567225Z"
}
},
"cell_type": "code",
"source": [
"import pandas as pd\n",
"df = pd.DataFrame(lexical_conj_pairs)"
],
"id": "ce0321a0c59abbe8",
"outputs": [],
"execution_count": 42
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:29.653055Z",
"start_time": "2024-11-16T01:57:29.637167Z"
}
},
"cell_type": "code",
"source": "df",
"id": "b3d677f09cff4d7e",
"outputs": [
{
"ename": "NameError",
"evalue": "name 'df' is not defined",
"output_type": "error",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)",
"Cell \u001B[0;32mIn[44], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[43mdf\u001B[49m\n",
"\u001B[0;31mNameError\u001B[0m: name 'df' is not defined"
]
}
],
"execution_count": 44
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:29.856272Z",
"start_time": "2024-11-16T01:57:29.838003Z"
}
},
"cell_type": "code",
"source": "df[(df[\"charge_diff_sign\"] < 0) & (df[\"predicate\"] == CAO)]",
"id": "4f5986951d764fa1",
"outputs": [
{
"ename": "NameError",
"evalue": "name 'df' is not defined",
"output_type": "error",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)",
"Cell \u001B[0;32mIn[45], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[43mdf\u001B[49m[(df[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mcharge_diff_sign\u001B[39m\u001B[38;5;124m\"\u001B[39m] \u001B[38;5;241m<\u001B[39m \u001B[38;5;241m0\u001B[39m) \u001B[38;5;241m&\u001B[39m (df[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mpredicate\u001B[39m\u001B[38;5;124m\"\u001B[39m] \u001B[38;5;241m==\u001B[39m CAO)]\n",
"\u001B[0;31mNameError\u001B[0m: name 'df' is not defined"
]
}
],
"execution_count": 45
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:30.236461Z",
"start_time": "2024-11-16T01:57:30.218120Z"
}
},
"cell_type": "code",
"source": "df[(df[\"charge_diff_sign\"] > 0) & (df[\"predicate\"] == CBO)]",
"id": "577c5ec2b5fe7e60",
"outputs": [
{
"ename": "NameError",
"evalue": "name 'df' is not defined",
"output_type": "error",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)",
"Cell \u001B[0;32mIn[46], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[43mdf\u001B[49m[(df[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mcharge_diff_sign\u001B[39m\u001B[38;5;124m\"\u001B[39m] \u001B[38;5;241m>\u001B[39m \u001B[38;5;241m0\u001B[39m) \u001B[38;5;241m&\u001B[39m (df[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mpredicate\u001B[39m\u001B[38;5;124m\"\u001B[39m] \u001B[38;5;241m==\u001B[39m CBO)]\n",
"\u001B[0;31mNameError\u001B[0m: name 'df' is not defined"
]
}
],
"execution_count": 46
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:30.547390Z",
"start_time": "2024-11-16T01:57:30.531512Z"
}
},
"cell_type": "code",
"source": " df.groupby([\"predicate\"]).size()",
"id": "b381a47bce47eda3",
"outputs": [
{
"ename": "NameError",
"evalue": "name 'df' is not defined",
"output_type": "error",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)",
"Cell \u001B[0;32mIn[47], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[43mdf\u001B[49m\u001B[38;5;241m.\u001B[39mgroupby([\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mpredicate\u001B[39m\u001B[38;5;124m\"\u001B[39m])\u001B[38;5;241m.\u001B[39msize()\n",
"\u001B[0;31mNameError\u001B[0m: name 'df' is not defined"
]
}
],
"execution_count": 47
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:57:30.855558Z",
"start_time": "2024-11-16T01:57:30.839137Z"
}
},
"cell_type": "code",
"source": "df.to_csv(\"tmp/conjrels.csv\", index=False)\n",
"id": "5ee58eef65ddbc9d",
"outputs": [
{
"ename": "NameError",
"evalue": "name 'df' is not defined",
"output_type": "error",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)",
"Cell \u001B[0;32mIn[48], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[43mdf\u001B[49m\u001B[38;5;241m.\u001B[39mto_csv(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mtmp/conjrels.csv\u001B[39m\u001B[38;5;124m\"\u001B[39m, index\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mFalse\u001B[39;00m)\n",
"\u001B[0;31mNameError\u001B[0m: name 'df' is not defined"
]
}
],
"execution_count": 48
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.003412Z",
"start_time": "2024-08-24T00:15:18.496412Z"
}
},
"cell_type": "code",
"source": [
"# group by suffix1, suffix2, predicate, and count the number of rows\n",
"summary = df.groupby([\"suffix1\", \"suffix2\", \"predicate\"]).size()\n",
"summary.sort_values(ascending=False)"
],
"id": "f1be2d07a0d5ce95",
"outputs": [
{
"data": {
"text/plain": [
"suffix1 suffix2 predicate \n",
"acid ate obo:chebi#is_conjugate_acid_of 1522\n",
"ate acid obo:chebi#is_conjugate_base_of 1522\n",
"NO_SUFFIX (1-) obo:chebi#is_conjugate_acid_of 1332\n",
"(1-) NO_SUFFIX obo:chebi#is_conjugate_base_of 1332\n",
"NO_SUFFIX (4-) obo:chebi#is_conjugate_acid_of 673\n",
" ... \n",
"anion(1-) acid NO_REL 1\n",
"(6-) (2-) NO_REL 1\n",
" (3-) NO_REL 1\n",
" (4-) NO_REL 1\n",
"zwitterion(2-) zwitterion obo:chebi#is_conjugate_base_of 1\n",
"Length: 424, dtype: int64"
]
},
"execution_count": 48,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 48
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.003568Z",
"start_time": "2024-08-24T00:15:18.593731Z"
}
},
"cell_type": "code",
"source": [
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"from scipy.stats import entropy\n",
"\n",
"data = df\n",
"\n",
"# Display the first few rows of the data to understand its structure\n",
"data.head()\n",
"\n",
"# Create a function to calculate entropy for each group\n",
"def calculate_entropy(group):\n",
" counts = group.value_counts(normalize=True)\n",
" return entropy(counts)\n",
"\n",
"# Grouping the data by suffix1 and suffix2, then calculating entropy for the predicates\n",
"entropy_data = data.groupby(['suffix1', 'suffix2'])['predicate'].apply(calculate_entropy).unstack(fill_value=0)\n",
"\n",
"# Plotting the entropy heatmap with reversed x-axis\n",
"plt.figure(figsize=(10, 8))\n",
"sns.heatmap(entropy_data.loc[:, ::-1], annot=True, cmap=\"coolwarm\")\n",
"plt.title('Entropy of Predicate Distribution by Suffix1 and Suffix2 (Reversed X-Axis)')\n",
"plt.xlabel('Suffix2')\n",
"plt.ylabel('Suffix1')\n",
"plt.show()\n"
],
"id": "b15618246ffb5c58",
"outputs": [
{
"data": {
"text/plain": [
""
],
"image/png": "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DAsWLMDr169RqVIlzJs3TzmjnVCf42/evMHEiRNha2urnAHzfT179sTJkyexZcsWtGjRQuNn6L59+2BkZJTnpCvm5uZo37499u/fj9u3b2PdunXw9/dXjsaoWbMmBgwYgG3btqFt27ZqPdbafDYD2T27derUyXfSEaLCECm07dsm+o9xcXHBmDFjMHbs2AK3HTZsGExNTT/oWkRE/wUKhQKdO3eGh4eH8kKpRELZvHkzfv75Zxw/fjzPWTbp05aSkoLmzZtjyZIlhbquGVF+2ANF9AHWrFmDBw8e4LfffsPOnTv1nQ6RwXr79i22bt2KP//8E48ePUL//v31nRL9B+RMDHH06FG1afbpv2HXrl2oWrVqsfW0EwEsoIg+yMmTJ/H3339jypQpOp8AguhjYmZmhl27dkEul2PhwoVFmnGSqLDMzMwQHByMqVOnonXr1h88YyZ9XF6+fImtW7dix44d7IGkYsUhfERERERERFriNOZERERERERaYgFFRERERESkJRZQREREREREWmIBRUREREREpCXOwveJ8/A+o+8Uiuzgwvwvivqhuk7PEjS+0BYvaSho/KlBhbuQpyHZvdRa0PgpxqUEjT9owt+CxicqqhPjHggav83KzwWNT3k7PvmpoPHFsjRB4wtN6GPzt0hPQeMX1WET7S5CLYTOGXF6e+yCsAeKiIiIiIhISyygiIiIiIiItMQhfEREREREpEZkwgsQa8IeKCIiIiIiIi2xB0pgSUlJGD58OMLCwmBiYgIAuHz5MoKCghAVFaV1nBUrVqBChQro1atXsecoMRFh4oiq8HS3Q7osC7sOJGBXeILBxU+XZWDpxp9w+mIMTCUS9O3aHn27tte47b2HCQjesAO37z9ExXL2mDikLxrUctVr/sUdP0OWjp/WL0bMhShITM3Qvlt/tO/WP982L/55gtnje2LcjB/gWit7Eor0tFT8vGkpYi+ehEIhR0P3Nug9eJKguWvrQ+LLZDJ8v24jzl64BFOJBL27d0UvH2+N2874dgnO/646acbCWVPR9IsGAIADh49i175wvH2Xgi/c6mLoyLHYsWMbzp87B4lEAl+/HvD19dMYe968ubh08aLKujlzvkGjxo2RkSHD9u3bcOb0GaSlp6FO7ToYMXKkVs8PMOz9r8/YjC9c/PSMTCzcfQxRf8TB1MQYA1o3xsDWjTVu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ItK9jvMt9J3Cf9bhHbxcAxERffrYA6WZ1rPwERERERER/dexgCIiIiIiItISh/AREREREZEahYJD+DRhDxQREREREZGWWEDpUFJSEnx9fZGRkYFff/0VHTt2hJubG/z9/XHjxo0820mlUvj4+CA9PV2H2RIRERHRf5kcIr0thowFlA4FBwcjICAAf/31FyZNmoTAwEBERESgevXqCAwMRGpqqsZ21tbWaNWqFdavX6/jjImIiIiI6H0soHQkISEBUVFR8Pb2xrlz51ClShV0794djo6OmDhxIhITE3Hv3r082/v7+2P79u1ISUnRYdZERERERPQ+FlA6EhYWBg8PD0gkElhZWeHevXuIiYmBXC7H/v37YWFhAUdHxzzb29nZoVKlSoiMjNRh1kRERET0X6WASG+LIeMsfDoSHR2NgIAAAECnTp1w8uRJ9O3bF0ZGRhCLxQgJCYGlpWW+Mdzd3REdHY3evXvrImUiIiIiIsqFPVA6kJmZibi4ODg7OwPInhQiMTERs2fPxu7du9GtWzdMmzYNSUlJ+capUqUKbt68qYuUiYiIiOg/TqEQ6W0xZCygdCA5ORlyuRzW1tYAgKVLl6JatWoICAhArVq1MH/+fJibm2Pfvn24fPky3NzclMu6deuUcaysrAossoiIiIiISDgcwqcDIlF2FS2XywEAN27cQP/+/ZX3i8ViuLq64smTJxgwYADCw8OV970/rE8ul0MsZs1LRERERMIz9HOR9IUFlA5YWVnByMgIUqkUAGBvb4/4+HiVbR48eIDatWvDzMwMTk5OGuNIpVLY2toKni8REREREWnG7gwdyOlhiouLAwD06tULu3fvRnh4OB4+fIilS5fiyZMn8PHxyTdOXFwcatSooYuUiYiIiIhIA/ZA6Ujz5s0RGxuLgIAAdOrUCe/evUNISAiePXuG6tWrY9u2bbCxsck3RmxsLHr16qWjjImIiIjov8zQJ3PQFxZQOuLn5wdfX1+kpqbC3NwcPXv2RM+ePbVun5CQgAcPHqBDhw4CZklERERERPnhED4dcXR0hKenZ5EvhLt79274+/ujZMmSxZwZEREREZE6XkhXMxZQOhQUFITQ0FDIZLJCtZNKpTh9+jRGjBghUGZERERERKQNDuHTIXt7e0RERBS6nbW1NQ4ePChARkREREREVBgsoIiIiIiISA0nkdCMQ/iI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IioHzkDVYH5+fpgwYQLOnTuH4uJiBAcHIyMjA1evXsXatWvVPv748ePx+eef48svv4SDgwOaNWsGAHj+/Dnmz59fZV0AayJBENC0aVMALwvl69evw9raGn369MH69etVnF31wRXURERE1Q9noGowGxsbHD58GG3btoWTkxOePHkCCwsLHDx4EN26dVP7+ADw119/wdTUVFI8AUBBQQH27t1bKfFJtg4dOiAyMhIA0L59e8TGxgIAsrKyVJkWERERkdJxBqoGW7NmDcaMGYNp06ZJjT9//hxLliyp8LUsyo4PAHZ2dvDw8MCcOXPg7u5e4Xgkn//973+YMGEC6tWrB1dXV6xbtw4uLi7Izs5W+ubMRERERKrEAqqGSUtLw6NHjwAAv/zyC4yNjaGrqyt1zF9//YXt27crVOAoO/5/TZ48GY6Ojpg1axYSEhKwYMECiESiCselt7O2tsaJEydQUFCAhg0bYvfu3Th27Bj09PTQp08fVadHREREpDQsoGqYBw8ewMvLS3JfVqe6evXqYdSoUWoZ/3WvCiVHR0fs3r0bPj4+GDx4MBYtWlTh2PRu2tra0NbWBvCy+9yIESNUnBERERGR8nEfqBrMyckJu3btUloLZWXHNzY2RmxsLBo3bgwAKCoqwvz583Hw4EEUFBTgxo0bSjkvUWV5fX80IiIiqh7YRKIG69KlC7S0tMqM5+XlwcfHR+3jT5kyBfXr15fc19LSwqJFizBnzhzY2NhUOD5RRRQXF+Phw4coLi5+4zHTpk1Dw4YNqzArIiIiqigu4athkpKSkJGRAQDYu3cvTExMJMuwXklLS0NMTIxaxn/dmzbKdXd3Z0MJUplt27Zh586dSElJkYwZGRlh6NChGD58uNSxry93JSIiouqBS/hqmJSUFEyePBmCICA7OxvNmjVDrVr/NxEpEolQv359eHh4lPmwpw7xidRVaWkpJk6ciISEBLi5ucHKygq6urp48OABrly5gt27d6Nr165Yu3at1O8EERERVS8soGowT09PrFmzpkyXvOoSn0idbNiwAb///ju2bt2Kjz76qMzj9+7dw6hRozB8+HDOPBEREVVjLKCIiCqBi4sLJk2a9NY27kePHsXq1asRFRVVhZkRERFRZeI1UDVM+/btERMTg8aNG8PY2PiteyYp0sVO2fGJ1FVmZibMzMzeekzHjh1x586dKsqIiIiIlIEFVA2zefNmyZK6sLCwahefSF3p6Ojg/v37aNGixRuPyc7OVlpbfyIiIqoaLKBqmM6dO0v+vGvXLnTv3h12dnaV1kpZ2fGJ1FXPnj3xyy+/YN26dTJnXgVBwK+//gonJycVZEdERESVhddA1WA//fQTYmNjkZKSAmNjYzg4OMDBwQEWFhaV0iVM2fGJ1ElOTg7c3d3RqlUrjBs3Dh07doSuri5ycnJw7do1/Prrr8jLy8Mff/zBWSgiIqJqjAUUIS8vD/Hx8YiLi0NsbCyePHkCW1tbrFq1qlrEJ1IX//zzDxYsWIATJ05IjdeqVQu9evXC3Llz8eGHH6ooOyIiIqoMXMJHKC0thUgkQp06daCrq4usrCypTUDVPT6RumjWrBl+/fVXPHz4ENeuXcPTp0+hq6uLjh07ctaJiIjoPcEZqBrMz88PFy9exN27d2FkZAQrKyvY2NjAxsYGjRs3Vvv4RERERERVjTNQNVhiYiLu3LkDW1tbODg4wMrKCiYmJtDQ0KgW8YnUiZOT01vb9r8iEolw7NixKsiIiIiIlIEzUDXcgwcPkJCQgAsXLiAhIQHZ2dkwNTWFjY0NpkyZovbxidTFnj173vhYfn4+NmzYgLt378LS0hLh4eFVmBkRERFVJhZQBAB4/vw5EhIScOzYMURGRqJWrVpITk6uNvGJ1NXx48exaNEi5OfnY+bMmRgyZIiqUyIiIqIKYAFVg506dQrx8fE4f/48UlJSYGBgADs7O9jZ2aFLly6oW7euWscnUmd3797F999/j1OnTsHNzQ0zZ86Enp6eqtMiIiKiCmIBVYMZGxvj888/R/fu3eHg4IBmzZoBeDlbtGjRIixevFit4xOpo5KSEqxfvx5r166FgYEBAgMDYWlpqeq0iIiIqJJwN9Ma7q+//oKpqamkuAGAgoIC7N27t1rEJ1In8fHxcHV1xW+//Ybp06djz549LJ6IiIjeMyygajg7Ozt4eHhg586d1TI+kbqYOXMmvLy8UFhYiMDAQJiYmCAxMREXLlwocyMiIqLqi23Ma7jJkyfD0dERs2bNQkJCAhYsWCBXK2Z1iU+kLvbv3w8AyMrKwsyZM994nEgkwo0bN6oqLSIiIqpkLKBqsFeFjKOjI3bv3g0fHx8MHjwYixYtqhbxidRJSkqKqlMgIiKiKsAlfDXY6/1DWrZsie3bt8Pc3BxeXl7VIj4RERERUVXjDFQNNmXKFNSvX19yX0tLC4sWLYKFhQX27dun9vGJiIiIiKoa25gTERERERHJiUv4iIiIiIiI5MQCioiIiIiISE4soIiIiIiIiOTEAoqIiJSquLgYQUFB+Oyzz9CxY0f06NEDixcvxvPnz+V6/vHjx9G9e3eYm5vjzJkzCAoKgrW1NWxsbBAWFgYnJye54qSmpsLb2xtWVlZwcnJCcHAwxGJxRV4aERHVQGwiQURESrV48WKcPXsWc+bMQatWrXDnzh0sWrQILVu2RHBw8Duf7+rqChMTE0yePBmamppwcHDAwoULYWdnh8aNGyM/Px+NGjV6a4wXL16gf//+6Ny5M8aOHYs7d+7A19cXU6ZMwYgRIyrrpRIRUQ3AGSgiIlKqPXv2YNq0aejWrRtatmyJbt26ITAwECdOnMCDBw/e+fxnz57B2toaLVq0QHFxMQCgW7duaNGiBerWrfvO4gkALly4gLy8PMyfPx8ff/wxHB0d4eXlhaioqAq/PiIiqllYQBERkVKJRCKcO3dOarmcpaUlDhw4gIYNG8LJyQkRERGSx+Lj42FkZAQAcHJywt27dzFnzhw4OTlJluv16tULvr6+iIiIkIytWrUKXbp0QV5eHgAgLi4OJiYmuHr1Ktq3b49ffvkFWlpaUrnJu4yQiIjoFRZQRESkVCNHjsSWLVvg5OSEgIAAHDlyBAUFBWjXrh00NTXf+txdu3ahWbNmmDNnDnbs2IGdO3cCAHbu3Im5c+dKHTtp0iTo6elh9erVKCwsREBAAMaMGYOOHTuiSZMm6NKli+TYgoIC7NixA127dq38F0xERO+12qpOgIiI3m+TJ09Gq1atsG3bNuzYsQPbt29HgwYNMHfuXAwePPitz23UqBE0NDSgo6MDfX19FBQUSMZ1dHSkjtXS0sLChQvh7e2NR48eoXbt2pgyZUqZmGKxGL6+vvj3338xfvz4ynuhRERUI7CAIiIipRswYAAGDBiA3NxcxMTE4Pfff8fcuXMlS/UqS+fOneHi4oKIiAhs3bq1zJK9kpISzJ49GydPnsSGDRvQpEmTSj0/ERG9/7iEj4iIlCYlJQVLliyR3G/YsCFcXFywZcsWNGvWDOfOnSvznNLSUoXPJxaL8ddff0FDQ6NM7OLiYkyfPh1//vknQkNDYWVlpfB5iIio5mIBRURESlNaWoqNGzfi+vXrUuNaWlqSDnqampr4999/JY/duXNH4fOFhYXhwYMH+OmnnxASEoLU1FTJY/PmzUNsbCx+++03dO7cWeFzEBFRzcYCioiIlMbExAQ9evTApEmTEBUVhaysLFy6dAkBAQEoKiqCs7MzTE1NsWvXLvz111+Ij4/Hhg0bFDpXdnY2fv75Z8yePRu9e/dGjx49MG/ePAiCgNjYWERERMDX1xcGBgbIyclBTk4OHj9+XMmvmIiI3ncsoIiISKlWrVoFV1dXrFmzBn369MH48ePx/Plz/P7779DW1sb06dPxwQcfwM3NDYsWLcK0adMUOs+CBQvQsWNH9O/fHwDg5+eHa9euYceOHThy5AiAl7NQ9vb2ktuQIUMq7XUSEVHNIBIEQVB1EkRERERERNUBZ6CIiIiIiIjkxAKKiIiIiIhITiygiIiIiIiI5MQCioiIiIiISE4soIiIiIiIiOTEAoqIiIiIiEhOLKCIiIiIiIjkxAKKiIiIiIhITiygiIiIiIiI5MQCioiIiIiISE4soIiIiIiIiOTEAoqIiIiIiEhO/w/Ow9xz1MtYLQAAAABJRU5ErkJggg=="
},
"metadata": {},
"output_type": "display_data"
}
],
"execution_count": 49
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.003682Z",
"start_time": "2024-08-24T00:15:19.254225Z"
}
},
"cell_type": "code",
"source": [
"pivot_table = df.pivot_table(index=['suffix1', 'suffix2'], columns='predicate', aggfunc='size', fill_value=0).reset_index()\n",
"pivot_table.to_csv(\"tmp/pivot_table.csv\", index=False)\n",
"pivot_table"
],
"id": "575eb6dbb6024008",
"outputs": [
{
"data": {
"text/plain": [
"predicate suffix1 suffix2 NO_REL obo:chebi#is_conjugate_acid_of \\\n",
"0 (1+) (1-) 2 2 \n",
"1 (1+) (2+) 19 0 \n",
"2 (1+) (2-) 3 2 \n",
"3 (1+) (3+) 11 0 \n",
"4 (1+) (3-) 2 0 \n",
".. ... ... ... ... \n",
"271 zwitterion(1-) ate 1 0 \n",
"272 zwitterion(1-) zwitterion 0 0 \n",
"273 zwitterion(2-) (3-) 0 1 \n",
"274 zwitterion(2-) acid 1 0 \n",
"275 zwitterion(2-) zwitterion 0 0 \n",
"\n",
"predicate obo:chebi#is_conjugate_base_of obo:chebi#is_tautomer_of \n",
"0 0 0 \n",
"1 19 0 \n",
"2 0 0 \n",
"3 0 0 \n",
"4 0 0 \n",
".. ... ... \n",
"271 0 0 \n",
"272 3 0 \n",
"273 0 0 \n",
"274 0 0 \n",
"275 1 0 \n",
"\n",
"[276 rows x 6 columns]"
],
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" predicate | \n",
" suffix1 | \n",
" suffix2 | \n",
" NO_REL | \n",
" obo:chebi#is_conjugate_acid_of | \n",
" obo:chebi#is_conjugate_base_of | \n",
" obo:chebi#is_tautomer_of | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" (1+) | \n",
" (1-) | \n",
" 2 | \n",
" 2 | \n",
" 0 | \n",
" 0 | \n",
"
\n",
" \n",
" 1 | \n",
" (1+) | \n",
" (2+) | \n",
" 19 | \n",
" 0 | \n",
" 19 | \n",
" 0 | \n",
"
\n",
" \n",
" 2 | \n",
" (1+) | \n",
" (2-) | \n",
" 3 | \n",
" 2 | \n",
" 0 | \n",
" 0 | \n",
"
\n",
" \n",
" 3 | \n",
" (1+) | \n",
" (3+) | \n",
" 11 | \n",
" 0 | \n",
" 0 | \n",
" 0 | \n",
"
\n",
" \n",
" 4 | \n",
" (1+) | \n",
" (3-) | \n",
" 2 | \n",
" 0 | \n",
" 0 | \n",
" 0 | \n",
"
\n",
" \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
"
\n",
" \n",
" 271 | \n",
" zwitterion(1-) | \n",
" ate | \n",
" 1 | \n",
" 0 | \n",
" 0 | \n",
" 0 | \n",
"
\n",
" \n",
" 272 | \n",
" zwitterion(1-) | \n",
" zwitterion | \n",
" 0 | \n",
" 0 | \n",
" 3 | \n",
" 0 | \n",
"
\n",
" \n",
" 273 | \n",
" zwitterion(2-) | \n",
" (3-) | \n",
" 0 | \n",
" 1 | \n",
" 0 | \n",
" 0 | \n",
"
\n",
" \n",
" 274 | \n",
" zwitterion(2-) | \n",
" acid | \n",
" 1 | \n",
" 0 | \n",
" 0 | \n",
" 0 | \n",
"
\n",
" \n",
" 275 | \n",
" zwitterion(2-) | \n",
" zwitterion | \n",
" 0 | \n",
" 0 | \n",
" 1 | \n",
" 0 | \n",
"
\n",
" \n",
"
\n",
"
276 rows × 6 columns
\n",
"
"
]
},
"execution_count": 50,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 50
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.005059Z",
"start_time": "2024-08-24T00:15:19.334380Z"
}
},
"cell_type": "code",
"source": "df[(df[\"suffix1\"] == \"(1+)\") & (df[\"suffix2\"] == \"NO_SUFFIX\") & (df[\"predicate\"] == CBO)]",
"id": "ecff73edc212adaa",
"outputs": [
{
"data": {
"text/plain": [
" suffix1 suffix2 predicate chem1 \\\n",
"3026 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:141055 \n",
"10936 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:58644 \n",
"12230 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:64003 \n",
"12330 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:64364 \n",
"13406 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:72567 \n",
"14208 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:75297 \n",
"14382 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:76278 \n",
"14672 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:76819 \n",
"14698 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:76922 \n",
"16426 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:86083 \n",
"16452 (1+) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:86380 \n",
"\n",
" chem2 charge_diff charge_diff_sign \\\n",
"3026 CHEBI:141057 NaN NaN \n",
"10936 CHEBI:32818 NaN NaN \n",
"12230 CHEBI:64004 NaN NaN \n",
"12330 CHEBI:10650 NaN NaN \n",
"13406 CHEBI:6438 NaN NaN \n",
"14208 CHEBI:31057 NaN NaN \n",
"14382 CHEBI:16299 NaN NaN \n",
"14672 CHEBI:15906 NaN NaN \n",
"14698 CHEBI:77055 NaN NaN \n",
"16426 CHEBI:86085 NaN NaN \n",
"16452 CHEBI:599440 NaN NaN \n",
"\n",
" stem \n",
"3026 validoxylamine B \n",
"10936 p-coumaroylagmatine \n",
"12230 N-allyl-6-chloro-1-(3-methylphenyl)-2,3,4,5-te... \n",
"12330 sumatriptan \n",
"13406 levobunolol \n",
"14208 13-deoxydaunorubicin \n",
"14382 dehydrocoformycin \n",
"14672 demethylmacrocin \n",
"14698 argemonine \n",
"16426 (Z)-p-coumaroylagmatine \n",
"16452 amorolfine "
],
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" suffix1 | \n",
" suffix2 | \n",
" predicate | \n",
" chem1 | \n",
" chem2 | \n",
" charge_diff | \n",
" charge_diff_sign | \n",
" stem | \n",
"
\n",
" \n",
" \n",
" \n",
" 3026 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:141055 | \n",
" CHEBI:141057 | \n",
" NaN | \n",
" NaN | \n",
" validoxylamine B | \n",
"
\n",
" \n",
" 10936 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:58644 | \n",
" CHEBI:32818 | \n",
" NaN | \n",
" NaN | \n",
" p-coumaroylagmatine | \n",
"
\n",
" \n",
" 12230 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:64003 | \n",
" CHEBI:64004 | \n",
" NaN | \n",
" NaN | \n",
" N-allyl-6-chloro-1-(3-methylphenyl)-2,3,4,5-te... | \n",
"
\n",
" \n",
" 12330 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:64364 | \n",
" CHEBI:10650 | \n",
" NaN | \n",
" NaN | \n",
" sumatriptan | \n",
"
\n",
" \n",
" 13406 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:72567 | \n",
" CHEBI:6438 | \n",
" NaN | \n",
" NaN | \n",
" levobunolol | \n",
"
\n",
" \n",
" 14208 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:75297 | \n",
" CHEBI:31057 | \n",
" NaN | \n",
" NaN | \n",
" 13-deoxydaunorubicin | \n",
"
\n",
" \n",
" 14382 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:76278 | \n",
" CHEBI:16299 | \n",
" NaN | \n",
" NaN | \n",
" dehydrocoformycin | \n",
"
\n",
" \n",
" 14672 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:76819 | \n",
" CHEBI:15906 | \n",
" NaN | \n",
" NaN | \n",
" demethylmacrocin | \n",
"
\n",
" \n",
" 14698 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:76922 | \n",
" CHEBI:77055 | \n",
" NaN | \n",
" NaN | \n",
" argemonine | \n",
"
\n",
" \n",
" 16426 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:86083 | \n",
" CHEBI:86085 | \n",
" NaN | \n",
" NaN | \n",
" (Z)-p-coumaroylagmatine | \n",
"
\n",
" \n",
" 16452 | \n",
" (1+) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:86380 | \n",
" CHEBI:599440 | \n",
" NaN | \n",
" NaN | \n",
" amorolfine | \n",
"
\n",
" \n",
"
\n",
"
"
]
},
"execution_count": 51,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 51
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.006179Z",
"start_time": "2024-08-24T00:15:19.586411Z"
}
},
"cell_type": "code",
"source": "df",
"id": "d20d7eb6fce43eb1",
"outputs": [
{
"data": {
"text/plain": [
" suffix1 suffix2 predicate chem1 \\\n",
"0 acid anion obo:chebi#is_conjugate_acid_of CHEBI:100147 \n",
"1 anion acid obo:chebi#is_conjugate_base_of CHEBI:62070 \n",
"2 acid NO_SUFFIX NO_REL CHEBI:10046 \n",
"3 NO_SUFFIX acid NO_REL CHEBI:10045 \n",
"4 acid ate obo:chebi#is_conjugate_acid_of CHEBI:10072 \n",
"... ... ... ... ... \n",
"17161 NO_SUFFIX (1-) obo:chebi#is_conjugate_acid_of CHEBI:130073 \n",
"17162 (1-) NO_SUFFIX obo:chebi#is_conjugate_base_of CHEBI:9162 \n",
"17163 NO_SUFFIX (1-) obo:chebi#is_conjugate_acid_of CHEBI:79317 \n",
"17164 ate acid obo:chebi#is_conjugate_base_of CHEBI:994 \n",
"17165 acid ate obo:chebi#is_conjugate_acid_of CHEBI:995 \n",
"\n",
" chem2 charge_diff charge_diff_sign \\\n",
"0 CHEBI:62070 NaN NaN \n",
"1 CHEBI:100147 NaN NaN \n",
"2 CHEBI:10045 NaN NaN \n",
"3 CHEBI:10046 NaN NaN \n",
"4 CHEBI:71201 NaN NaN \n",
"... ... ... ... \n",
"17161 CHEBI:91301 NaN NaN \n",
"17162 CHEBI:79317 NaN NaN \n",
"17163 CHEBI:9162 NaN NaN \n",
"17164 CHEBI:995 NaN NaN \n",
"17165 CHEBI:994 NaN NaN \n",
"\n",
" stem \n",
"0 nalidix \n",
"1 nalidix \n",
"2 Wyerone \n",
"3 Wyerone \n",
"4 xanthuren \n",
"... ... \n",
"17161 5,20-diHEPE \n",
"17162 sinigrin \n",
"17163 sinigrin \n",
"17164 cis,cis-2-amino-3-(3-oxoprop-1-enyl)but-2-enedio \n",
"17165 cis,cis-2-amino-3-(3-oxoprop-1-enyl)but-2-enedio \n",
"\n",
"[17166 rows x 8 columns]"
],
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" suffix1 | \n",
" suffix2 | \n",
" predicate | \n",
" chem1 | \n",
" chem2 | \n",
" charge_diff | \n",
" charge_diff_sign | \n",
" stem | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" acid | \n",
" anion | \n",
" obo:chebi#is_conjugate_acid_of | \n",
" CHEBI:100147 | \n",
" CHEBI:62070 | \n",
" NaN | \n",
" NaN | \n",
" nalidix | \n",
"
\n",
" \n",
" 1 | \n",
" anion | \n",
" acid | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:62070 | \n",
" CHEBI:100147 | \n",
" NaN | \n",
" NaN | \n",
" nalidix | \n",
"
\n",
" \n",
" 2 | \n",
" acid | \n",
" NO_SUFFIX | \n",
" NO_REL | \n",
" CHEBI:10046 | \n",
" CHEBI:10045 | \n",
" NaN | \n",
" NaN | \n",
" Wyerone | \n",
"
\n",
" \n",
" 3 | \n",
" NO_SUFFIX | \n",
" acid | \n",
" NO_REL | \n",
" CHEBI:10045 | \n",
" CHEBI:10046 | \n",
" NaN | \n",
" NaN | \n",
" Wyerone | \n",
"
\n",
" \n",
" 4 | \n",
" acid | \n",
" ate | \n",
" obo:chebi#is_conjugate_acid_of | \n",
" CHEBI:10072 | \n",
" CHEBI:71201 | \n",
" NaN | \n",
" NaN | \n",
" xanthuren | \n",
"
\n",
" \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
"
\n",
" \n",
" 17161 | \n",
" NO_SUFFIX | \n",
" (1-) | \n",
" obo:chebi#is_conjugate_acid_of | \n",
" CHEBI:130073 | \n",
" CHEBI:91301 | \n",
" NaN | \n",
" NaN | \n",
" 5,20-diHEPE | \n",
"
\n",
" \n",
" 17162 | \n",
" (1-) | \n",
" NO_SUFFIX | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:9162 | \n",
" CHEBI:79317 | \n",
" NaN | \n",
" NaN | \n",
" sinigrin | \n",
"
\n",
" \n",
" 17163 | \n",
" NO_SUFFIX | \n",
" (1-) | \n",
" obo:chebi#is_conjugate_acid_of | \n",
" CHEBI:79317 | \n",
" CHEBI:9162 | \n",
" NaN | \n",
" NaN | \n",
" sinigrin | \n",
"
\n",
" \n",
" 17164 | \n",
" ate | \n",
" acid | \n",
" obo:chebi#is_conjugate_base_of | \n",
" CHEBI:994 | \n",
" CHEBI:995 | \n",
" NaN | \n",
" NaN | \n",
" cis,cis-2-amino-3-(3-oxoprop-1-enyl)but-2-enedio | \n",
"
\n",
" \n",
" 17165 | \n",
" acid | \n",
" ate | \n",
" obo:chebi#is_conjugate_acid_of | \n",
" CHEBI:995 | \n",
" CHEBI:994 | \n",
" NaN | \n",
" NaN | \n",
" cis,cis-2-amino-3-(3-oxoprop-1-enyl)but-2-enedio | \n",
"
\n",
" \n",
"
\n",
"
17166 rows × 8 columns
\n",
"
"
]
},
"execution_count": 52,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 52
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Create is-a map",
"id": "d17e17831c235596"
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.006730Z",
"start_time": "2024-08-24T00:15:19.687885Z"
}
},
"cell_type": "code",
"source": "is_as = list(chebi.relationships(predicates=[IS_A]))",
"id": "5a77f734fba5ae24",
"outputs": [],
"execution_count": 53
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.006862Z",
"start_time": "2024-08-24T00:15:25.255263Z"
}
},
"cell_type": "code",
"source": [
"is_a_map = defaultdict(list)\n",
"for s, _, o in is_as:\n",
" is_a_map[s].append(o)"
],
"id": "7a7a9eb7c9110c39",
"outputs": [],
"execution_count": 54
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"## Fetch Uniprot Synonyms\n",
"\n",
"These are bio-friendly synonyms."
],
"id": "2e35a6a093aae65c"
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.006961Z",
"start_time": "2024-08-24T00:15:25.397779Z"
}
},
"cell_type": "code",
"outputs": [],
"execution_count": 55,
"source": [
"from semsql.sqla.semsql import OwlAxiomAnnotation\n",
"\n",
"q = session.query(OwlAxiomAnnotation)\n",
"axiom_anns = list(q)"
],
"id": "6ac6247a4be899da"
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.014295Z",
"start_time": "2024-08-24T00:15:38.870996Z"
}
},
"cell_type": "code",
"source": "len(axiom_anns)",
"id": "c2c109cbfa2e46a5",
"outputs": [
{
"data": {
"text/plain": [
"716075"
]
},
"execution_count": 56,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 56
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.014707Z",
"start_time": "2024-08-24T00:15:39.176170Z"
}
},
"cell_type": "code",
"source": "up_axiom_anns = [row for row in axiom_anns if row.annotation_predicate == \"oio:hasDbXref\" and row.annotation_value == \"UniProt\"]",
"id": "6132886244ebdbe",
"outputs": [],
"execution_count": 57
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.014812Z",
"start_time": "2024-08-24T00:15:39.867494Z"
}
},
"cell_type": "code",
"source": [
"bio_syn_map = {row.subject: row.value for row in axiom_anns if row.annotation_predicate == \"oio:hasDbXref\" and row.annotation_value == \"UniProt\"}\n",
"len(bio_syn_map)"
],
"id": "a021b7d4e91c99e0",
"outputs": [
{
"data": {
"text/plain": [
"16393"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 58
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.014924Z",
"start_time": "2024-08-24T00:15:40.580153Z"
}
},
"cell_type": "code",
"source": "len(bio_syn_map)",
"id": "8ad8345e0db49f7a",
"outputs": [
{
"data": {
"text/plain": [
"16393"
]
},
"execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 59
},
{
"metadata": {},
"cell_type": "markdown",
"source": "",
"id": "fc455907eee67b8a"
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"## RHEA pH Mapping\n",
"\n",
"RHEA provides a table that maps CHEBI IDs to their pH 7.3 stable forms. The mapping may be reflexive\n",
"print((e.g.a stable form will map to itself))\n",
"\n",
"The mappings may not be complete - in particular only leaf nodes are mapped."
],
"id": "d67387abc3bd9dc7"
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.015055Z",
"start_time": "2024-08-24T00:15:40.903477Z"
}
},
"cell_type": "code",
"source": "\n",
"id": "9ab37d9db461f843",
"outputs": [],
"execution_count": 59
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.015145Z",
"start_time": "2024-08-24T00:15:41.217559Z"
}
},
"cell_type": "code",
"source": "",
"id": "6c6c70e637f11eb9",
"outputs": [],
"execution_count": null
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.015227Z",
"start_time": "2024-08-24T00:15:41.855434Z"
}
},
"cell_type": "code",
"source": [
"import pystow\n",
"ph_mapping_df = pystow.ensure_csv(\"rhea\", url=\"https://ftp.expasy.org/databases/rhea/tsv/chebi_pH7_3_mapping.tsv\")\n",
"for col in [\"CHEBI\", \"CHEBI_PH7_3\"]:\n",
" ph_mapping_df[col] = \"CHEBI:\" + ph_mapping_df[col].astype(str)\n",
"\n"
],
"id": "e5792b2c75c4a675",
"outputs": [],
"execution_count": 60
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.015311Z",
"start_time": "2024-08-24T00:15:41.963911Z"
}
},
"cell_type": "code",
"source": "ph_mapping_df",
"id": "7cc020b7576cc449",
"outputs": [
{
"data": {
"text/plain": [
" CHEBI CHEBI_PH7_3 ORIGIN\n",
"0 CHEBI:3 CHEBI:3 computation\n",
"1 CHEBI:7 CHEBI:7 computation\n",
"2 CHEBI:8 CHEBI:8 computation\n",
"3 CHEBI:19 CHEBI:19 computation\n",
"4 CHEBI:20 CHEBI:20 computation\n",
"... ... ... ...\n",
"119802 CHEBI:691037 CHEBI:691037 computation\n",
"119803 CHEBI:691622 CHEBI:691622 computation\n",
"119804 CHEBI:741548 CHEBI:132939 computation\n",
"119805 CHEBI:744019 CHEBI:744019 computation\n",
"119806 CHEBI:746859 CHEBI:746859 computation\n",
"\n",
"[119807 rows x 3 columns]"
],
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" CHEBI | \n",
" CHEBI_PH7_3 | \n",
" ORIGIN | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" CHEBI:3 | \n",
" CHEBI:3 | \n",
" computation | \n",
"
\n",
" \n",
" 1 | \n",
" CHEBI:7 | \n",
" CHEBI:7 | \n",
" computation | \n",
"
\n",
" \n",
" 2 | \n",
" CHEBI:8 | \n",
" CHEBI:8 | \n",
" computation | \n",
"
\n",
" \n",
" 3 | \n",
" CHEBI:19 | \n",
" CHEBI:19 | \n",
" computation | \n",
"
\n",
" \n",
" 4 | \n",
" CHEBI:20 | \n",
" CHEBI:20 | \n",
" computation | \n",
"
\n",
" \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
"
\n",
" \n",
" 119802 | \n",
" CHEBI:691037 | \n",
" CHEBI:691037 | \n",
" computation | \n",
"
\n",
" \n",
" 119803 | \n",
" CHEBI:691622 | \n",
" CHEBI:691622 | \n",
" computation | \n",
"
\n",
" \n",
" 119804 | \n",
" CHEBI:741548 | \n",
" CHEBI:132939 | \n",
" computation | \n",
"
\n",
" \n",
" 119805 | \n",
" CHEBI:744019 | \n",
" CHEBI:744019 | \n",
" computation | \n",
"
\n",
" \n",
" 119806 | \n",
" CHEBI:746859 | \n",
" CHEBI:746859 | \n",
" computation | \n",
"
\n",
" \n",
"
\n",
"
119807 rows × 3 columns
\n",
"
"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 61
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.016299Z",
"start_time": "2024-08-24T00:15:42.311545Z"
}
},
"cell_type": "code",
"source": "ph_mapping = dict(zip(ph_mapping_df['CHEBI'], ph_mapping_df['CHEBI_PH7_3']))",
"id": "afd7468af0f9a848",
"outputs": [],
"execution_count": 62
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.016568Z",
"start_time": "2024-08-24T00:15:42.383741Z"
}
},
"cell_type": "code",
"source": "len(ph_mapping)",
"id": "feb64fe9e36bbdec",
"outputs": [
{
"data": {
"text/plain": [
"119807"
]
},
"execution_count": 63,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 63
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.016898Z",
"start_time": "2024-08-24T00:15:42.436633Z"
}
},
"cell_type": "code",
"source": [
"# we expected leaf and leaf-y nodes to be mapped\n",
"assert ph_mapping[CYSTEINATE_1_MINUS] == CYSTEINE_ZWITTERION"
],
"id": "f960fab3cbd8c968",
"outputs": [],
"execution_count": 64
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.017042Z",
"start_time": "2024-08-24T00:15:42.754150Z"
}
},
"cell_type": "code",
"source": "assert ph_mapping[CITRIC_ACID] != CITRIC_ACID",
"id": "5d984c637fc464a6",
"outputs": [],
"execution_count": 65
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.017145Z",
"start_time": "2024-08-24T00:15:43.078525Z"
}
},
"cell_type": "code",
"source": [
"# reflexivity\n",
"assert ph_mapping[L_CYSTEINE_ZWITTERION] == L_CYSTEINE_ZWITTERION\n",
"assert ph_mapping[CYSTEINE_ZWITTERION] == CYSTEINE_ZWITTERION"
],
"id": "6215936dbaf1e9f7",
"outputs": [],
"execution_count": 66
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.017350Z",
"start_time": "2024-08-24T00:15:43.399428Z"
}
},
"cell_type": "code",
"source": "assert ph_mapping[WATER] == WATER",
"id": "3f295b2fce03ea7f",
"outputs": [],
"execution_count": 67
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.017526Z",
"start_time": "2024-08-24T00:15:43.717362Z"
}
},
"cell_type": "code",
"source": [
"# groupings are not mapped\n",
"assert AMINO_ACID not in ph_mapping"
],
"id": "41959f4eb1f532e7",
"outputs": [],
"execution_count": 68
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.017637Z",
"start_time": "2024-08-24T00:15:44.072155Z"
}
},
"cell_type": "code",
"source": [
"# create a reverse mapping\n",
"\n",
"rev_ph_mapping = defaultdict(list)\n",
"for k, v in ph_mapping.items():\n",
" rev_ph_mapping[v].append(k)\n",
" \n",
"len(rev_ph_mapping)"
],
"id": "5a139dee44a44030",
"outputs": [
{
"data": {
"text/plain": [
"111077"
]
},
"execution_count": 69,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 69
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.017768Z",
"start_time": "2024-08-24T00:15:45.014725Z"
}
},
"cell_type": "code",
"source": [
"assert L_CYSTEINE_ZWITTERION in rev_ph_mapping[L_CYSTEINE_ZWITTERION]\n",
"assert CYSTEINATE_1_MINUS in rev_ph_mapping[CYSTEINE_ZWITTERION]\n",
"\n"
],
"id": "9120365808e9bc04",
"outputs": [],
"execution_count": 70
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.017934Z",
"start_time": "2024-08-24T00:15:45.024002Z"
}
},
"cell_type": "code",
"source": [
"rhea_sccs = []\n",
"rhea_singletons = []\n",
"for _, clique in rev_ph_mapping.items():\n",
" if len(clique) > 1:\n",
" rhea_sccs.append(set(clique))\n",
" else:\n",
" rhea_singletons.append(clique[0])\n",
"\n",
"len(rhea_sccs), len(rhea_singletons)"
],
"id": "7c88440d52718d0a",
"outputs": [
{
"data": {
"text/plain": [
"(8140, 102937)"
]
},
"execution_count": 71,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 71
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"## Pick Canonical from Conjugate Cliques\n",
"\n",
"For each clique, pick the canonical term."
],
"id": "9ab6512f389c6a93"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.018170Z",
"start_time": "2024-08-24T00:15:45.411105Z"
}
},
"cell_type": "code",
"source": [
"\n",
"arbitrary_canonical_map = {}\n",
"def pick_canonical(ids: List[str]) -> str:\n",
" \"\"\"\n",
" Pick the canonical term from a list of ids.\n",
" \n",
" Priority order:\n",
" \n",
" 1. pH mapping\n",
" 2. Uniprot synonyms\n",
" 3. Charge 0\n",
" \n",
" :param ids: \n",
" :return: \n",
" \"\"\"\n",
" # ensure deterministic order\n",
" ids = sorted(ids)\n",
" for id in ids:\n",
" if id in ph_mapping:\n",
" return ph_mapping[id]\n",
" for id in ids:\n",
" if id in bio_syn_map:\n",
" return id\n",
" for id in ids:\n",
" if id in charges and charges[id] == 0:\n",
" return id\n",
" # prioritize shorter\n",
" ids = sorted(ids, key=lambda x: len(labels.get(x)))\n",
" for id in ids:\n",
" if id not in charges:\n",
" # last resort\n",
" return id\n",
" if len(ids) == 1:\n",
" return ids[0]\n",
" arbitrary_canonical_map[tuple(ids)] = ids[0]\n",
" return ids[0]\n",
" # raise ValueError(f\"Could not find canonical for {ids}\")\n",
"\n",
"assert pick_canonical([CITRIC_ACID]) == ph_mapping[CITRIC_ACID]\n",
"assert pick_canonical([L_CYSTEINE_ZWITTERION]) == L_CYSTEINE_ZWITTERION\n",
"assert pick_canonical([CYSTEINE_ZWITTERION, CYSTEINATE_1_MINUS, CYSTEINIUM]) == CYSTEINE_ZWITTERION\n",
"assert pick_canonical([AMINO_ACID, AMINO_ACID_ANION]) == AMINO_ACID\n",
"assert pick_canonical([ALPHA_AMINO_ACID, ALPHA_AMINO_ACID_ANION, ALPHA_AMINO_ACID_ZWITTERION]) == ALPHA_AMINO_ACID_ZWITTERION\n",
" "
],
"id": "e878bc530b346f26",
"outputs": [],
"execution_count": 72
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.018305Z",
"start_time": "2024-08-24T00:15:45.775801Z"
}
},
"cell_type": "code",
"source": "pick_canonical([ALPHA_AMINO_ACID, ALPHA_AMINO_ACID_ANION, ALPHA_AMINO_ACID_ZWITTERION])",
"id": "437cc21e6c84393c",
"outputs": [
{
"data": {
"text/plain": [
"'CHEBI:78608'"
]
},
"execution_count": 73,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 73
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.018621Z",
"start_time": "2024-08-24T00:15:46.520120Z"
}
},
"cell_type": "code",
"source": "",
"id": "25942fc6d94bd016",
"outputs": [],
"execution_count": null
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.018775Z",
"start_time": "2024-08-24T00:15:46.528398Z"
}
},
"cell_type": "code",
"source": [
"from typing import Set, Tuple\n",
"\n",
"\n",
"def create_canonical_map(scc_sets: List[Set[str]]) -> Tuple[Dict[str, Set[str]], Dict[str, str]]:\n",
" \"\"\"\n",
" Create a mapping between canonical and members of the strongly connected components\n",
" :param scc_sets: \n",
" :return: \n",
" \"\"\"\n",
" canonical_to_members = {}\n",
" for scc in scc_sets:\n",
" canonical = pick_canonical(scc)\n",
" canonical_to_members[canonical] = scc\n",
" members_to_canonical = {m: c for c, ms in canonical_to_members.items() for m in ms}\n",
" return (canonical_to_members, members_to_canonical)\n",
"\n",
"canonical_to_members, members_to_canonical = create_canonical_map(sccs)\n",
"assert len(canonical_to_members) > 8000\n",
"assert members_to_canonical[CYSTEINE_ZWITTERION] == CYSTEINE_ZWITTERION\n",
"assert members_to_canonical[CYSTEINATE_1_MINUS] == CYSTEINE_ZWITTERION"
],
"id": "5ac60d2c2ca3e36d",
"outputs": [],
"execution_count": 74
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.018887Z",
"start_time": "2024-08-24T00:15:46.951638Z"
}
},
"cell_type": "code",
"source": "",
"id": "820efde068eb28fa",
"outputs": [],
"execution_count": null
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.019140Z",
"start_time": "2024-08-24T00:15:47.312693Z"
}
},
"cell_type": "code",
"source": "# assert CITRIC_ACID in members_to_canonical\n",
"id": "b922566d4a6977f1",
"outputs": [],
"execution_count": 75
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.019229Z",
"start_time": "2024-08-24T00:15:47.673888Z"
}
},
"cell_type": "code",
"source": "##",
"id": "986db0b5676b022",
"outputs": [],
"execution_count": 76
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.019333Z",
"start_time": "2024-08-24T00:15:48.034616Z"
}
},
"cell_type": "code",
"source": "",
"id": "144827585302bdb6",
"outputs": [],
"execution_count": null
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.019416Z",
"start_time": "2024-08-24T00:15:48.390743Z"
}
},
"cell_type": "code",
"source": [
"# assess completeness of the sccs\n",
"missing_in_conjugate_sccs = []\n",
"for chem in stem_to_chem.keys():\n",
" if chem not in canonical_to_members:\n",
" missing_in_conjugate_sccs.append(chem)\n",
" \n",
"len(missing_in_conjugate_sccs)\n"
],
"id": "a021fe7fed571235",
"outputs": [
{
"data": {
"text/plain": [
"31415"
]
},
"execution_count": 77,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 77
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.023327Z",
"start_time": "2024-08-24T00:15:48.747771Z"
}
},
"cell_type": "code",
"source": [
"missing_in_lexical_analysis = []\n",
"for chem in canonical_to_members.keys():\n",
" if chem not in chem_to_stem:\n",
" missing_in_lexical_analysis.append(chem)\n",
" \n",
"len(missing_in_lexical_analysis)"
],
"id": "803cbc15883d0c19",
"outputs": [
{
"data": {
"text/plain": [
"1927"
]
},
"execution_count": 78,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 78
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.023768Z",
"start_time": "2024-08-24T00:15:49.110226Z"
}
},
"cell_type": "code",
"source": [
"\n",
"for _, vmap in stem_to_chem.items():\n",
" for v1 in vmap.values():\n",
" for v2 in vmap.values():\n",
" if v1 > v2:\n",
" rel = (v1, \"?\", v2)\n",
" if rel not in conjrels:\n",
" conjrels.append(rel)\n",
" # conj_graph.add_edge(v1, v2))\n",
"conj_graph = calculate_conj_graph(conjrels)\n",
"full_sccs = list(nx.strongly_connected_components(conj_graph))\n",
"len(full_sccs)"
],
"id": "c49abac57eb8498",
"outputs": [
{
"data": {
"text/plain": [
"9321"
]
},
"execution_count": 79,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 79
},
{
"metadata": {},
"cell_type": "markdown",
"source": "This number is the total number of cliques we will use",
"id": "ddcaccb58acaefc1"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.023919Z",
"start_time": "2024-08-24T00:15:55.326335Z"
}
},
"cell_type": "code",
"source": "",
"id": "a426f8f57483a9e9",
"outputs": [],
"execution_count": null
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024Z",
"start_time": "2024-08-24T00:15:55.677465Z"
}
},
"cell_type": "code",
"source": [
"canonical_to_members, members_to_canonical = create_canonical_map(full_sccs)\n",
"assert len(canonical_to_members) > 9000"
],
"id": "7e12327ea167d1f7",
"outputs": [],
"execution_count": 80
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024089Z",
"start_time": "2024-08-24T00:15:56.034231Z"
}
},
"cell_type": "code",
"source": "labels[members_to_canonical[AMINO_ACID]]",
"id": "47d4065d938af84f",
"outputs": [
{
"data": {
"text/plain": [
"'amino acid'"
]
},
"execution_count": 81,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 81
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024185Z",
"start_time": "2024-08-24T00:15:56.382058Z"
}
},
"cell_type": "code",
"source": "assert members_to_canonical[AMINO_ACID_ANION] == AMINO_ACID",
"id": "8ae582d277664a82",
"outputs": [],
"execution_count": 82
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024259Z",
"start_time": "2024-08-24T00:15:56.732138Z"
}
},
"cell_type": "code",
"source": "len(members_to_canonical)",
"id": "69daa1cf3e2af9a4",
"outputs": [
{
"data": {
"text/plain": [
"19624"
]
},
"execution_count": 83,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 83
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Exclusion List",
"id": "ff7443169be8ebfe"
},
{
"cell_type": "code",
"source": [
"ions = list(chebi.descendants(ION, [IS_A]))\n",
"exclusion_list = [ion for ion in ions if ion not in canonical_to_members]"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024351Z",
"start_time": "2024-08-24T00:15:57.086873Z"
}
},
"id": "6f9c14d520e38499",
"outputs": [],
"execution_count": 84
},
{
"cell_type": "code",
"source": [
"len(exclusion_list)"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024432Z",
"start_time": "2024-08-24T00:16:00.492363Z"
}
},
"id": "707d750e6f27b0d",
"outputs": [
{
"data": {
"text/plain": [
"6762"
]
},
"execution_count": 85,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 85
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024524Z",
"start_time": "2024-08-24T00:16:00.868151Z"
}
},
"cell_type": "code",
"source": [
"# assert AAAC in exclusion_list, f\"expected {AAAC} in exclusion_list\"\n",
"assert CITRIC_ACID not in exclusion_list\n",
"assert AMINO_ACID not in exclusion_list"
],
"id": "d4012fde318a42cb",
"outputs": [],
"execution_count": 86
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024605Z",
"start_time": "2024-08-24T00:16:01.233771Z"
}
},
"cell_type": "code",
"source": [
"def rewire(id: str) -> Optional[str]:\n",
" \"\"\"\n",
" Rewire an ID to its canonical form, if it is not in the exclusion list\n",
" :param id: \n",
" :return: \n",
" \"\"\"\n",
" rewired = members_to_canonical.get(id, id)\n",
" if rewired in exclusion_list:\n",
" return None\n",
" return rewired"
],
"id": "eb60026fd7282828",
"outputs": [],
"execution_count": 87
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024679Z",
"start_time": "2024-08-24T00:16:01.588085Z"
}
},
"cell_type": "code",
"source": "rewire(AAAE)",
"id": "d2f7eb4cab17323e",
"outputs": [
{
"data": {
"text/plain": [
"'CHEBI:83410'"
]
},
"execution_count": 88,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 88
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024768Z",
"start_time": "2024-08-24T00:16:01.940983Z"
}
},
"cell_type": "code",
"source": [
"assert rewire(AAAE) != AAAE\n",
"assert rewire(AAAE) not in exclusion_list"
],
"id": "458724079e756895",
"outputs": [],
"execution_count": 89
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Generate Ontology",
"id": "3c586153bb91baeb"
},
{
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024851Z",
"start_time": "2024-08-24T00:16:02.295171Z"
}
},
"cell_type": "code",
"source": "",
"id": "8ef381b8af27a6d7",
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": [
"from typing import Tuple\n",
"from pydantic import BaseModel\n",
"\n",
"\n",
"\n",
"class Term(BaseModel):\n",
" stanza_type: str = \"Term\"\n",
" id: str\n",
" label: str\n",
" synonyms: Optional[List[str]] = None\n",
" xrefs: Optional[List[str]] = []\n",
" alt_ids: Optional[List[str]] = None\n",
" parents: List[str] = []\n",
" relationships: List[Tuple[str, str]] = []\n",
" inchi: Optional[str] = None\n",
" physiologically_stable_form: Optional[str] = None\n",
" comments: List[str] = []\n",
" \n",
" def as_obo(self) -> str:\n",
" name = self.label.replace('{', r'\\{')\n",
" lines = [\n",
" f\"[{self.stanza_type}]\",\n",
" f\"id: {self.id}\",\n",
" f\"name: {name}\",\n",
" ]\n",
" lines += [f\"synonym: {s}\" for s in self.synonyms or []]\n",
" lines += [f\"alt_id: {alt_id}\" for alt_id in self.alt_ids or []]\n",
" lines += [f\"is_a: {is_a}\" for is_a in self.parents or []]\n",
" lines += [f\"xref: {xref}\" for xref in self.xrefs or []]\n",
" lines += [f\"relationship: {p} {v}\" for p, v in self.relationships or []]\n",
" lines += [f\"comment: {'; '.join(self.comments)}\"] if self.comments else []\n",
" lines += [f\"property_value: chemrof:inchi_string \\\"{self.inchi}\\\" xsd:string\"] if self.inchi else []\n",
" lines += [f\"property_value: chemrof:has_physiologically_stable_form {self.physiologically_stable_form}\"] if self.physiologically_stable_form else []\n",
" lines += [\"\"]\n",
" return \"\\n\".join(lines)\n",
" \n",
" \n",
"class Ontology(BaseModel):\n",
" terms: List[Term] = []\n",
" \n",
" def as_obo(self) -> str:\n",
" lines = [\n",
" f\"ontology: chebi-slim\",\n",
" \"idspace: chemrof https://w3id.org/chemrof/\",\n",
" \"\",\n",
" ]\n",
" return \"\\n\".join(lines + [t.as_obo() for t in self.terms])\n",
" \n",
" \n"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.024923Z",
"start_time": "2024-08-24T00:16:02.649433Z"
}
},
"id": "e1d93ec81dd98d00",
"outputs": [],
"execution_count": 90
},
{
"cell_type": "code",
"source": [
"BAD_SUFFIXES = [\"zwitterion\", \"ion\", \"(1+)\", \"(2+)\"]\n",
"\n",
"def make_term(id: str) -> Optional[Term]:\n",
" \"\"\"\n",
" Make a term from an ID\n",
" \n",
" :param id: \n",
" :return: \n",
" \"\"\"\n",
" if id in exclusion_list:\n",
" return None\n",
" if members_to_canonical.get(id, id) != id:\n",
" # non-canonical members are not included\n",
" return None\n",
" #if id not in initial_terms:\n",
" # # filter for testing\n",
" # return None\n",
" label = labels.get(id, id)\n",
" if id in bio_syn_map:\n",
" label = bio_syn_map[id]\n",
" else:\n",
" for suffix in BAD_SUFFIXES:\n",
" suffix = \" \" + suffix\n",
" if label.endswith(suffix):\n",
" label = label.replace(suffix, \"\")\n",
" term = Term(id=id, label=label)\n",
" alt_ids = [x for x in canonical_to_members.get(id, []) if x != id]\n",
" if id in ph_mapping:\n",
" term.physiologically_stable_form = ph_mapping[id]\n",
" if alt_ids:\n",
" term.alt_ids = alt_ids\n",
" else:\n",
" alt_ids = []\n",
" equiv_set = [id] + alt_ids\n",
" comments = []\n",
" for alt_id in equiv_set:\n",
" for parent in is_a_map.get(alt_id, []):\n",
" rewired_parent = rewire(parent)\n",
" if rewired_parent and rewired_parent not in term.parents:\n",
" term.parents.append(rewired_parent)\n",
" if rewired_parent != parent or alt_id != id:\n",
" comments.append(f\"Parent {rewired_parent} was rewired from {alt_id} to {parent}\")\n",
" for (p, o) in preserved_rels_by_subject.get(alt_id, []):\n",
" rewired_o = rewire(o)\n",
" if rewired_o and (p, rewired_o) not in term.relationships:\n",
" term.relationships.append((p, rewired_o))\n",
" # TODO: xrefs\n",
" for xref in xrefs.get(alt_id, []):\n",
" if xref.startswith(\"PMID:\"):\n",
" continue\n",
" term.xrefs.append(xref)\n",
" if alt_id in inchis:\n",
" if not term.inchi:\n",
" term.inchi = inchis[alt_id]\n",
" term.comments = comments\n",
" return term\n",
"\n",
"\n",
"#assert L_CYSTEINE_ZWITTERION in initial_terms\n",
"t = make_term(L_CYSTEINE_ZWITTERION)\n",
"assert t.label == \"L-cysteine\"\n",
"print(t.as_obo())"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025001Z",
"start_time": "2024-08-24T00:16:03.023772Z"
}
},
"id": "b196288c3d4bc41c",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Term]\n",
"id: CHEBI:35235\n",
"name: L-cysteine\n",
"alt_id: CHEBI:32442\n",
"alt_id: CHEBI:17561\n",
"alt_id: CHEBI:32445\n",
"alt_id: CHEBI:32443\n",
"is_a: CHEBI:35237\n",
"is_a: CHEBI:59869\n",
"is_a: CHEBI:26650\n",
"is_a: CHEBI:83813\n",
"xref: Gmelin:49993\n",
"xref: Reaxys:4128886\n",
"xref: Gmelin:325857\n",
"xref: Beilstein:4128886\n",
"xref: YMDB:YMDB00046\n",
"xref: Wikipedia:Cysteine\n",
"xref: Reaxys:1721408\n",
"xref: PDBeChem:CYS\n",
"xref: MetaCyc:CYS\n",
"xref: KNApSAcK:C00001351\n",
"xref: KEGG:D00026\n",
"xref: KEGG:C00097\n",
"xref: HMDB:HMDB0000574\n",
"xref: Gmelin:49991\n",
"xref: ECMDB:ECMDB00574\n",
"xref: Drug_Central:769\n",
"xref: DrugBank:DB00151\n",
"xref: CAS:52-90-4\n",
"xref: Beilstein:1721408\n",
"xref: Gmelin:325860\n",
"xref: Reaxys:5921923\n",
"xref: Gmelin:325856\n",
"xref: Beilstein:5921923\n",
"relationship: RO:0018039 CHEBI:35236\n",
"relationship: RO:0000087 CHEBI:78675\n",
"relationship: RO:0000087 CHEBI:64577\n",
"relationship: RO:0000087 CHEBI:77703\n",
"relationship: RO:0000087 CHEBI:77746\n",
"comment: Parent CHEBI:59869 was rewired from CHEBI:32442 to CHEBI:59814; Parent CHEBI:26650 was rewired from CHEBI:17561 to CHEBI:26650; Parent CHEBI:83813 was rewired from CHEBI:17561 to CHEBI:83813\n",
"property_value: chemrof:inchi_string \"InChI=1S/C3H7NO2S/c4-2(1-7)3(5)6/h2,7H,1,4H2,(H,5,6)/t2-/m0/s1\" xsd:string\n",
"property_value: chemrof:has_physiologically_stable_form CHEBI:35235\n",
"\n"
]
}
],
"execution_count": 91
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025086Z",
"start_time": "2024-08-24T00:16:03.394309Z"
}
},
"cell_type": "code",
"source": "assert rewire(is_a_map[CORD_E][0]) not in exclusion_list",
"id": "2a3ed58115eabd33",
"outputs": [],
"execution_count": 92
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025170Z",
"start_time": "2024-08-24T00:16:03.744976Z"
}
},
"cell_type": "code",
"source": [
"t = make_term(CORD_E)\n",
"print(t.as_obo())\n"
],
"id": "238e38614ea62be7",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Term]\n",
"id: CHEBI:213754\n",
"name: Cordycepamide E\n",
"is_a: CHEBI:83410\n",
"comment: Parent CHEBI:83410 was rewired from CHEBI:213754 to CHEBI:46874\n",
"property_value: chemrof:inchi_string \"InChI=1S/C15H19NO4/c1-9(2)13-14(18)16(3)12(15(19)20-13)8-10-4-6-11(17)7-5-10/h4-7,9,12-13,17H,8H2,1-3H3/t12-,13+/m0/s1\" xsd:string\n",
"property_value: chemrof:has_physiologically_stable_form CHEBI:213754\n",
"\n"
]
}
],
"execution_count": 93
},
{
"cell_type": "code",
"source": [
"\n",
"# assert CYSTEINE_ZWITTERION in initial_terms\n",
"t = make_term(CYSTEINE_ZWITTERION)\n",
"assert t.label == \"cysteine\"\n",
"assert \"CHEBI:78608\" in t.parents\n",
"print(t.as_obo())"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025253Z",
"start_time": "2024-08-24T00:16:04.096810Z"
}
},
"id": "bc403819eabb21f5",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Term]\n",
"id: CHEBI:35237\n",
"name: cysteine\n",
"alt_id: CHEBI:32458\n",
"alt_id: CHEBI:32456\n",
"alt_id: CHEBI:32457\n",
"alt_id: CHEBI:15356\n",
"is_a: CHEBI:33709\n",
"is_a: CHEBI:78608\n",
"is_a: CHEBI:26834\n",
"is_a: CHEBI:62031\n",
"xref: Gmelin:49992\n",
"xref: Gmelin:325859\n",
"xref: Reaxys:4128885\n",
"xref: Gmelin:363235\n",
"xref: Beilstein:4128885\n",
"xref: Gmelin:49990\n",
"xref: Wikipedia:Cysteine\n",
"xref: Reaxys:1721406\n",
"xref: KNApSAcK:C00007323\n",
"xref: KNApSAcK:C00001351\n",
"xref: KEGG:C00736\n",
"xref: Gmelin:2933\n",
"xref: CAS:3374-22-9\n",
"xref: Beilstein:1721406\n",
"relationship: BFO:0000051 CHEBI:50326\n",
"relationship: RO:0000087 CHEBI:78675\n",
"comment: Parent CHEBI:33709 was rewired from CHEBI:35237 to CHEBI:35238; Parent CHEBI:78608 was rewired from CHEBI:32458 to CHEBI:33719; Parent CHEBI:26834 was rewired from CHEBI:32456 to CHEBI:63470; Parent CHEBI:62031 was rewired from CHEBI:15356 to CHEBI:26167\n",
"property_value: chemrof:inchi_string \"InChI=1S/C3H7NO2S/c4-2(1-7)3(5)6/h2,7H,1,4H2,(H,5,6)\" xsd:string\n",
"property_value: chemrof:has_physiologically_stable_form CHEBI:35237\n",
"\n"
]
}
],
"execution_count": 94
},
{
"cell_type": "code",
"source": [
"for is_a in t.parents:\n",
" print(is_a, labels[is_a])"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025331Z",
"start_time": "2024-08-24T00:16:04.458964Z"
}
},
"id": "79d094c451d6a3d9",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CHEBI:33709 amino acid\n",
"CHEBI:78608 alpha-amino acid zwitterion\n",
"CHEBI:26834 sulfur-containing amino acid\n",
"CHEBI:62031 polar amino acid zwitterion\n"
]
}
],
"execution_count": 95
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025411Z",
"start_time": "2024-08-24T00:16:04.815776Z"
}
},
"cell_type": "code",
"source": "print(make_term(ALPHA_AMINO_ACID_ZWITTERION).as_obo())",
"id": "2ae23324e2b21a47",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Term]\n",
"id: CHEBI:78608\n",
"name: an alpha-amino acid\n",
"alt_id: CHEBI:33558\n",
"alt_id: CHEBI:33704\n",
"alt_id: CHEBI:33719\n",
"is_a: CHEBI:33709\n",
"xref: MetaCyc:Alpha-Amino-Acids\n",
"xref: KEGG:C05167\n",
"xref: KEGG:C00045\n",
"comment: Parent CHEBI:33709 was rewired from CHEBI:78608 to CHEBI:35238\n",
"property_value: chemrof:has_physiologically_stable_form CHEBI:78608\n",
"\n"
]
}
],
"execution_count": 96
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025496Z",
"start_time": "2024-08-24T00:16:05.173476Z"
}
},
"cell_type": "code",
"source": [
"t = make_term(\"CHEBI:25944\")\n",
"t.comments\n",
"print(t.as_obo())\n"
],
"id": "7cf212252b470eac",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Term]\n",
"id: CHEBI:25944\n",
"name: pesticide\n",
"is_a: CHEBI:33232\n",
"xref: Wikipedia:Pesticide\n",
"\n"
]
}
],
"execution_count": 97
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025572Z",
"start_time": "2024-08-24T00:16:05.531782Z"
}
},
"cell_type": "code",
"source": [
"GLU_1M = \"CHEBI:14321\"\n",
"assert preserved_rels_by_subject[GLU_1M]\n",
"print(make_term(GLU_1M).as_obo())"
],
"id": "4fde5945f3dbd863",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Term]\n",
"id: CHEBI:14321\n",
"name: glutamate\n",
"alt_id: CHEBI:18237\n",
"alt_id: CHEBI:29987\n",
"is_a: CHEBI:78608\n",
"is_a: CHEBI:62031\n",
"xref: Gmelin:327908\n",
"xref: Wikipedia:Glutamic_acid\n",
"xref: Reaxys:1723799\n",
"xref: KNApSAcK:C00019577\n",
"xref: KNApSAcK:C00001358\n",
"xref: KEGG:D04341\n",
"xref: KEGG:C00302\n",
"xref: Gmelin:101971\n",
"xref: CAS:617-65-2\n",
"xref: Beilstein:1723799\n",
"xref: Reaxys:4134100\n",
"xref: Gmelin:327903\n",
"xref: Beilstein:4134100\n",
"relationship: RO:0000087 CHEBI:78675\n",
"relationship: BFO:0000051 CHEBI:50329\n",
"comment: Parent CHEBI:78608 was rewired from CHEBI:14321 to CHEBI:33558; Parent CHEBI:62031 was rewired from CHEBI:18237 to CHEBI:26167\n",
"property_value: chemrof:inchi_string \"InChI=1S/C5H9NO4/c6-3(5(9)10)1-2-4(7)8/h3H,1-2,6H2,(H,7,8)(H,9,10)/p-1\" xsd:string\n",
"property_value: chemrof:has_physiologically_stable_form CHEBI:14321\n",
"\n"
]
}
],
"execution_count": 98
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025649Z",
"start_time": "2024-08-24T00:16:05.892222Z"
}
},
"cell_type": "code",
"source": "bio_syn_map[ALPHA_AMINO_ACID_ZWITTERION]",
"id": "bad0eb941e50a582",
"outputs": [
{
"data": {
"text/plain": [
"'an alpha-amino acid'"
]
},
"execution_count": 99,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 99
},
{
"cell_type": "code",
"source": [
"ont = Ontology(terms=[t])\n",
"print(ont.as_obo())"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025732Z",
"start_time": "2024-08-24T00:16:06.251105Z"
}
},
"id": "f8fda8f3ae80f063",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ontology: chebi-slim\n",
"idspace: chemrof https://w3id.org/chemrof/\n",
"\n",
"[Term]\n",
"id: CHEBI:25944\n",
"name: pesticide\n",
"is_a: CHEBI:33232\n",
"xref: Wikipedia:Pesticide\n",
"\n"
]
}
],
"execution_count": 100
},
{
"cell_type": "code",
"source": [
"with open(\"tmp/t.obo\", \"w\") as file:\n",
" file.write(ont.as_obo())"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025810Z",
"start_time": "2024-08-24T00:16:06.606612Z"
}
},
"id": "b0e8b052fec4776f",
"outputs": [],
"execution_count": 101
},
{
"cell_type": "code",
"source": [
"def roots(terms: List[Term]):\n",
" return [t.id for t in terms if not t.parents]"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025886Z",
"start_time": "2024-08-24T00:16:06.963704Z"
}
},
"id": "2aecf0e555b32a2a",
"outputs": [],
"execution_count": 102
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.025956Z",
"start_time": "2024-08-24T00:16:07.313073Z"
}
},
"cell_type": "code",
"source": "",
"id": "61d45587ba2b1b57",
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": [
"\n",
"def make_terms_for_ids(ids: List[str]) -> List[Term]:\n",
" \"\"\"\n",
" Make terms for a list of IDs\n",
" \n",
" :param ids: \n",
" :return: \n",
" \"\"\"\n",
" terms = []\n",
" n = 0\n",
" for id in ids:\n",
" n += 1\n",
" t = make_term(id)\n",
" if t:\n",
" terms.append(t)\n",
" if n % 10000 == 0:\n",
" print(f\"Processed {n} IDs, made {len(terms)} terms\")\n",
" return terms\n",
" "
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026032Z",
"start_time": "2024-08-24T00:16:07.668198Z"
}
},
"id": "6f93d120489cc7ac",
"outputs": [],
"execution_count": 103
},
{
"cell_type": "code",
"source": [],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026104Z",
"start_time": "2024-08-24T00:16:08.024489Z"
}
},
"id": "fb1e972e983ab1ba",
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": [
"def write_terms(terms: List[Term], path: str):\n",
" \"\"\"\n",
" Write terms to a file\n",
" \n",
" :param terms: \n",
" :param path: \n",
" :return: \n",
" \"\"\"\n",
" ont = Ontology(terms=terms)\n",
" with open(path, \"w\") as file:\n",
" file.write(ont.as_obo())\n",
" \n",
" "
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026170Z",
"start_time": "2024-08-24T00:16:08.381337Z"
}
},
"id": "373457deffad71d3",
"outputs": [],
"execution_count": 104
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026240Z",
"start_time": "2024-08-24T00:16:08.735221Z"
}
},
"cell_type": "code",
"source": [
"def generate_write_all(ids: List[str], path: str) -> List[Term]:\n",
" \"\"\"\n",
" Run whole pipeline\n",
" \n",
" :param ids: \n",
" :param path: \n",
" :return: \n",
" \"\"\"\n",
" terms = make_terms_for_ids(ids)\n",
" write_terms(terms, path)\n",
" return terms"
],
"id": "4c511c4c5ff0977f",
"outputs": [],
"execution_count": 105
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026311Z",
"start_time": "2024-08-24T00:16:09.088855Z"
}
},
"cell_type": "code",
"source": [
"amino_acid_ids = list(chebi.descendants(AMINO_ACID))\n",
"assert L_CYSTEINE_ZWITTERION in amino_acid_ids\n",
"assert len(amino_acid_ids) > 100"
],
"id": "9a96b67935019540",
"outputs": [],
"execution_count": 106
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026381Z",
"start_time": "2024-08-24T00:16:12.508101Z"
}
},
"cell_type": "code",
"source": "terms = generate_write_all(amino_acid_ids, \"tmp/amino_acids.obo\")",
"id": "816113d92ec207d1",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Processed 10000 IDs, made 7879 terms\n"
]
}
],
"execution_count": 107
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026452Z",
"start_time": "2024-08-24T00:16:17.924748Z"
}
},
"cell_type": "code",
"source": [
"[t] = [t for t in terms if t.id == L_CYSTEINE_ZWITTERION]\n",
"print(t.as_obo())"
],
"id": "c0b4338f1146a984",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Term]\n",
"id: CHEBI:35235\n",
"name: L-cysteine\n",
"alt_id: CHEBI:32442\n",
"alt_id: CHEBI:17561\n",
"alt_id: CHEBI:32445\n",
"alt_id: CHEBI:32443\n",
"is_a: CHEBI:35237\n",
"is_a: CHEBI:59869\n",
"is_a: CHEBI:26650\n",
"is_a: CHEBI:83813\n",
"xref: Gmelin:49993\n",
"xref: Reaxys:4128886\n",
"xref: Gmelin:325857\n",
"xref: Beilstein:4128886\n",
"xref: YMDB:YMDB00046\n",
"xref: Wikipedia:Cysteine\n",
"xref: Reaxys:1721408\n",
"xref: PDBeChem:CYS\n",
"xref: MetaCyc:CYS\n",
"xref: KNApSAcK:C00001351\n",
"xref: KEGG:D00026\n",
"xref: KEGG:C00097\n",
"xref: HMDB:HMDB0000574\n",
"xref: Gmelin:49991\n",
"xref: ECMDB:ECMDB00574\n",
"xref: Drug_Central:769\n",
"xref: DrugBank:DB00151\n",
"xref: CAS:52-90-4\n",
"xref: Beilstein:1721408\n",
"xref: Gmelin:325860\n",
"xref: Reaxys:5921923\n",
"xref: Gmelin:325856\n",
"xref: Beilstein:5921923\n",
"relationship: RO:0018039 CHEBI:35236\n",
"relationship: RO:0000087 CHEBI:78675\n",
"relationship: RO:0000087 CHEBI:64577\n",
"relationship: RO:0000087 CHEBI:77703\n",
"relationship: RO:0000087 CHEBI:77746\n",
"comment: Parent CHEBI:59869 was rewired from CHEBI:32442 to CHEBI:59814; Parent CHEBI:26650 was rewired from CHEBI:17561 to CHEBI:26650; Parent CHEBI:83813 was rewired from CHEBI:17561 to CHEBI:83813\n",
"property_value: chemrof:inchi_string \"InChI=1S/C3H7NO2S/c4-2(1-7)3(5)6/h2,7H,1,4H2,(H,5,6)/t2-/m0/s1\" xsd:string\n",
"property_value: chemrof:has_physiologically_stable_form CHEBI:35235\n",
"\n"
]
}
],
"execution_count": 108
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026529Z",
"start_time": "2024-08-24T00:16:18.302490Z"
}
},
"cell_type": "code",
"source": [
"from oaklib.datamodels.vocabulary import OWL_CLASS\n",
"\n",
"# all_ids = list(chebi.descendants(ROOT))\n",
"all_ids = list(chebi.entities(filter_obsoletes=True, owl_type=OWL_CLASS))\n",
"terms = generate_write_all(all_ids, \"tmp/all.obo\")\n"
],
"id": "7fec1427e96baea2",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Processed 10000 IDs, made 9955 terms\n",
"Processed 20000 IDs, made 19917 terms\n",
"Processed 30000 IDs, made 29878 terms\n",
"Processed 40000 IDs, made 38207 terms\n",
"Processed 50000 IDs, made 47129 terms\n",
"Processed 60000 IDs, made 56582 terms\n",
"Processed 70000 IDs, made 65929 terms\n",
"Processed 80000 IDs, made 75147 terms\n",
"Processed 90000 IDs, made 84597 terms\n",
"Processed 100000 IDs, made 93930 terms\n",
"Processed 110000 IDs, made 103890 terms\n",
"Processed 120000 IDs, made 113825 terms\n",
"Processed 130000 IDs, made 123784 terms\n",
"Processed 140000 IDs, made 132470 terms\n",
"Processed 150000 IDs, made 141078 terms\n",
"Processed 160000 IDs, made 149833 terms\n",
"Processed 170000 IDs, made 158566 terms\n",
"Processed 180000 IDs, made 166455 terms\n",
"Processed 190000 IDs, made 174804 terms\n",
"Processed 200000 IDs, made 184378 terms\n"
]
}
],
"execution_count": 109
},
{
"cell_type": "code",
"source": [
"len(terms)"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026608Z",
"start_time": "2024-08-24T00:17:38.385274Z"
}
},
"id": "153be3a8b30c2713",
"outputs": [
{
"data": {
"text/plain": [
"185206"
]
},
"execution_count": 110,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 110
},
{
"cell_type": "code",
"source": [
"# many roots expected when we make a subset\n",
"len(roots(terms))"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026690Z",
"start_time": "2024-08-24T00:17:38.762055Z"
}
},
"id": "ac31bc98e2ef7ef7",
"outputs": [
{
"data": {
"text/plain": [
"16"
]
},
"execution_count": 111,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 111
},
{
"cell_type": "code",
"source": "#write_terms(terms, f\"tmp/{ROOT.replace(':', '_')}.obo\")",
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026776Z",
"start_time": "2024-08-24T00:17:39.162207Z"
}
},
"id": "858eda4cbfd8fb5d",
"outputs": [],
"execution_count": 112
},
{
"cell_type": "code",
"source": [
"fertirelin = \"CHEBI:177856\""
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026847Z",
"start_time": "2024-08-24T00:17:39.529104Z"
}
},
"id": "dbc4c586e92c3328",
"outputs": [],
"execution_count": 113
},
{
"cell_type": "code",
"source": [
"t = make_term(fertirelin)\n",
"print(t.as_obo())"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.026935Z",
"start_time": "2024-08-24T00:17:39.893737Z"
}
},
"id": "90336d5cc96554c7",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Term]\n",
"id: CHEBI:177856\n",
"name: fertirelin\n",
"is_a: CHEBI:25676\n",
"xref: KEGG:D07957\n",
"xref: Chemspider:163670\n",
"xref: CAS:38234-21-8\n",
"property_value: chemrof:inchi_string \"InChI=1S/C55H76N16O12/c1-4-59-53(82)44-12-8-20-71(44)54(83)38(11-7-19-60-55(56)57)66-49(78)39(21-30(2)3)65-46(75)27-62-47(76)40(22-31-13-15-34(73)16-14-31)67-52(81)43(28-72)70-50(79)41(23-32-25-61-36-10-6-5-9-35(32)36)68-51(80)42(24-33-26-58-29-63-33)69-48(77)37-17-18-45(74)64-37/h5-6,9-10,13-16,25-26,29-30,37-44,61,72-73H,4,7-8,11-12,17-24,27-28H2,1-3H3,(H,58,63)(H,59,82)(H,62,76)(H,64,74)(H,65,75)(H,66,78)(H,67,81)(H,68,80)(H,69,77)(H,70,79)(H4,56,57,60)/t37-,38-,39-,40-,41-,42-,43-,44-/m0/s1\" xsd:string\n",
"\n"
]
}
],
"execution_count": 114
},
{
"cell_type": "code",
"source": [
"chebi.label(is_a_map[fertirelin][0])"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.027020Z",
"start_time": "2024-08-24T00:17:40.255847Z"
}
},
"id": "e1185be596f3b559",
"outputs": [
{
"data": {
"text/plain": [
"'oligopeptide'"
]
},
"execution_count": 115,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 115
},
{
"cell_type": "code",
"source": [],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-11-16T01:31:29.027117Z",
"start_time": "2024-08-24T00:17:40.640098Z"
}
},
"id": "3e17588e0084f22d",
"outputs": [],
"execution_count": null
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}