Simulate sensor records whose response acts at a known temporal grain
Source:R/simulate.R
simulate_records.RdDraws units carrying a year of sensor readings and a multi-variable presence-absence response
whose dependence on the record is a fixed linear functional of the record at one named grain.
The grain is therefore known before anything is fitted, which is what makes the output usable
for asking whether select_grain() finds it.
Usage
simulate_records(
n = 300L,
mechanism = c("none", "event", "season", "lag"),
variables = 10L,
prevalence = 0.1,
auc = 0.75,
from = "2021-09-01",
days = 365L,
step_hours = 3,
seasonal = 8,
offset_sd = 1,
anomaly_sd = 1,
anomaly_days = 2,
offset_effect = 0,
sensor_sd = 0.3,
year_start = "09-01",
seed = 1L,
draw = 1L
)Arguments
- n
Number of units to draw.
- mechanism
Which generating mechanism, see The mechanisms.
- variables
Number of response variables. Each gets its own weights within the mechanism.
- prevalence
Marginal probability of presence, shared by every variable.
- auc
Population area under the ROC curve of the driver against the response.
- from
First reading instant,
"YYYY-MM-DD", read as UTC.- days
Length of the record in days.
- step_hours
Sampling step in hours. Must divide 24.
- seasonal
Amplitude of the seasonal cycle shared by every unit, in reading units.
- offset_sd
Standard deviation of the unit-level thermal offset.
- anomaly_sd
Marginal standard deviation of each unit's AR(1) anomaly.
- anomaly_days
Correlation time of that anomaly, in days.
- offset_effect
Weight the unit-level offset enters the driver with. At the default
0the driver reads the unit's anomaly alone, so a grain coarse enough to average the anomaly away loses the signal. At1the offset carries the response as well, and since every grain however coarse reports the offset, every grain is then equally good: that is the grain-invariant control, not a temporal mechanism.- sensor_sd
Standard deviation of the measurement noise added to the latent record. The response is generated from the latent record; the readings returned carry this noise.
- year_start
"MM-DD"boundary of the hydrological year, passed tograin_matrix()when the mechanism's bins are located. Use the same value when representing the readings.- seed
Seed of the design: the weights and the link coefficients. Two calls with the same
seedand differentdrawshare a design and draw independent units, which is what lets a held-out deployment sample be drawn from the same population as a training sample.- draw
Seed of the unit draw. Also names the units, so two draws never collide.
Value
A timesift_simulation: a list with readings, the long table grain_matrix() takes;
y, the [unit, variable] 0/1 response; driver, the standardised driver z behind it;
grain, the true grain or NA; weights, the [reading, variable] weights defining the
driver; link, the solved b0 and b1; and design, the settings the draw is reproducible
from.
What the true grain is
The response is driven by g_ij = sum_t w_j(t) * a_i(t), a weighted mean of unit i's latent
anomaly: the record with the seasonal cycle every unit shares and the unit's own constant
offset taken out, since neither of those is temporally located and a grain of any width reports
both. The weights w_j are constant within the bins of one grain and zero outside
a short stretch of them, so g is exactly a linear combination of that grain's bin means. The
true grain of a mechanism is the coarsest grain of grain_matrix() at which g is still an
exact linear functional of the representation: at that grain and at every grain whose bins
nest inside it, no information about g has been averaged away, and at any coarser grain some
has. Finer grains keep the information but spread it over more coefficients, so they lose to
the true grain by variance rather than by bias, which is the tension the selection has to
resolve.
The mechanisms
"none"No temporal signal. The driver is a standard normal drawn independently of the record, so nothing in the readings carries information about the response and no grain is correct.
grainisNA."event"An isolated event. The weights are uniform over three consecutive day bins at a fixed calendar position, one position per variable. True grain
"day": a week bin mixes the three days with four others and cannot be unmixed."season"A smooth seasonal response. The weights are uniform over one whole season bin, one season per variable, cycled. True grain
"season": month and day bins nest inside a season so they are exact too, a year bin mixes all four seasons."lag"A lagged, cumulative response. The weights decay geometrically over four consecutive week bins from a fixed anchor, one anchor per variable. True grain
"week": day bins nest inside weeks so they are exact too, month bins straddle week boundaries.
How the skill is set rather than emergent
The driver is standardised to a standard normal by its population mean and standard deviation,
both computed in closed form from the generating parameters rather than from the drawn units, so
every draw and every chunk of a draw is on the same scale. The response is
y_ij ~ Bernoulli(plogis(b0 + b1 z_ij)) with b0 and b1 solved by numerical integration so
that the marginal prevalence is prevalence and the population area under the ROC curve of z
is auc. auc is therefore a ceiling no fitted model reaches: the response is generated from
the latent record and the readings carry sensor_sd of measurement noise on top of it, and the
weights have to be estimated.
See also
select_grain(), which this exists to test, and grain_matrix(), whose calendar the
weights are defined on.
Examples
sim <- simulate_records(n = 40L, mechanism = "event", variables = 2L, days = 60L)
sim
sim$grain
x <- grain_matrix(sim$readings, unit, time, reading, grain = c("day", "month"))
dim(x$day)