The geometric model of visual meteor magnitudes is a commonly used statistical approach to describe the magnitude distribution of a meteor shower. The observable magnitude distribution of meteors is then \[ P[M = m] \sim \begin{cases} f(m_{\mathrm{lim}} - m)\, r^m, & \text{if } m_{\mathrm{lim}} - m > -0.5,\\[5pt] 0 & \text{otherwise,} \end{cases} \]
where \(m_{\mathrm{lim}}\) denotes the limiting (non-integer) magnitude of the observation, and \(m\) the integer meteor magnitude. The function \(f(\cdot)\) denotes the perception probability function.
The estimation of the population index r, briefly called the r-value, is a common task in the evaluation of meteor magnitudes. Here we demonstrate several methods to estimate this parameter.
First, we obtain some magnitude observations from the example data set, which also includes the limiting magnitude.
observations <- with(PER_2015_magn$observations, {
idx <- !is.na(lim_magn) & sl_start > 135.81 & sl_end < 135.87
data.frame(
magn_id = magn_id[idx],
lim_magn = lim_magn[idx]
)
})
head(observations, 5) # Example values| magn_id | lim_magn |
|---|---|
| 225413 | 5.30 |
| 225432 | 5.95 |
| 225438 | 6.01 |
| 225449 | 6.48 |
| 225496 | 5.50 |
Next, the observed meteor magnitudes are matched with the corresponding observations. This is necessary as we need the limiting magnitudes of the observations to determine the r-value.
Using
magnitudes <- with(new.env(), {
magnitudes <- merge(
observations,
as.data.frame(PER_2015_magn$magnitudes),
by = "magn_id"
)
magnitudes$magn <- as.integer(as.character(magnitudes$magn))
subset(magnitudes, (magnitudes$lim_magn - magnitudes$magn) > -0.5)
})
head(magnitudes, 5) # Example valueswe obtain a data frame with the absolute observed frequencies
Freq for each observation of a magnitude class. The
expression
subset(magnitudes, (magnitudes$lim_magn - magnitudes$magn) > -0.5
ensures that meteors fainter than the limiting magnitude are not used if
they exist.
| magn_id | lim_magn | magn | Freq | |
|---|---|---|---|---|
| 9 | 225413 | 5.30 | 4 | 1.0 |
| 11 | 225413 | 5.30 | 1 | 2.0 |
| 14 | 225413 | 5.30 | 3 | 3.0 |
| 15 | 225432 | 5.95 | 4 | 2.0 |
| 17 | 225432 | 5.95 | 3 | 1.5 |
This data frame contains a total of 97 meteors. This is a sufficiently large number to estimate the r-value.
The maximum likelihood method can be used to estimate the r-value in
an asymptotically unbiased manner. For this, the function
dvmgeom() is needed, which returns the probability density
of the observable meteor magnitudes when the r-value and the limiting
magnitudes are known.
The following algorithm estimates the r-value by maximizing the
likelihood with the optim() function. The function
ll() returns the negative log-likelihood, as
optim() identifies a minimum.
# maximum likelihood estimation (MLE) of r
result_ml <- with(magnitudes, {
# log likelihood function
ll <- function(r) -sum(Freq * dvmgeom(magn, lim_magn, r, log = TRUE))
r_start <- 2.0 # starting value
r_lower <- 1.2 # lowest expected value
r_upper <- 4.0 # highest expected value
# find minimum
optim(r_start, ll, method = "Brent", lower = r_lower, upper = r_upper, hessian = TRUE)
})This gives the expected value and the variance of the r-value:
print(result_ml$par) # mean of r
#> [1] 2.344528
print(1 / result_ml$hessian[1][1]) # variance of r
#> [1] 0.01718636We can additionally visualize the likelihood function here.
with(new.env(), {
data_plot <- data.frame(r = seq(2.0, 2.8, 0.01))
data_plot$ll <- mapply(function(r) {
with(magnitudes, {
# log likelihood function
sum(Freq * dvmgeom(magn, lim_magn, r, log = TRUE))
})
}, data_plot$r)
data_plot$l <- exp(data_plot$ll - max(data_plot$ll))
data_plot$l <- data_plot$l / sum(data_plot$l)
brks <- seq(min(data_plot$r) - 0.02, max(data_plot$r) + 0.02, by = 0.02)
plot(data_plot$r, data_plot$l,
# breaks = brks,
type = "l",
col = "blue",
xlab = "r",
xaxt = "n",
ylab = "likelihood"
)
xlabels <- seq(min(round(data_plot$r, 1)) - 0.1, max(round(data_plot$r, 1)) + 0.1, by = 0.1)
axis(
side = 1,
at = xlabels,
labels = sprintf("%.1f", xlabels)
)
abline(v = result_ml$par, col = "red", lwd = 1)
})The likelihood function is approximately a normal distribution in this case. This is important in this context because the variance of the estimated r-value is derived from the curvature (the second derivative at the maximum) of the log-likelihood function.
The maximum likelihood estimate can also be obtained as a generalized
linear model. vmgeom_glm() fits the same likelihood through
glm(), which becomes useful as soon as the r-value is no
longer assumed to be constant.
The response is passed as a two-column matrix holding the meteor
magnitude and the corresponding limiting magnitude. Supplying the
limiting magnitude as part of the response ensures that it is subsetted
together with the remaining data whenever glm() drops rows.
The observed frequencies enter as weights.
# rows with a frequency of zero carry no information
magnitudes_obs <- subset(magnitudes, magnitudes$Freq > 0)
result_glm <- vmgeom_glm(
cbind(magn, lim_magn) ~ 1,
data = magnitudes_obs,
weights = magnitudes_obs$Freq
)The link function is \(\eta = \mathrm{logit}(1/r) = -\log(r-1)\). The quantity \(1/r\) is the factor by which the observable rate drops when the limiting magnitude is lowered by one, and therefore lies between \(0\) and \(1\), which makes a logit link the natural choice. It also enforces \(r > 1\) for every finite \(\eta\), a constraint the geometric model requires.
Rather than back-transforming the coefficients by hand,
predict() returns the population index directly. Both
methods agree, which is expected: without covariates this is an
intercept-only fit of the same likelihood.
# the model has no covariates, so any single row predicts the global r-value
newdata <- data.frame(row.names = "")
print(c(
"ML" = result_ml$par,
"GLM" = as.numeric(predict(result_glm, newdata, type = "r"))
))
#> ML GLM
#> 2.344528 2.344528The same holds for the uncertainty. The maximum likelihood standard deviation comes from the curvature of the log-likelihood, the one of the linear model from the standard error of \(\eta\), transformed with the delta method:
print(c(
"ML" = sqrt(1 / result_ml$hessian[1][1]),
"GLM" = as.numeric(predict(result_glm, newdata, type = "r", se.fit = TRUE)$se.fit)
))
#> ML GLM
#> 0.1310968 0.1310969For a single r-value the likelihood approach is the cheaper one. On
the data set used here it is roughly twice as fast as the linear model,
since glm() carries the overhead of its iterative fitting
on top of the same likelihood. Both are a matter of milliseconds, and
for aggregated frequencies the cost is governed by the number of
magnitude classes rather than by the number of meteors.
Predictions are available on several scales — "r",
"inv_r", "log_r" and "link" — and
se.fit = TRUE adds confidence limits in addition to the
standard error. The limits are formed on the link scale and then
transformed, so they never fall below 1.
print(predict(result_glm, newdata, se.fit = TRUE))
#> $fit
#>
#> 2.344528
#>
#> $se.fit
#>
#> 0.1310969
#>
#> $lwr
#>
#> 2.110642
#>
#> $upr
#>
#> 2.627666The real benefit appears once covariates are added. Because the fit
is an ordinary glm object, summary(),
anova(), AIC()/BIC() and
select_knots() can all be used. A model in which the
r-value varies with solar longitude would be written as
and a smooth activity-dependent profile as
Since the family reports the exact log-likelihood of the fitted
distribution, AIC() also allows a comparison against a
different magnitude distribution altogether. Fitting the ideal
distribution to the same data with vmideal_glm() gives:
result_ideal <- vmideal_glm(
cbind(magn, lim_magn) ~ 1,
data = magnitudes_obs,
weights = magnitudes_obs$Freq
)
print(c("geometric" = AIC(result_glm), "ideal" = AIC(result_ideal)))
#> geometric ideal
#> 353.1941 350.6568Each model has one parameter, so the comparison reduces to their likelihoods. The ideal distribution attains the lower value here, though by a margin that this excerpt of 97 meteors does not resolve — a difference of a few units is weak evidence, and the two models are hard to tell apart at the limiting magnitudes involved. Such a comparison is only valid when both fits use the same observations and the same response.
Covariates can also be accommodated on a different basis, by transforming the magnitudes first; see the section on the variance-stabilizing transformation below.
This method asks how strongly the observed rate would drop if the limiting magnitude were lowered by one. Under the model of the introduction, the expected number of meteors recorded at limiting magnitude \(m_{\mathrm{lim}}\) is proportional to
\[ S(m_{\mathrm{lim}}) \;=\; \sum_{m \in \mathbb{Z}} f(m_{\mathrm{lim}} - m)\, r^{m}, \]
each magnitude class contributing its population share \(r^{m}\) weighted by the probability \(f(\cdot)\) of actually perceiving it. Lowering the limiting magnitude by one and forming the ratio gives the relative drop of the rate,
\[ \frac{S(m_{\mathrm{lim}} - 1)}{S(m_{\mathrm{lim}})} \;=\; \frac{1}{r}, \]
which turns out to be exactly \(1/r\) — independent of \(m_{\mathrm{lim}}\) and of the shape of \(f(\cdot)\). Measuring this drop is what gives the method its name.
The rates themselves are not observed, but the same ratio can be formed per meteor. For a meteor of magnitude \(m\) observed at limiting magnitude \(m_{\mathrm{lim}}\) let
\[ a(m, m_{\mathrm{lim}}) \;=\; \frac{f(m_{\mathrm{lim}} - m - 1)}{f(m_{\mathrm{lim}} - m)}, \]
the factor by which its chance of being perceived would shrink if the limiting magnitude were lowered by one, and write \(a_i = a(m_i, m_{\mathrm{lim},i})\) for the \(i\)-th observed meteor. This leads to
\[ \mathbb{E}\!\left[\frac{1}{n}\sum_{i=1}^{n} a_i\right] = \frac{1}{r}, \]
so averaging \(a\) over the observed meteors estimates \(1/r\) without bias. The derivation below proves both identities.
Setting. Fix a limiting magnitude \(m_{\mathrm{lim}}\) and let \(M\) denote the magnitude of a randomly selected observed meteor. Its distribution is
\[ P[M = m] \;=\; \frac{f(m_{\mathrm{lim}} - m)\, r^{m}}{S(m_{\mathrm{lim}})}. \]
Two properties of the perception probability are used, and nothing else:
Let \(m_{\max}\) be the largest class with \(f(m_{\mathrm{lim}} - m_{\max}) > 0\). The support of \(M\) is contained in \(\{m \le m_{\max}\}\), and on it the denominator of \(a(m, m_{\mathrm{lim}})\) is strictly positive, so \(a(M, m_{\mathrm{lim}})\) is well defined almost surely.
Claim. \(\mathbb{E}\bigl[a(M, m_{\mathrm{lim}})\bigr] = 1/r\) for every \(m_{\mathrm{lim}}\).
Proof. Writing out the expectation and cancelling the perception probability, which is legitimate because it is non-zero on the support,
\[ \begin{aligned} \mathbb{E}\bigl[a(M, m_{\mathrm{lim}})\bigr] &= \sum_{m \le m_{\max}} \frac{f(m_{\mathrm{lim}} - m)\, r^{m}}{S(m_{\mathrm{lim}})} \cdot \frac{f(m_{\mathrm{lim}} - m - 1)}{f(m_{\mathrm{lim}} - m)} \\[5pt] &= \frac{1}{S(m_{\mathrm{lim}})} \sum_{m \le m_{\max}} f(m_{\mathrm{lim}} - m - 1)\, r^{m}. \end{aligned} \]
The remaining sum is by definition \(S(m_{\mathrm{lim}} - 1)\), so that \(\mathbb{E}\bigl[a(M, m_{\mathrm{lim}})\bigr] = S(m_{\mathrm{lim}} - 1) / S(m_{\mathrm{lim}})\): the expectation of \(a\) is precisely the relative drop of the rate stated above.
Before shifting the index, note that its uppermost term already vanishes. At \(m = m_{\max}\) the numerator is evaluated one class beyond the faintest perceptible one,
\[ f\bigl(m_{\mathrm{lim}} - m_{\max} - 1\bigr) = 0, \]
so the summation effectively stops at \(m_{\max} - 1\). This is the step where the vanishing perception probability enters, and it is what makes the shift below land exactly on the range of \(S(m_{\mathrm{lim}})\). Replacing \(m\) by \(m - 1\) therefore gives
\[ \begin{aligned} \sum_{m \,\le\, m_{\max}} f(m_{\mathrm{lim}} - m - 1)\, r^{m} &= \sum_{m \,\le\, m_{\max} - 1} f(m_{\mathrm{lim}} - m - 1)\, r^{m} \\[5pt] &= \sum_{m \,\le\, m_{\max}} f(m_{\mathrm{lim}} - m)\, r^{m - 1} \\[5pt] &= \frac{1}{r} \sum_{m \,\le\, m_{\max}} f(m_{\mathrm{lim}} - m)\, r^{m} \\[5pt] &= \frac{S(m_{\mathrm{lim}})}{r}, \end{aligned} \]
where the last sum is \(S(m_{\mathrm{lim}})\) because \(f(m_{\mathrm{lim}} - m) = 0\) for every \(m > m_{\max}\), so extending it to all integers adds nothing. Both series converge absolutely, since \(r^{m} \to 0\) as \(m \to -\infty\), which is what permits the shift. Altogether
\[ \begin{aligned} \mathbb{E}\bigl[a(M, m_{\mathrm{lim}})\bigr] &= \frac{1}{S(m_{\mathrm{lim}})} \cdot \frac{S(m_{\mathrm{lim}})}{r} \\[5pt] &= \frac{1}{r}. \qquad \blacksquare \end{aligned} \]
Along the way this also proves the rate identity claimed above, since the intermediate result was \(S(m_{\mathrm{lim}} - 1) = S(m_{\mathrm{lim}})/r\).
Finally, the second identity follows. For \(n\) meteors observed under limiting magnitudes \(m_{\mathrm{lim},1}, \dots, m_{\mathrm{lim},n}\), each \(a_i\) satisfies \(\mathbb{E}[a_i] = 1/r\) by the claim, since it holds for every limiting magnitude separately. Linearity of the expectation then gives
\[ \mathbb{E}\!\left[\frac{1}{n}\sum_{i=1}^{n} a_i\right] = \frac{1}{r}. \]
The sample mean of \(a\) is thus an unbiased estimator of \(1/r\) — exactly, not merely asymptotically, and irrespective of whether the observations share the same limiting magnitude. This is what allows observations made under different conditions to be pooled. The estimator is inexpensive to compute and directly comparable to the likelihood-based approaches discussed earlier.
Unbiasedness for \(1/r\) does not carry over to \(r\) itself, however: \(1/\bar{a}\) is a non-linear function of \(\bar{a}\), which is why the delta method is applied below.
A second property concerns the bright end. Once a meteor is bright
enough that both perception probabilities are close to 1.0,
the ratio approaches 1 as well, and it no longer matters
how bright the meteor actually was. At a limiting magnitude of
6.0 the ratio is already 0.999 for a magnitude
\(-3\) meteor and indistinguishable
from 1 for anything brighter. Such meteors are therefore
effectively pooled into a single class, and the information about their
exact brightness is lost.
In practice this is not a drawback but an advantage. Meteors that bright are rare — at \(r = 2.5\) and the same limiting magnitude, magnitude \(-3\) and brighter accounts for less than one percent of all observed meteors — so little information is discarded. In exchange, the pooling keeps a single very bright meteor from acting as an outlier, precisely in the part of the magnitude range where one observation would otherwise carry a lot of weight. The same applies to the variance-stabilizing transformation below, which is built on this ratio.
We estimate \(r\) as \(1/\bar{a}\) from the sample mean \(\bar{a}\). Since \(r\) is a non-linear function of \(\bar{a}\), we apply the delta method to correct for bias and to compute the variance of \(r\). The delta method uses a Taylor expansion around the mean of \(a\) to approximate the distribution of \(r\). Note that the variance of \(a\) entering it depends on \(r\) itself; the section on the variance-stabilizing transformation below starts from this same statistic and removes that dependency.
result_rate <- with(magnitudes, {
N <- sum(Freq)
a <- vmperception(lim_magn - magn - 1) / vmperception(lim_magn - magn)
a_mean <- as.numeric(weighted.mean(a, w = Freq))
a_var <- as.numeric(cov.wt(cbind(a), wt = Freq)$cov) / N
# apply the delta method and return the result
list(
"mean" = 1 / a_mean - a_var / a_mean^3,
"var" = a_var / a_mean^4
)
})This gives the expected value and the variance of the r-value:
print(result_rate$mean) # mean of r
#> [1] 2.323974
print(result_rate$var) # variance of r
#> [1] 0.01273307Using the bootstrap method, it can be assessed whether the mean is normally distributed.
# Bootstrapping Method
r_means <- with(magnitudes, {
N <- sum(Freq)
a <- vmperception(lim_magn - magn - 1) / vmperception(lim_magn - magn)
replicate(50000, {
s <- sample(a, size = N, replace = TRUE, prob = Freq)
1 / mean(s)
})
})The graphical representation indicates that this is indeed approximately the case.
with(new.env(), {
r_sd <- sqrt(result_rate$var)
r_min <- result_rate$mean - 3 * r_sd
r_max <- result_rate$mean + 3 * r_sd
r <- subset(r_means, r_means > r_min & r_means < r_max)
brks <- seq(min(r) - 0.02, max(r) + 0.02, by = 0.02)
hist(r,
breaks = brks,
col = "skyblue",
border = "black",
main = "Histogram of mean r",
xlab = "r",
xaxt = "n",
ylab = "count"
)
xlabels <- seq(min(round(r, 1)) - 0.1, max(round(r, 1)) + 0.1, by = 0.1)
axis(
side = 1,
at = xlabels,
labels = sprintf("%.1f", xlabels)
)
abline(v = result_rate$mean, col = "red", lwd = 1)
})This method is a refinement of the rate-based one. It uses the same
quantity \(a(m, m_{\mathrm{lim}})\),
but rescales it so that its variance no longer depends on the parameter
r. It is likewise far cheaper than estimation based on the
maximum likelihood principle.
Recall what was established in the previous section. The sample mean
of \(a\) estimates \(1/r\) without bias, exactly and for pooled
limiting magnitudes alike, which is a strong property. Its weakness is
the second moment: the variance of \(a\) is itself a function of \(r\). At a limiting magnitude of
6.0 it falls from about 0.066 at \(r = 1.4\) to 0.033 at \(r = 4\), a factor of two across the range
of interest. Two consequences follow. The precision of the estimate
cannot be judged without already knowing \(r\), and the delta method is needed to
convert \(\bar{a}\) into an estimate of
\(r\) at all.
The variance-stabilizing transformation removes exactly this
weakness. It maps \(a\) onto a scale on
which the variance is approximately 1.0 regardless of \(r\) — over \(1.4
\le r \le 4\) it stays within about 0.16 of that
value, comfortably covering the \(1.7 \le r
\le 3.3\) met in visual meteor work. Outside that window the
stabilization degrades on both sides, which bounds the use of the
transformed magnitudes as a response in linear models, but not the
reading of a single \(r\) off their
mean. vmgeom_vst_from_magn() performs the mapping and
vmgeom_vst_to_r() maps the mean of the transformed
magnitudes back onto \(r\).
The mapping is monotonically decreasing: a bright meteor is far from
the limiting magnitude and receives a large tm. Towards the
bright end it runs into a ceiling, since \(a\) cannot exceed 1 and the
transformed value is therefore bounded by what the transformation makes
of that limit. At the faint end it reaches zero half a magnitude below
the limiting magnitude, where a meteor is no longer perceived. Shown
here for a limiting magnitude of 6.0.
with(new.env(), {
lim_magn <- 6.0
m <- seq(-2, 5.5, 0.05)
plot(m, vmgeom_vst_from_magn(m, lim_magn),
type = "l",
col = "blue",
xlab = "m",
ylab = "tm"
)
})The way back needs no limiting magnitude at all, since the statistic it rests on does not depend on one. On logarithmic axes the relation is nearly a straight line, which is the additivity discussed further below.
with(new.env(), {
tm <- seq(1.5, 4.3, 0.01)
plot(tm, vmgeom_vst_to_r(tm),
type = "l",
col = "blue",
log = "xy",
xlab = "tm",
ylab = "r",
xaxt = "n",
yaxt = "n"
)
axis(1, at = c(1.5, 2, 3, 4), labels = c("1.5", "2", "3", "4"))
axis(2, at = c(1, 1.5, 2, 3, 4), labels = c("1", "1.5", "2", "3", "4"))
})The price is paid on the first moment. The transformation is non-linear in \(a\), and the mean of a non-linear function is not that function of the mean, so the exact unbiasedness of the rate-based estimator is lost. This deviation is systematic: it does not average out, and it does not shrink as the sample grows.
Its cause is a slight dependence of the transformed mean on the
limiting magnitude, which the statistic \(a\) itself does not have. Over \(1.7 \le r \le 3.3\), the range met in
visual meteor work, it amounts to at most 0.8% of \(r\).
What matters in practice is how that compares with the random error, and this depends on the number of meteors. The random error shrinks as the sample grows while the systematic one does not, so the two meet at a certain sample size: for a few hundred meteors the random error still dominates by a factor of five or more, at a few thousand by roughly two, and only beyond some ten thousand does the systematic part set the accuracy — no matter how many more meteors are added. Visual meteor data sets of that size are the exception, so for most analyses the deviation can be ignored.
Where it does matter — for a very large data set, or wherever an
unbiased estimate counts for more than a constant variance — the
rate-based method of the previous section remains exactly unbiased for
\(1/r\), and vmgeom_glm()
fits the exact likelihood.
Everything the rate-based method offers is retained. Since the
transformation is a monotone rescaling of \(a\), the pooling of bright meteors
described above carries over unchanged. And because \(a\) lies between 0 and
1, the transformed values are bounded as well, with the
upper bound corresponding to \(r = 1\);
the estimate can consequently never fall below the value the geometric
model requires.
The same monotonicity matters when the transformation is used inside
a predictive model. vmgeom_vst_to_r() is defined on the
whole range of values the transformation can produce and returns
NA only for values it cannot. Should sparse data drive a
prediction into an implausible region, the result is therefore a value
that is too large but still ordered, rather than a missing one — usually
far easier to handle downstream than an NA.
The delta method is applied below in this section as well, since the back-transformation is non-linear here too. What changes is the variance that enters it: a constant one instead of a value that depends on the very parameter being estimated.
Beyond stabilizing the variance, the transformation also puts the parameter on a scale with a simple structure. Classically, transformations of this kind are judged by three criteria, which Box and Cox (1964, An Analysis of Transformations, J. R. Stat. Soc. B 26, 211–252) treat together: constant variance, an approximately symmetric distribution, and additivity, meaning that effects combine linearly on the transformed scale. The third one is what the transformation is built around. It belongs to the Box-Cox family itself, applied to \(a\), and \(a\) estimates \(1/r\), so
\[ \log t \;=\; \mathrm{const.} - \beta \, \log r \]
with \(\beta > 0\): the relation
is close to linear on the logarithmic scale. A multiplicative change of
the population index therefore becomes an approximately additive shift
of \(\log t\). Over the range of \(r\) the calibration covers this is what the
mean of the transformed magnitudes follows, to the accuracy stated
above, and it is what the calibration of vmgeom_vst_to_r()
rests on. The departure from exact linearity is slight but systematic,
and the back-transformation accounts for it explicitly — see below.
This is what makes the transformed magnitudes usable as a response in
ordinary linear models, with residuals that are homoscedastic by
construction — precisely what such models assume.
vmgeom_vst_lm() fits them, and the estimation below is
already of that form: taking the mean of the transformed magnitudes
is an intercept-only linear model. A profile in which \(r\) varies with a covariate only extends
the right-hand side of the formula, so that
fits a solar-longitude dependence in the same way
vmgeom_glm() does above. The estimated coefficients
translate back into changes of \(\log
r\) up to the factor \(\beta\).
The back-transformation is \(\log r = c + d
\log t + e\,t\), so that factor is \(\beta = -1/(d + e\,t)\), which over the
range met in practice runs from about 0.73 to
0.81. The term in \(t\) is
what captures the curvature of the relation; a straight line through it
would leave a systematic error about 1.7 times as large,
and it is the reason vmgeom_vst_to_r() reproduces \(r = 1\) exactly at the upper bound of
tm.
That makes this approach a lightweight alternative to
vmgeom_glm(), and the choice between them is a trade-off.
In favour of the transformation is its cost: on the data set used here a
single estimate takes well under a millisecond, some two orders of
magnitude less than the generalized linear model, which iterates over
the full likelihood. Since the transformation is applied once per meteor
and everything afterwards is ordinary least squares, this advantage
grows with the size of the data set, and the whole toolbox built around
lm() becomes available.
Against it stands a loss of accuracy, though not where one would
expect it. The dispersion of the estimate is practically that of the
maximum likelihood estimate — in a simulation with n = 500
the two standard deviations agree to within a few percent over \(2 \le r \le 4\). What the transformation
costs is not variance but the systematic deviation of the
back-transformation described above, which adds to the total error and,
unlike the random part, does not shrink with the sample size. At
n = 500 and \(r = 3\) it
raises the root mean squared error by roughly 3% over that
of the maximum likelihood estimate; for larger samples its share grows.
vmgeom_glm(), fitting the exact likelihood, carries no such
deviation and additionally provides the inferential machinery of a
glm object — summary(), anova(),
information criteria. The transformation is therefore preferable for
quick estimates and exploratory work, the generalized linear model
whenever accuracy matters — including, unlike the usual trade-off, for
large data sets, where the systematic part comes to dominate.
A constant variance carries a few further consequences worth noting:
The resulting procedure is straightforward: all that is needed is the
mean of the transformed meteor magnitudes, from which an estimate of the
parameter r is obtained. vmgeom_vst_lm() does
both in one step. It applies the transformation and fits the result as
the intercept-only linear model announced above, so that the step to a
covariate is a matter of extending the formula. The response is passed
as cbind(magn, lim_magn) and the observed frequencies as
weights, exactly as for vmgeom_glm().
result_vs <- vmgeom_vst_lm(
cbind(magn, lim_magn) ~ 1,
data = magnitudes,
weights = magnitudes$Freq
)predict() converts the fitted mean back to the
r scale. It applies the delta method, which accounts for
the nonlinearity of the back-transformation, and returns the standard
error alongside the estimate.
# the model has no covariates, so any single row predicts the global r-value
newdata <- data.frame(row.names = "")
prediction <- predict(result_vs, newdata, se.fit = TRUE, bias_correction = TRUE)Two of the arguments deserve a comment. Passing the frequencies as
weights is the correct and complete way to supply
aggregated counts — nothing is lost by it, and there is no separate
mechanism one ought to be using instead. The coefficients it produces
are exactly those of a data set holding one row per meteor.
What differs is only how the residual variance is read back.
R has no notion of frequency weights in lm(): weights are
taken to express precision, so the residual variance is normalized by
the number of rows. summary() therefore reports a standard
error that refers to the 48 magnitude classes rather than to the 97
meteors behind them — here about 40% too large, with the
confidence interval correspondingly too wide. The same applies to
glm(family = gaussian()).
predict() takes care of this: it derives the residual
scale from the sum of the weights and thus reports the standard error of
the meteors. Nothing needs to be done differently when calling the
function — but the standard errors of summary(result_vs)
should not be used, since they carry the row-based normalization.
Expanding the rows into one meteor each would settle the question at the source, and for integer counts it gives exactly the same result as the correction applied here. Visual observations, however, may contain half meteors, which makes that route unavailable.
bias_correction = TRUE adds the second-order term of the
delta method. It is not the default — predict() methods in
R report a first-order standard error, and so does
vmgeom_glm() above — but it is what this section is about.
The term is small here, well below the random error at this sample size,
and it corrects the curvature of the back-transformation, not
the systematic deviation described further above. That deviation is a
property of the calibration and does not shrink as the sample grows.
Thus, one obtains the mean and the variance of the mean of
r.
result_vs <- list(mean = unname(prediction$fit), var = unname(prediction$se.fit)^2)
print(paste("r mean:", result_vs$mean))
#> [1] "r mean: 2.26269452053892"
print(paste("r var:", result_vs$var))
#> [1] "r var: 0.0103330446553896"Using the bootstrap method, it can be assessed whether the mean is normally distributed.
# Bootstrapping Method
r_means <- with(magnitudes, {
N <- sum(Freq)
tm <- vmgeom_vst_from_magn(magn, lim_magn)
replicate(50000, {
s <- sample(tm, size = N, replace = TRUE, prob = Freq)
vmgeom_vst_to_r(mean(s))
})
})The graphical representation indicates that this is indeed approximately the case.
with(new.env(), {
r_sd <- sqrt(result_vs$var)
r_min <- as.vector(result_vs$mean - 3 * r_sd)
r_max <- as.vector(result_vs$mean + 3 * r_sd)
r <- subset(r_means, r_means > r_min & r_means < r_max)
brks <- seq(min(r) - 0.02, max(r) + 0.02, by = 0.02)
hist(r,
breaks = brks,
col = "skyblue",
border = "black",
main = "Histogram of mean r",
xlab = "r",
xaxt = "n",
ylab = "count"
)
xlabels <- seq(min(round(r, 1)) - 0.1, max(round(r, 1)) + 0.1, by = 0.1)
axis(
side = 1,
at = xlabels,
labels = sprintf("%.1f", xlabels)
)
abline(v = result_vs$mean, col = "red", lwd = 1)
})So far, we have operated under the assumption that the real
distribution of meteor magnitudes is exponential and that the perception
probabilities are accurate. We now use the Chi-Square goodness-of-fit
test to check whether the observed frequencies match the expected
frequencies. Then, using the estimated r-value, we retrieve the relative
frequencies p for each observation and add them to the data
frame magnitudes:
We must also consider the probabilities for the magnitude class with the brightest meteors.
The smallest magnitude class magn_min is -6. In
calculating the probabilities, we assume that the magnitude class -6
contains meteors that are either brighter or equally bright as -6 and
thus use the function pvmgeom() to determine their
probability.
idx <- magnitudes$magn == magn_min
magnitudes$p[idx] <- with(
magnitudes[idx, ],
pvmgeom(m = magn + 1L, lm = lim_magn, result_rate$mean, lower.tail = TRUE)
)This ensures that the probability of observing a meteor of any given magnitude is 100%. This is known as the normalization condition. Accordingly, the Chi-Square goodness-of-fit test will fail if this condition is not met.
We now create the contingency table magnitutes_observed
for the observed meteor magnitudes and its margin table.
magnitutes_observed <- xtabs(Freq ~ magn_id + magn, data = magnitudes)
magnitutes_observed_mt <- margin.table(magnitutes_observed, margin = 2)
print(magnitutes_observed_mt)
#> magn
#> -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6
#> 0.0 0.0 0.0 0.0 3.0 4.0 7.0 10.0 23.0 26.5 20.0 3.0 0.5Next, we check which magnitude classes need to be aggregated so that each contains at least 10 meteors, allowing us to perform a Chi-Square goodness-of-fit test.
The last output shows that meteors of magnitude class 0
or brighter must be combined into a magnitude class 0-.
Meteors with a brightness less than 4 are grouped here in
the magnitude class 4+, and a new contingency table
magnitudes.observed is created:
magnitudes$magn[magnitudes$magn <= 0] <- "0-"
magnitudes$magn[magnitudes$magn >= 4] <- "4+"
magnitutes_observed <- xtabs(Freq ~ magn_id + magn, data = magnitudes)
print(margin.table(magnitutes_observed, margin = 2))
#> magn
#> 0- 1 2 3 4+
#> 14.0 10.0 23.0 26.5 23.5We now need the corresponding expected relative frequencies
magnitutes_expected <- xtabs(p ~ magn_id + magn, data = magnitudes)
magnitutes_row_freq <- margin.table(magnitutes_observed, margin = 1)
magnitutes_expected <- sweep(magnitutes_expected, 1, magnitutes_row_freq, `*`)
magnitutes_expected <- magnitutes_expected / sum(magnitutes_expected)
print(sum(magnitudes$Freq) * margin.table(magnitutes_expected, margin = 2))
#> magn
#> 0- 1 2 3 4+
#> 15.68943 14.26433 19.69970 21.01861 26.32794and then carry out the Chi-Square goodness-of-fit test:
chisq_test_result <- chisq.test(
x = margin.table(magnitutes_observed, margin = 2),
p = margin.table(magnitutes_expected, margin = 2)
)As a result, we obtain the p-value:
chi2_df <- chisq_test_result$parameter - 1
chi2_pval <- pchisq(chisq_test_result$statistic, df = chi2_df, lower.tail = FALSE)
print(chi2_pval)
#> X-squared
#> 0.2906011If we set the level of significance at 5 percent, then it is clear that the p-value with 0.2906011 is greater than 0.05. Thus, under the assumption that the magnitude distribution follows an geometric meteor magnitude distribution and assuming that the perception probabilities are correct (i.e., error-free or precisely known), the assumptions cannot be rejected. However, the converse is not true; the assumptions may not necessarily be correct. The total count of meteors here is too small for such a conclusion.
To verify the p-value, we also graphically represent the Pearson residuals:
chisq_test_residuals <- with(new.env(), {
chisq_test_residuals <- residuals(chisq_test_result)
v <- as.vector(chisq_test_residuals)
names(v) <- names(chisq_test_residuals)
v
})
plot(
chisq_test_residuals,
main = "Residuals of the chi-square goodness-of-fit test",
xlab = "m",
ylab = "Residuals",
ylim = c(-3, 3),
xaxt = "n"
)
abline(h = 0.0, lwd = 2)
axis(1, at = seq_along(chisq_test_residuals), labels = names(chisq_test_residuals))