diff --git a/data/compounds/retatrutide.json b/data/compounds/retatrutide.json index 1a2221a..4459a60 100644 --- a/data/compounds/retatrutide.json +++ b/data/compounds/retatrutide.json @@ -489,7 +489,7 @@ } }, { - "value": "Randomized controlled trial, humans, n=534, 2025: Percent reduction from baseline in total fat mass was 4·9% (SE 1·4%) with retatrutide 0·5 mg, 15·2% (3·2%) with retatrutide 4 mg (pooled), 26·1% (2·5%) with retatrutide 8 mg (pooled), 23·2% (3·0%) with retatrutide 12 mg, 2·6% (1·6%) with dulaglutide, and 4·5% (1·2%) with placebo.", + "value": "Randomized controlled trial, humans, n=281, 2025: Percent reduction from baseline in total fat mass was 4·9% (SE 1·4%) with retatrutide 0·5 mg, 15·2% (3·2%) with retatrutide 4 mg (pooled), 26·1% (2·5%) with retatrutide 8 mg (pooled), 23·2% (3·0%) with retatrutide 12 mg, 2·6% (1·6%) with dulaglutide, and 4·5% (1·2%) with placebo.", "evidence_label": "human-clinical-trial", "source_ids": [ "pmid-40609566" @@ -500,7 +500,7 @@ "study": "Coskun 2025", "year": "2025", "design": "Randomized controlled trial", - "n": 534, + "n": 281, "species": "Humans", "dose": "0·5 mg; 4 mg", "route": "subcutaneous", @@ -649,7 +649,7 @@ } }, { - "value": "Human study, humans, n=367, 2026: Background and hypothesis Retatrutide is an agonist of the glucose-dependent insulinotropic polypeptide, glucagon-like peptide-1 and glucagon receptors that reduced weight and hemoglobin A1c (HbA1c) in individuals with obesity and type 2 diabetes (T2D).", + "value": "Human study, humans, n=146, 2026: Background and hypothesis Retatrutide is an agonist of the glucose-dependent insulinotropic polypeptide, glucagon-like peptide-1 and glucagon receptors that reduced weight and hemoglobin A1c (HbA1c) in individuals with obesity and type 2 diabetes (T2D).", "evidence_label": "observational-human", "source_ids": [ "pmid-41160422" @@ -660,7 +660,7 @@ "study": "Heerspink 2026", "year": "2026", "design": "Human study", - "n": 367, + "n": 146, "species": "Humans", "dose": "12 mg", "route": "not stated in abstract", @@ -1204,6 +1204,10 @@ { "date": "2026-09-29", "change": "Reviewed by Adam Mirando, PharmD, on 2026-09-29." + }, + { + "date": "2026-09-29", + "change": "Corrected two more sample sizes that had stored the screening count: the body-composition substudy from 534 to 281 enrolled, and the cardiovascular trial from 367 screened to 146 randomised." } ], "guide": [ diff --git a/scripts/draft_claims.py b/scripts/draft_claims.py index 3a158bd..4b488eb 100644 --- a/scripts/draft_claims.py +++ b/scripts/draft_claims.py @@ -90,10 +90,14 @@ r"\b" + NUM + r"\s+(?:\([^)]{0,40}\)\s*)?(?:\w+\s+){0,3}?" r"(?:were|was)\s+(?:randomly\s+assigned|randomi[sz]ed|allocated|enrolled)\b", re.IGNORECASE) SCREENED_AFTER = re.compile(r"^[^.]{0,40}?\b(?:were|was)\s+screened\b", re.IGNORECASE) +# "randomized 1:1", "randomised 1:1:1:1:1" is an allocation ratio, not a count. +# The bare verb pattern reads it as a sample of 1, which is why it sits below +# the noun pattern and refuses a number followed by a colon. +RATIO_AFTER = re.compile(r"^\s*:") N_RE = [ ASSIGNED, - re.compile(r"\b(?:enrolled|randomi[sz]ed|included|analy[sz]ed|recruited)\s+" + NUM + r"\b", re.IGNORECASE), re.compile(r"\b" + NUM + r"\s+(?:patients|participants|subjects|volunteers|adults|individuals|people|men|women|children|infants|rats|mice|rabbits|dogs|pigs|animals)\b", re.IGNORECASE), + re.compile(r"\b(?:enrolled|randomi[sz]ed|included|analy[sz]ed|recruited)\s+" + NUM + r"\b", re.IGNORECASE), re.compile(r"\b[nN]\s?=\s?" + NUM + r"\b"), ] DESIGN_FROM_TITLE = [ @@ -227,9 +231,15 @@ def sample_size(abstract: str) -> int | None: n = int(re.sub(r"[ ,\u00a0\u202f]", "", m.group(1))) if not 1 <= n <= 2_000_000: continue + # Both look at what follows the number itself, not the whole match: + # ASSIGNED ends after "randomly assigned", and the colon in + # "assigned: 134 to retatrutide 4 mg" is not an allocation ratio. + after = abstract[m.end(1):] # "930 participants were screened and 537 were randomly assigned": # the screening count is not the trial's size. - if rx is not ASSIGNED and SCREENED_AFTER.match(abstract[m.end():]): + if rx is not ASSIGNED and SCREENED_AFTER.match(after): + continue + if RATIO_AFTER.match(after): continue return n return None diff --git a/scripts/test_drafter.py b/scripts/test_drafter.py index a8bedda..b7e446f 100644 --- a/scripts/test_drafter.py +++ b/scripts/test_drafter.py @@ -54,6 +54,29 @@ def test_screened_then_randomised_short_form(self): def test_plain_enrolment_still_wins(self): self.assertEqual(dc.sample_size("We enrolled 338 adults, 51.8% of whom were men."), 338) + def test_allocation_ratio_is_not_a_count(self): + # "randomized 1:1" put n=1 on kisspeptin, survodutide, semax, tesamorelin + # and thymosin-alpha-1 rows when the noun pattern lost precedence. + self.assertEqual(dc.sample_size( + "Of these, 194 patients have been randomized 1:1 to either Group A or Group B."), 194) + self.assertEqual(dc.sample_size( + "Totally 387 people (aged 18-75 years) were randomized 1:1:1:1:1 to once-weekly " + "subcutaneous survodutide or placebo."), 387) + + def test_colon_after_assignment_is_not_a_ratio(self): + # The colon in "assigned: 134 to retatrutide 4 mg" introduces the arms, + # so it must not disqualify the number that precedes the verb. + self.assertEqual(dc.sample_size( + "930 participants were screened and 537 (296 [55%] female) were randomly assigned: " + "134 to retatrutide 4 mg, 133 to retatrutide 9 mg"), 537) + + def test_screened_participants_phrasing(self): + self.assertEqual(dc.sample_size( + "Of 367 participants screened, 146 were randomized to study interventions."), 146) + self.assertEqual(dc.sample_size( + "534 participants were screened for inclusion into the main study; 253 were excluded " + "and 281 participants were enrolled and randomly assigned to the main study."), 281) + def test_enrolment_beats_subgroup_n(self): text = "The renal impairment study included 33 participants (normal function, n = 14; mild impairment, n = 7)." self.assertEqual(dc.sample_size(text), 33)