Marginal generalized function-on-scalar regression¶
Version 0.51 adds a distinct non-Gaussian functional-response model after the Gaussian mixed-effects covariance sequence was closed at 0.50. Version 0.53 adds an explicit Poisson exposure/rate observation contract, and version 0.54 adds explicit grouped-binomial success/denominator observations without changing the marginal GEE estimand.
For participant \(i\), trial \(j\), and observed time \(t\),
The coefficient functions are represented in an analyst-declared clamped B-spline basis,
The generalized layer supports two deliberately narrow families:
- binomial responses with logit link, represented either as Bernoulli 0/1 observations or explicit grouped integer successes plus integer denominators;
- Poisson count responses with log link, optionally with an explicit strictly positive exposure.
The estimand is marginal / population averaged. There are no functional random effects in this model.
Why GEE rather than another mixed model¶
For non-Gaussian outcomes, conditional mixed-model coefficients and marginal population-average coefficients are not generally numerically equivalent. The 0.51 API therefore makes the estimand explicit instead of reusing the Gaussian mixed-effects interface.
Every participant is one independent GEE cluster. All of that participant's trial-by-time observations remain in the fit, so predictors may vary across trials.
The first tranche fixes the working dependence structure to independence and uses the robust sandwich covariance. The working correlation is not estimated, compared, or selected.
API¶
from eyetrajectoriespy import (
fit_generalized_function_on_scalar_regression,
)
fit = fit_generalized_function_on_scalar_regression(
trajectories,
design,
predictors=("condition",),
participant_column="participant_id",
dimension="target_aoi",
family="binomial",
basis_size=4,
spline_degree=2,
)
For a count trajectory:
count_fit = fit_generalized_function_on_scalar_regression(
count_trajectories,
design,
predictors=("condition",),
participant_column="participant_id",
dimension="fixation_count",
family="poisson",
basis_size=4,
spline_degree=2,
)
No family, link, basis size, interaction, categorical encoding, working correlation, or model is chosen automatically.
Expanded design¶
Let
and let
The stacked GEE design contains the products
so each scalar coefficient receives its own \(q\)-dimensional basis coefficient vector.
The expanded design must be full rank.
Cluster-count guard¶
If there are \(K\) scalar coefficients including the intercept and \(q\) B-spline functions per coefficient, the expanded coefficient vector has
free parameters.
Version 0.51 requires
This is a minimum structural guard, not a theorem that the robust sandwich covariance is accurately estimated. The number and heterogeneity of independent participants remain scientifically important.
Response contracts¶
Bernoulli¶
The binary response is required to be coded exactly as
Aggregated proportions are not treated as Bernoulli observations and trial denominators are never inferred silently.
For grouped binomial data, version 0.54 requires an integer success-count trajectory \(S_{ij}(t)\) and an explicit positive integer denominator \(N_{ij}(t)\),
with
grouped_fit = fit_generalized_function_on_scalar_regression(
success_count_trajectories,
design,
predictors=("condition",),
participant_column="participant_id",
dimension="successes",
family="binomial",
binomial_denominator=trial_counts,
basis_size=4,
spline_degree=2,
)
binomial_denominator must have shape (n_curves, n_time) or
(n_curves,); curve-level denominators are explicitly expanded over time and
that expansion is recorded. Denominators must be finite, strictly positive
integers. Successes must be finite non-negative integers and cannot exceed the
corresponding denominator.
The public API intentionally does not accept arbitrary proportions as a
substitute. A value such as 0.67 is scientifically ambiguous because 2/3 and
670/1000 carry very different information. Internally, after validation, the
backend receives \(S/N\) as the binomial response and \(N\) as the GEE
observation weight. The package retains the original successes, denominators,
observed proportions, and fitted denominator-specific expected successes.
The grouped backend is regression-tested against the equivalent row-expanded Bernoulli representation under the same participant clustering and working independence, including the robust sandwich covariance.
The Bernoulli model is
Poisson¶
The count response must be a non-negative integer:
The model is
Without an exposure argument, this remains the 0.51/0.52 expected-count model.
With a scientifically meaningful exposure \(E_{ij}(t)>0\), version 0.53 fits
so that
The package exposes exposure, not a generic arbitrary offset. Exposure must
be supplied explicitly with shape (n_curves, n_time), or with shape
(n_curves,) when one value applies to a complete curve. Curve-level values
are expanded over time and that expansion is recorded in provenance. Exposure
must be finite and strictly positive everywhere; zero exposure is rejected even
when the observed count is zero.
rate_fit = fit_generalized_function_on_scalar_regression(
count_trajectories,
design,
predictors=("condition",),
participant_column="participant_id",
dimension="fixation_count",
family="poisson",
exposure=observed_duration,
exposure_units="seconds",
basis_size=4,
spline_degree=2,
)
The package never infers exposure from grid spacing, trial duration, sample counts, or metadata. The scientific assumption is substantive: \(E[Y\mid x,E]=E\lambda(x)\), so expected counts are assumed to scale proportionally with the declared opportunity/time denominator.
Robust covariance¶
With working independence, the GEE point estimate is combined with the robust cluster sandwich covariance,
where the middle empirical term accumulates cluster-level score contributions.
The package reports coefficient-function pointwise standard errors obtained by mapping the robust basis-parameter covariance back through \(\mathbf B(t)\).
Naive working-correlation standard errors are not exposed in 0.51.
Whole-participant bootstrap¶
For whole-function simultaneous inference, 0.51 resamples participants, not trial-time rows:
from eyetrajectoriespy import (
bootstrap_generalized_function_on_scalar_coefficients,
)
bootstrap = bootstrap_generalized_function_on_scalar_coefficients(
fit,
n_bootstrap=1000,
random_state=51,
)
Every sampled participant occurrence carries all of its curves and observed time points. If a source participant is sampled more than once, each occurrence receives a distinct bootstrap GEE group identity so duplicated source clusters are not incorrectly treated as one cluster.
Every bootstrap replicate refits the same family, link, basis and working-independence GEE.
Failed replicates are not silently dropped or redrawn.
Simultaneous bands¶
from eyetrajectoriespy import (
generalized_function_on_scalar_simultaneous_bands,
)
band = generalized_function_on_scalar_simultaneous_bands(
bootstrap,
confidence_level=0.95,
simultaneous_scope="coefficient",
)
For coefficient \(k\), bootstrap replicate \(b\), and observed grid point \(t_m\),
The resulting band is
With simultaneous_scope="family", one maximum is taken across all declared
coefficient functions and observed time points.
These are link-scale coefficient bands. They are not automatically converted into probability/count-mean bands because a response-scale effect depends on the full scalar predictor vector.
Version 0.52 adds that downstream interpretation only after the analyst declares the complete scalar predictor profile(s). See fixed-profile marginal prediction and contrasts.
Exposure audit¶
For an exposure-adjusted Poisson fit, inspect the observation contract directly:
from eyetrajectoriespy import generalized_function_on_scalar_exposure_frame
exposure_audit = generalized_function_on_scalar_exposure_frame(rate_fit)
print(exposure_audit)
print(exposure_audit.attrs["exposure_audit"])
The audit reports per-curve minimum/maximum exposure, within-curve range, whether exposure varies over time, declared units, global minimum/maximum, between-curve variation, and the global maximum/minimum exposure ratio. Large ratios are exposed for review rather than automatically rejected.
Inspect and report¶
from eyetrajectoriespy import (
generalized_function_on_scalar_coefficient_frame,
generalized_function_on_scalar_reporting_text,
plot_generalized_function_on_scalar_coefficients,
)
frame = generalized_function_on_scalar_coefficient_frame(band)
plot_generalized_function_on_scalar_coefficients(
band,
coefficient="condition",
)
print(
generalized_function_on_scalar_reporting_text(
fit,
band=band,
)
)
Interpretation¶
For a Bernoulli/logit model, \(\beta_k(t)\) is a time-varying marginal log-odds coefficient.
For a Poisson/log model without exposure, \(\beta_k(t)\) remains a time-varying marginal log-mean-count coefficient.
For a Poisson/log model with explicit exposure, \(\beta_k(t)\) is a time-varying marginal log-rate coefficient and \(\exp\{\beta_k(t)\}\) is a multiplicative rate ratio for a one-unit predictor change, holding exposure fixed.
These coefficients are not subject-specific effects conditional on functional random effects.
Limitations¶
The generalized contract intentionally does not include:
- arbitrary proportion-only grouped-binomial input without integer successes and explicit denominators;
- arbitrary user-defined offsets or silently inferred exposure/denominators;
- exposure-measurement-error models;
- negative-binomial or zero-inflated families;
- automatically selected working correlation;
- penalized/smoothing-parameter selection;
- generalized functional random effects;
- sparse/irregular response grids;
- between-grid simultaneous coverage.
Those extensions require separate estimands and validation and are not silently approximated.