Tumor and Multitumor Data

Conducting dose-response analysis on dichotomous tumor data differs from analyzing standard dichotomous tumor data in the following ways:

  • The Multistage cancer model uses different parameter settings for model fit than the standard Multistage model.

  • A cancer slope factor is calculated.

  • In some cases, there may be a need to combine multiple tumor datasets and then calculate a single cancer slope factor.

To that end, this guide covers some different approaches that you can use in pybmds for handling tumor data.

Quickstart

To run a single dataset:

import pybmds
from pybmds.models.dichotomous import MultistageCancer

dataset = pybmds.DichotomousDataset(
    doses=[0, 25, 75, 125, 200],
    ns=[20, 20, 20, 20, 20],
    incidences=[0, 1, 7, 15, 19],
    name="Tumor dataset A",
    dose_units="mg/kg-d",
)

model = MultistageCancer(dataset, settings={"bmr": 0.1})
model.execute(slope_factor=True)

print(f"BMD = {model.results.bmd:f}")
print(f"BMDL = {model.results.bmdl:f}")
print(f"CSF = {model.results.slope_factor:f}")

model.plot()
BMD = 35.929912
BMDL = 18.233585
CSF = 0.005484
../_images/d90372746b817409e7df78d743292f67935e65ab49c8e53d6a9b954dc00f0418.png

To run multiple datasets and calculate a single combined slope factor:

import pybmds

datasets = [
    pybmds.DichotomousDataset(
        doses=[0, 25, 75, 125, 200],
        ns=[20, 20, 20, 20, 20],
        incidences=[0, 1, 7, 15, 19],
        name="Tumor A",
        dose_units="mg/m³",
    ),
    pybmds.DichotomousDataset(
        doses=[0, 25, 75, 125, 200],
        ns=[20, 20, 20, 20, 20],
        incidences=[0, 0, 1, 7, 11],
        name="Tumor B",
        dose_units="mg/m³",
    ),
]

session = pybmds.Multitumor(datasets, settings={"bmr": 0.2}, name="Example")
session.execute()

session.plot()
../_images/81ddd08e619f3c8d31c9950261b0af61006df04bd9b98aba536a352ca4855718.png

To view individual model results for selected models for each dataset:

# Print overall results
print("Overall")
print(f"BMD = {session.results.bmd:f}")
print(f"BMDL = {session.results.bmdl:f}")
print(f"CSF = {session.results.slope_factor:f}")
print()

# Print individual model results
selected_model_indexes = session.results.selected_model_indexes
for i, dataset_models in enumerate(session.models):
    selected_index = selected_model_indexes[i]
    selected_model = dataset_models[selected_index]
    print(f"{selected_model.dataset.metadata.name}: {selected_model.name()}")
    print(f"BMD = {selected_model.results.bmd:f}")
    print(f"BMDL = {selected_model.results.bmdl:f}")
    print(f"CSF = {selected_model.results.slope_factor:f}")
    print()
Overall
BMD = 18.835597
BMDL = 15.066557
CSF = 0.013274

Tumor A: Multistage 1
BMD = 24.653310
BMDL = 18.882341
CSF = 0.010592

Tumor B: Multistage 1
BMD = 79.818267
BMDL = 55.735346
CSF = 0.003588

Create a tumor dataset

Create a tumor dataset using the same method as a dichotomous dataset.

As with a dichotomous dataset, provide a list of doses, incidences, and the total number of subjects, one item per dose-group.

You can also add optional attributes, such as name, dose_name, dose_units, response_name, response_units, etc.

dataset = pybmds.DichotomousDataset(
    name="Chemical X Tumor A",
    dose_units="ppm",
    doses=[0, 25, 75, 125, 200],
    ns=[20, 20, 20, 20, 20],
    incidences=[0, 1, 7, 15, 19],
)

print(dataset.tbl())
dataset.plot()
╒════════╤═════════════╤═════╕
│   Dose │   Incidence │   N │
╞════════╪═════════════╪═════╡
│      0 │           0 │  20 │
│     25 │           1 │  20 │
│     75 │           7 │  20 │
│    125 │          15 │  20 │
│    200 │          19 │  20 │
╘════════╧═════════════╧═════╛
../_images/bae4b06c55178f5f621373ef8fe6a15f0c0b16398238f57bbc8057a5da70c6e0.png

Single dataset fit

With a single tumor dataset defined above, you can run a single Multistage cancer model:

import pybmds
from pybmds.models.dichotomous import MultistageCancer

model = MultistageCancer(dataset, settings={"bmr": 0.10, "degree": 2})
model.execute(slope_factor=True)
model.plot()
../_images/93e12410a238c5a1de4bda2cb0bc6f70c541776eef4f15bea5dd614b3eff3c6c.png

After executing, results are stored in a results attribute on the model. You can view individual items in the results by accessing:

print(model.name())
print(f"BMD = {session.results.bmd:f}")
print(f"BMDL = {session.results.bmdl:f}")
print(f"CSF = {session.results.slope_factor:f}")
Multistage 2
BMD = 18.835597
BMDL = 15.066557
CSF = 0.013274

Or generate a text report to view a summary:

print(model.text())
      Multistage 2 Model      
══════════════════════════════

Version: pybmds 24.1 (bmdscore 24.1)

Input Summary:
╒══════════════════════════════╤════════════════════════╕
│ BMR                          │ 10% Extra Risk         │
│ Confidence Level (one sided) │ 0.95                   │
│ Modeling approach            │ frequentist_restricted │
│ Degree                       │ 2                      │
╘══════════════════════════════╧════════════════════════╛

Parameter Settings:
╒═════════════╤═══════════╤═══════╤═══════╕
│ Parameter   │   Initial │   Min │   Max │
╞═════════════╪═══════════╪═══════╪═══════╡
│ g           │     -17   │   -18 │    18 │
│ b1          │       0.1 │     0 │ 10000 │
│ b2          │       0.1 │     0 │ 10000 │
╘═════════════╧═══════════╧═══════╧═══════╛

Modeling Summary:
╒════════════════╤══════════════╕
│ BMD            │  35.9299     │
│ BMDL           │  18.2336     │
│ BMDU           │  42.1027     │
│ Slope Factor   │   0.00548438 │
│ AIC            │  68.4561     │
│ Log-Likelihood │ -32.2281     │
│ P-Value        │   0.979879   │
│ Overall d.f.   │   3          │
│ Chi²           │   0.185606   │
╘════════════════╧══════════════╛

Model Parameters:
╒════════════╤═════════════╤════════════╤══════════════╕
│ Variable   │    Estimate │ On Bound   │ Std Error    │
╞════════════╪═════════════╪════════════╪══════════════╡
│ g          │ 1.523e-08   │ yes        │ Not Reported │
│ b1         │ 3.10908e-05 │ no         │ 0.279639     │
│ b2         │ 8.07489e-05 │ no         │ 0.795918     │
╘════════════╧═════════════╧════════════╧══════════════╛
Standard errors estimates are not generated for parameters estimated on corresponding bounds,
although sampling error is present for all parameters, as a rule. Standard error estimates may not
be reliable as a basis for confidence intervals or tests when one or more parameters are on bounds.


Goodness of Fit:
╒════════╤════════╤════════════╤════════════╤════════════╤═══════════════════╕
│   Dose │   Size │   Observed │   Expected │   Est Prob │   Scaled Residual │
╞════════╪════════╪════════════╪════════════╪════════════╪═══════════════════╡
│      0 │     20 │          0 │  3.046e-07 │  1.523e-08 │      -0.000551905 │
│     25 │     20 │          1 │  0.999088  │  0.0499544 │       0.000935604 │
│     75 │     20 │          7 │  7.33062   │  0.366531  │      -0.153425    │
│    125 │     20 │         15 │ 14.3585    │  0.717926  │       0.318744    │
│    200 │     20 │         19 │ 19.2137    │  0.960686  │      -0.245902    │
╘════════╧════════╧════════════╧════════════╧════════════╧═══════════════════╛

Analysis of Deviance:
╒═══════════════╤══════════════════╤════════════╤════════════╤═════════════╤═════════════╕
│ Model         │   Log-Likelihood │   # Params │ Deviance   │ Test d.f.   │ P-Value     │
╞═══════════════╪══════════════════╪════════════╪════════════╪═════════════╪═════════════╡
│ Full model    │         -32.1362 │          5 │ -          │ -           │ -           │
│ Fitted model  │         -32.2281 │          2 │ 0.183639   │ 3           │ 0.980186    │
│ Reduced model │         -68.0292 │          1 │ 71.7859    │ 4           │ 9.54792e-15 │
╘═══════════════╧══════════════════╧════════════╧════════════╧═════════════╧═════════════╛

Change input settings

Model settings can be customized for a run, as with standard dichotomous models.

model = MultistageCancer(
    dataset, 
    settings={
        "bmr_type": pybmds.DichotomousRiskType.AddedRisk, 
        "bmr": 0.15, 
        "degree": 3,
    },
)
print(model.settings.tbl())
╒══════════════════════════════╤════════════════════════╕
│ BMR                          │ 15% Added Risk         │
│ Confidence Level (one sided) │ 0.95                   │
│ Modeling approach            │ frequentist_restricted │
│ Degree                       │ 3                      │
╘══════════════════════════════╧════════════════════════╛

Change parameter settings

Initial parameter settings are different for the MultistageCancer model compared with the dichotomous Multistage:

from pybmds.models.dichotomous import Multistage, MultistageCancer

model = Multistage(dataset)
print("Multistage parameter settings:")
print(model.priors_tbl())

model = MultistageCancer(dataset)
print("Multistage Cancer parameter settings:")
print(model.priors_tbl())
Multistage parameter settings:
╒═════════════╤═══════════╤═══════╤═══════╕
│ Parameter   │   Initial │   Min │   Max │
╞═════════════╪═══════════╪═══════╪═══════╡
│ g           │         0 │   -18 │    18 │
│ b1          │         0 │     0 │ 10000 │
│ b2          │         0 │     0 │ 10000 │
╘═════════════╧═══════════╧═══════╧═══════╛
Multistage Cancer parameter settings:
╒═════════════╤═══════════╤═══════╤═══════╕
│ Parameter   │   Initial │   Min │   Max │
╞═════════════╪═══════════╪═══════╪═══════╡
│ g           │     -17   │   -18 │    18 │
│ b1          │       0.1 │     0 │ 10000 │
│ b2          │       0.1 │     0 │ 10000 │
╘═════════════╧═══════════╧═══════╧═══════╛

For Multistage models, the b2 parameter setting is reused for all beta parameters greater than or equal to b2.

These can be updated:

model.settings.priors.update("g", initial_value=0, min_value=-10, max_value=10)
model.settings.priors.update("b1", initial_value=10, min_value=0, max_value=100)
model.settings.priors.update("b2", initial_value=20, min_value=0, max_value=1000)

print(model.priors_tbl())
╒═════════════╤═══════════╤═══════╤═══════╕
│ Parameter   │   Initial │   Min │   Max │
╞═════════════╪═══════════╪═══════╪═══════╡
│ g           │         0 │   -10 │    10 │
│ b1          │        10 │     0 │   100 │
│ b2          │        20 │     0 │  1000 │
╘═════════════╧═══════════╧═══════╧═══════╛

Fit multiple models

The previous example runs a single Multitumor model to a single dataset. However, you may want, for example, to run multiple multitumor models of varying degrees to a single dataset.

Multiple dataset fit

To fit multiple models and one or more datasets, use an instance of the Multitumor class:

import pybmds

datasets = [
    pybmds.DichotomousDataset(
        doses=[0, 25, 75, 125, 200],
        ns=[20, 20, 20, 20, 20],
        incidences=[0, 1, 7, 15, 19],
        name="Tumor A",
        dose_units="mg/m³",
    ),
    pybmds.DichotomousDataset(
        doses=[0, 25, 75, 125, 200],
        ns=[20, 20, 20, 20, 20],
        incidences=[0, 0, 1, 7, 11],
        name="Tumor B",
        dose_units="mg/m³",
    ),
]

session = pybmds.Multitumor(datasets)
session.execute()

print(session.results.tbl())
session.plot()
╒══════════════════════════════════╤════════════╕
│ BMD                              │   8.8935   │
│ BMDL                             │   7.1139   │
│ BMDU                             │  11.2427   │
│ Slope Factor                     │   0.014057 │
│ Combined Log-likelihood          │ -70.8752   │
│ Combined Log-likelihood Constant │ -53.1841   │
╘══════════════════════════════════╧════════════╛
../_images/98997d12d03fc0f7556aeeaeca7c743b3c3abdf0d71edf7f2f11da872fd19dc6.png

You can generate Excel and Word exports:

# save excel report
df = session.to_df()
df.to_excel("output/report.xlsx")

# save to a word report
report = session.to_docx()
report.save("output/report.docx")

Change model settings

Settings for all datasets and models should be configured globally and are applied to all models:

session = pybmds.Multitumor(datasets, settings={
    "bmr_type": pybmds.DichotomousRiskType.AddedRisk, 
    "bmr": 0.15,
})

Change model degree

By default, multiple models are executed for each dataset, where the degree is varied from 1 to the number of doses minus 1 (and a maximum of 8).

For this example, we first create three datasets:

datasets = [
    pybmds.DichotomousDataset(
        doses=[0, 2, 3, 4, 5, 6, 7, 8, 9],
        ns=[20, 20, 20, 20, 20, 20, 20, 20, 20],
        incidences=[0, 1, 4, 8, 11, 12, 13, 14, 15],
        name="Tumor A (9 groups)",
        dose_units="mg/m³",
    ),
    pybmds.DichotomousDataset(
        doses=[0, 2, 3, 4, 5, 6, 7, 8, 9],
        ns=[20, 20, 20, 20, 20, 20, 20, 20, 20],
        incidences=[0, 1, 7, 15, 19, 19, 19, 19, 19],
        name="Tumor B (9 groups)",
        dose_units="mg/m³",
    ),
    pybmds.DichotomousDataset(
        doses=[0, 2, 3, 4, 5],
        ns=[20, 20, 20, 20, 20],
        incidences=[0, 0, 1, 7, 11],
        name="Tumor C (5 groups)",
        dose_units="mg/m³",
    ),
]

Next, we specify which model degrees to run for each dataset using degrees. Setting a value of 0 runs all degrees available up to a maximum of 8; specifying a specific degree will only run the specified degree.

degrees = [0, 3, 2]
session = pybmds.Multitumor(datasets, degrees=degrees)
session.execute()
session.plot()
../_images/ba9ffa104773a87fc9048012ccda18d24d412a852c820e8114511c8217c13459.png

The analysis executed the following models for each dataset:

for dataset_models in session.models:
    print(f"{dataset_models[0].dataset.metadata.name}")
    for model in dataset_models:
        print("\t" + model.name())
Tumor A (9 groups)
	Multistage 1
	Multistage 2
	Multistage 3
	Multistage 4
	Multistage 5
	Multistage 6
	Multistage 7
	Multistage 8
Tumor B (9 groups)
	Multistage 3
Tumor C (5 groups)
	Multistage 2