axiom

Bringing your own problem · step 3 of 6

Write the spec

What shape do you believe the response has, and how sure are you?

A SurfaceSpec is the model, and it is the object most worth understanding rather than copying. One kernel per treatment says what shape the dose-response is allowed to take; the intercept says what the units are allowed to differ by; the scales are your priors.

None of these is a default you can skip past. HillKernel says saturating with a half-way point near the reference dose — say that and a linear response can no longer be fitted, which is the point of saying it. The spec hashes, so the model that produced a number is identifiable later by more than a filename.

step 3
from axiom.surface import HillKernel, SurfaceSpec

spec = SurfaceSpec(
    name="leaflets_footfall",
    treatments=(leaflets,),
    outcome=footfall,
    # saturating, with the half-way point expected near 250 USD and
    # a lift of order 10 visits at saturation
    kernels={"leaflets": HillKernel(
        reference_dose=250.0, amplitude_scale=10.0,
    )},
    intercept="shared",
    unit_column="store",
    time_column="week",
    intercept_scale=8.0,
    noise_scale=3.0,
)
print(f"model      : {spec.name}")
print(f"content hash: {spec.content_hash()[:16]}")
print(f"treatments : {[t.name for t in spec.treatments]}")
print(f"intercept  : {spec.intercept}")
print()
print("`shared` says the stores share one baseline. They do not -- the")
print("next step shows what that costs, and it is visible in sigma.")
what it printed
model      : leaflets_footfall
content hash: 3079cac0db4889ac
treatments : ['leaflets']
intercept  : shared

`shared` says the stores share one baseline. They do not -- the
next step shows what that costs, and it is visible in sigma.

Every line above was captured by running this code at build time. The steps share one namespace, so the code on this page is the code you would type after the steps before it.