Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose decision support values table in ehrQL #2348

Merged
merged 4 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/how-to/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -923,3 +923,27 @@ dataset.prescription_date = case(
)
dataset.define_population(patients.exists_for_patient())
```

## Decision support values

Examples for the [TPP decision support values table](../reference/schemas/tpp.md#decision_support_values).

### Finding the most recent decision support value

#### Finding each patient's EFI (electronic frailty index)

```ehrql
from ehrql import create_dataset
from ehrql.tables.tpp import decision_support_values

dataset = create_dataset()
latest_efi_record = (
decision_support_values
.electronic_frailty_index()
.sort_by(decision_support_values.calculation_date)
.last_for_patient()
)
dataset.latest_efi = latest_efi_record.numeric_value
dataset.latest_efi_date = latest_efi_record.calculation_date
dataset.define_population(decision_support_values.exists_for_patient())
```
100 changes: 100 additions & 0 deletions docs/includes/generated_docs/schemas/tpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ from ehrql.tables.tpp import (
clinical_events,
clinical_events_ranges,
covid_therapeutics,
decision_support_values,
ec,
ec_cost,
emergency_care_attendances,
Expand Down Expand Up @@ -1185,6 +1186,105 @@ date or a past date at the time of form submission.
</div>


<p class="dimension-indicator"><code>many rows per patient</code></p>
## decision_support_values

Returns values computed by decision support algorithms, for example the
[Electronic Frailty Index (EFI)][efi_ref]

[efi_ref]: https://www.england.nhs.uk/ourwork/clinical-policy/older-people/frailty/efi/
<div markdown="block" class="definition-list-wrapper">
<div class="title">Columns</div>
<dl markdown="block">
<div markdown="block">
<dt id="decision_support_values.algorithm_type">
<strong>algorithm_type</strong>
<a class="headerlink" href="#decision_support_values.algorithm_type" title="Permanent link">🔗</a>
<code>integer</code>
</dt>
<dd markdown="block">
A unique id for the decision support algorithm. Currently, the only option is '1' for the electronic frailty index.

</dd>
</div>

<div markdown="block">
<dt id="decision_support_values.calculation_date">
<strong>calculation_date</strong>
<a class="headerlink" href="#decision_support_values.calculation_date" title="Permanent link">🔗</a>
<code>date</code>
</dt>
<dd markdown="block">
Date of calculation for the decision support algorithm.

</dd>
</div>

<div markdown="block">
<dt id="decision_support_values.numeric_value">
<strong>numeric_value</strong>
<a class="headerlink" href="#decision_support_values.numeric_value" title="Permanent link">🔗</a>
<code>float</code>
</dt>
<dd markdown="block">
The value computed by the decision support algorithm

</dd>
</div>

<div markdown="block">
<dt id="decision_support_values.algorithm_description">
<strong>algorithm_description</strong>
<a class="headerlink" href="#decision_support_values.algorithm_description" title="Permanent link">🔗</a>
<code>string</code>
</dt>
<dd markdown="block">
The description of the decision support algorithm.

</dd>
</div>

<div markdown="block">
<dt id="decision_support_values.algorithm_version">
<strong>algorithm_version</strong>
<a class="headerlink" href="#decision_support_values.algorithm_version" title="Permanent link">🔗</a>
<code>string</code>
</dt>
<dd markdown="block">
The version of the decision support algorithm.

</dd>
</div>

</dl>
</div>
<div markdown="block" class="definition-list-wrapper">
<div class="title">Methods</div>
<dl markdown="block">
<div markdown="block">
<dt id="decision_support_values.electronic_frailty_index">
<strong>electronic_frailty_index(</strong><strong>)</strong>
<a class="headerlink" href="#decision_support_values.electronic_frailty_index" title="Permanent link">🔗</a>
<code></code>
</dt>
<dd markdown="block">
Returns every calculated electronic frailty index v1 (EFI) for each patient.
<details markdown="block">
<summary>View method definition</summary>
```py
return decision_support_values.where(
decision_support_values.algorithm_description == "UK Electronic Frailty Index (eFI)"
).where(decision_support_values.algorithm_version == "1.0")

```
</details>
</dd>
</div>

</dl>
</div>


<p class="dimension-indicator"><code>many rows per patient</code></p>
## ec

Expand Down
15 changes: 15 additions & 0 deletions ehrql/backends/tpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,21 @@ def _risk_cohort_format(risk_cohort):
"""
)

decision_support_values = QueryTable(
"""
SELECT
decval.Patient_ID AS patient_id,
decval.AlgorithmType AS algorithm_type,
CAST(NULLIF(decval.CalculationDateTime, '9999-12-31T00:00:00') AS date) AS calculation_date,
decval.NumericValue AS numeric_value,
decvalref.AlgorithmDescription AS algorithm_description,
decvalref.AlgorithmVersion AS algorithm_version
FROM DecisionSupportValue AS decval
LEFT JOIN DecisionSupportValueReference AS decvalref
ON decval.AlgorithmType = decvalref.AlgorithmType
"""
)

@QueryTable.from_function
def ec(self):
return self._union_over_hes_archive(
Expand Down
39 changes: 39 additions & 0 deletions ehrql/tables/tpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"clinical_events",
"clinical_events_ranges",
"covid_therapeutics",
"decision_support_values",
"ec",
"ec_cost",
"emergency_care_attendances",
Expand Down Expand Up @@ -805,6 +806,44 @@ class covid_therapeutics(EventFrame):
)


@table
class decision_support_values(EventFrame):
"""
Returns values computed by decision support algorithms, for example the
[Electronic Frailty Index (EFI)][efi_ref]

[efi_ref]: https://www.england.nhs.uk/ourwork/clinical-policy/older-people/frailty/efi/

"""

algorithm_type = Series(
int,
description="A unique id for the decision support algorithm. Currently, the only option is '1' for the electronic frailty index.",
)
calculation_date = Series(
datetime.date,
description="Date of calculation for the decision support algorithm.",
)
numeric_value = Series(
float,
description="The value computed by the decision support algorithm",
)
algorithm_description = Series(
str, description="The description of the decision support algorithm."
)
algorithm_version = Series(
str, description="The version of the decision support algorithm."
)

def electronic_frailty_index(self):
"""
Returns every calculated electronic frailty index v1 (EFI) for each patient.
"""
return self.where(
self.algorithm_description == "UK Electronic Frailty Index (eFI)"
).where(self.algorithm_version == "1.0")


@table
class ec(EventFrame):
"""
Expand Down
73 changes: 73 additions & 0 deletions tests/integration/backends/test_tpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
CodedEvent_SNOMED,
CodedEventRange,
CustomMedicationDictionary,
DecisionSupportValue,
DecisionSupportValueReference,
EC_Cost,
EC_Cost_ARCHIVED,
EC_Diagnosis,
Expand Down Expand Up @@ -969,6 +971,77 @@ def test_covid_therapeutics_raw(select_all_tpp):
]


@register_test_for(tpp.decision_support_values)
def test_decision_support_values(select_all_tpp):
results = select_all_tpp(
Patient(Patient_ID=1),
DecisionSupportValueReference(
AlgorithmDescription="UK Electronic Frailty Index (eFI)",
AlgorithmSourceLink="link",
AlgorithmType=1,
AlgorithmVersion="1.0",
),
DecisionSupportValue(
Patient_ID=1,
AlgorithmType=1,
CalculationDateTime="2010-01-01T10:00:00",
NumericValue=37.5,
),
DecisionSupportValue(
Patient_ID=1,
AlgorithmType=1,
CalculationDateTime="2011-01-01T10:00:00",
NumericValue=40.5,
),
DecisionSupportValue(
Patient_ID=1,
AlgorithmType=1,
CalculationDateTime="2012-01-01T10:00:00",
NumericValue=45.0,
),
DecisionSupportValue(
Patient_ID=1,
AlgorithmType=1,
CalculationDateTime="2013-01-01T10:00:00",
NumericValue=47.0,
),
)
assert results == [
{
"patient_id": 1,
"algorithm_type": 1,
"calculation_date": date(2010, 1, 1),
"numeric_value": 37.5,
"algorithm_description": "UK Electronic Frailty Index (eFI)",
"algorithm_version": "1.0",
},
{
"patient_id": 1,
"algorithm_type": 1,
"calculation_date": date(2011, 1, 1),
"numeric_value": 40.5,
"algorithm_description": "UK Electronic Frailty Index (eFI)",
"algorithm_version": "1.0",
},
{
"patient_id": 1,
"algorithm_type": 1,
"calculation_date": date(2012, 1, 1),
"numeric_value": 45.0,
"algorithm_description": "UK Electronic Frailty Index (eFI)",
"algorithm_version": "1.0",
},
{
"patient_id": 1,
"algorithm_type": 1,
"calculation_date": date(2013, 1, 1),
"numeric_value": 47.0,
"algorithm_description": "UK Electronic Frailty Index (eFI)",
"algorithm_version": "1.0",
},
]


@register_test_for(tpp.ec)
def test_ec(select_all_tpp):
results = select_all_tpp(
Expand Down
50 changes: 50 additions & 0 deletions tests/integration/tables/test_tpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,53 @@ def test_addresses_imd_quintile(in_memory_engine):
},
{"patient_id": 6, "imd_quintile": "unknown", "imd_decile": "unknown"},
]


def test_decision_support_values_electronic_frailty_index(
in_memory_engine,
):
in_memory_engine.populate(
{
tpp.decision_support_values: [
dict(
patient_id=1,
algorithm_type=1,
calculation_date=date(2012, 1, 1),
numeric_value=25.0,
algorithm_description="UK Electronic Frailty Index (eFI)",
algorithm_version="1.0",
),
dict(
patient_id=1,
algorithm_type=1,
calculation_date=date(2010, 1, 1),
numeric_value=30.0,
algorithm_description="UK Electronic Frailty Index (eFI)",
algorithm_version="1.5",
),
dict(
patient_id=1,
algorithm_type=2,
calculation_date=date(2010, 1, 1),
numeric_value=25.0,
algorithm_description="A different index",
algorithm_version="1.0",
),
]
},
)

dataset = Dataset()
dataset.define_population(tpp.decision_support_values.exists_for_patient())
first_efi = (
tpp.decision_support_values.electronic_frailty_index()
.sort_by(tpp.decision_support_values.calculation_date)
.first_for_patient()
)
dataset.efi = first_efi.numeric_value
dataset.efi_year = first_efi.calculation_date.year
results = in_memory_engine.extract(dataset)

assert results == [
{"patient_id": 1, "efi": 25.0, "efi_year": 2012},
]
Loading