Support LogP (experimental only) #3

Open
opened 2026-04-12 16:28:13 -04:00 by aalexmmaldonado · 0 comments
aalexmmaldonado commented 2026-04-12 16:28:13 -04:00 (Migrated from github.com)

Right now, pcdigitizer parses exactly one annotation heading: Dissociation Constants. The architecture is ready for more. You add a member to the Annotation enum, write a processor class that satisfies AnnotationProcessor, and register it. That's the pattern.

LogP, the octanol–water partition coefficient, is a natural second heading to support. It's one of the most used molecular descriptors in drug design and environmental chemistry, and PubChem stores it under the "LogP" heading via PUG-View.

But there's a catch. PubChem has experimental LogP values for roughly 26,000 compounds and computed LogP values (XLogP3, ALOGPS, CLogP, and others) for over 90 million compounds. These cannot be mixed. Computed values are predictions from earlier models; training on them is circular. This processor must output only experimentally measured values.

Each AnnotationEntry includes a SourceName field that indicates who deposited the data. That's the most reliable filter. Experimental sources (e.g., HSDB, DrugBank, ILO-ICSC, HMDB, ChEBI) deposit measured partition coefficients, usually with a literature citation. HSDB entries look like "log Kow = 1.19" and cite Hansch/Leo. DrugBank and ILO-ICSC deposit bare floats like "1.19". Computed sources deposit predicted values and often name the method: "XLogP3 = 1.2", "ALOGPS logP = 2.34". Filter on SourceName, not on the free-text string.

Scope

Add LOGP = "LogP" to the Annotation enum. Create pcdigitizer/data/logp.py with a LogPData class whose from_page returns a DataFrame with these columns: cid, sid, pclid, source_name (String), logp_value (Float64), temperature_C (Float64, nullable), reference (String, nullable), comment (String, nullable).

Maintain an explicit set of known experimental source names and skip everything else with a logger.debug message. Err on the side of exclusion; sources can be added later after manual review. As a secondary guard, skip entries whose string contains "XLogP", "ALOGPS", "Estimated", "Predicted", or "calc" — some experimental sources occasionally include computed values too.

Common experimental formats to parse:

"log Kow = 1.19"            (HSDB — note the Kow label)
"1.19"                       (bare float from DrugBank, HMDB, ILO-ICSC)
"log P = 2.45"               (generic labeled form)
"log Kow = -0.73 at 25 °C"  (with temperature)

Register LogPData in registry.py. Export it from pcdigitizer/data/__init__.py. Write tests in tests/test_logp.py covering bare numerics, log Kow = format, values with temperature, computed-source filtering, junk strings, and the empty-input case.

The reference column matters for downstream confidence scoring. An experimental value citing Hansch & Leo is more trustworthy than a bare float with no citation.

Definition of done

GetAnnotationPage().do(item=1, annotation=Annotation.LOGP) returns a DataFrame containing only experimental values. The PR description includes a table of discovered SourceName values classified as experimental, computed, or ambiguous, with example strings from each. Tests pass. Docstrings follow the style in dissociation_constant.py.

Getting started

Read dissociation_constant.py end to end; it's the template for every new processor. Then look at how Annotation and ANNOTATION_REGISTRY connect in annotations.py and registry.py.

Your first task is not to write a parser. Write a scratch script that fetches two or three pages of LogP data, groups entries by SourceName, and prints each source's name, entry count, and three example StringWithMarkup values. That table tells you which sources are experimental and what formats to expect.

Right now, pcdigitizer parses exactly one annotation heading: Dissociation Constants. The architecture is ready for more. You add a member to the `Annotation` enum, write a processor class that satisfies `AnnotationProcessor`, and register it. That's the pattern. LogP, the octanol–water partition coefficient, is a natural second heading to support. It's one of the most used molecular descriptors in drug design and environmental chemistry, and PubChem stores it under the `"LogP"` heading via PUG-View. But there's a catch. PubChem has experimental LogP values for roughly 26,000 compounds and computed LogP values (XLogP3, ALOGPS, CLogP, and others) for over 90 million compounds. These cannot be mixed. Computed values are predictions from earlier models; training on them is circular. This processor must output only experimentally measured values. Each `AnnotationEntry` includes a `SourceName` field that indicates who deposited the data. That's the most reliable filter. Experimental sources (e.g., HSDB, DrugBank, ILO-ICSC, HMDB, ChEBI) deposit measured partition coefficients, usually with a literature citation. HSDB entries look like `"log Kow = 1.19"` and cite Hansch/Leo. DrugBank and ILO-ICSC deposit bare floats like `"1.19"`. Computed sources deposit predicted values and often name the method: `"XLogP3 = 1.2"`, `"ALOGPS logP = 2.34"`. Filter on `SourceName`, not on the free-text string. ### Scope Add `LOGP = "LogP"` to the `Annotation` enum. Create `pcdigitizer/data/logp.py` with a `LogPData` class whose `from_page` returns a DataFrame with these columns: `cid`, `sid`, `pclid`, `source_name` (String), `logp_value` (Float64), `temperature_C` (Float64, nullable), `reference` (String, nullable), `comment` (String, nullable). Maintain an explicit set of known experimental source names and skip everything else with a `logger.debug` message. Err on the side of exclusion; sources can be added later after manual review. As a secondary guard, skip entries whose string contains `"XLogP"`, `"ALOGPS"`, `"Estimated"`, `"Predicted"`, or `"calc"` — some experimental sources occasionally include computed values too. Common experimental formats to parse: ``` "log Kow = 1.19" (HSDB — note the Kow label) "1.19" (bare float from DrugBank, HMDB, ILO-ICSC) "log P = 2.45" (generic labeled form) "log Kow = -0.73 at 25 °C" (with temperature) ``` Register `LogPData` in `registry.py`. Export it from `pcdigitizer/data/__init__.py`. Write tests in `tests/test_logp.py` covering bare numerics, `log Kow =` format, values with temperature, computed-source filtering, junk strings, and the empty-input case. The `reference` column matters for downstream confidence scoring. An experimental value citing Hansch & Leo is more trustworthy than a bare float with no citation. ### Definition of done `GetAnnotationPage().do(item=1, annotation=Annotation.LOGP)` returns a DataFrame containing only experimental values. The PR description includes a table of discovered `SourceName` values classified as experimental, computed, or ambiguous, with example strings from each. Tests pass. Docstrings follow the style in `dissociation_constant.py`. ### Getting started Read `dissociation_constant.py` end to end; it's the template for every new processor. Then look at how `Annotation` and `ANNOTATION_REGISTRY` connect in `annotations.py` and `registry.py`. Your first task is not to write a parser. Write a scratch script that fetches two or three pages of LogP data, groups entries by `SourceName`, and prints each source's name, entry count, and three example `StringWithMarkup` values. That table tells you which sources are experimental and what formats to expect.
Sign in to join this conversation.