Let mizer arrays say what kind of value they hold - #538
Conversation
Mizer arrays now carry a `type` attribute — `"value"` (the default), `"density"` or `"proportion"` — replacing the `density_wrt` attribute added earlier in this development cycle. `density_wrt` could name any of four density measures, but three of them were dead as an array attribute: every mizer array is indexed by the model's weight grid, so a stored spectrum is per-gram by construction, and per-log-size quantities are computed on the fly inside plot data frames rather than stored. Carrying four values where two occur repeated the mistake the attribute was introduced to fix. Meanwhile a distinction that does earn its keep was handled ad hoc: `plotFeedingLevel()` hardcoded a [0, 1] clamp while `plot(getFeedingLevel(params))` — the array method for the same numbers — had none, and five other arrays are proportions that no plot knew about. - A `"density"` is multiplied by the appropriate Jacobian on a length axis and has its units restated from `1/g` to `1/cm`, as before. The density-measure vocabulary stays as internal plumbing, because `plotSpectra(per_log_size = TRUE, size_axis = "l")` still needs the `log_w` measure. - A `"proportion"` is plotted on a linear y axis covering the whole of [0, 1]. The range is only ever widened to include the data, never narrowed to that interval: the critical feeding level and the resource level can both legitimately exceed 1. - Arrays that declare no type still fall back to the old test on `value_name` and `units`, so existing code and saved objects are unaffected. Fixes `plotFeedingLevel(include_critical = TRUE)` drawing a critical feeding level above 1 off the top of the plot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`size_axis` and `per_log_size` are the same operation — both change the measure a density is expressed with respect to, and both are computed by `density_measure_jacobian()` — but only the first was available on an array. `plotSpectra()` owned the other, and takes a `MizerParams` or `MizerSim` rather than an array, so an arbitrary density array could not be viewed per log size at all. Worse, `per_log_size = TRUE` was swallowed by `...` and returned the per-size plot with no warning. `plot()`, `plot2()`, `addPlot()` and `animate()` now take `per_log_size` for any array whose type is `"density"`. Unlike `size_axis` it needs no weight-length relationship, so the resource classes take it too, and rows with no species — the "Total" row — survive the conversion on a weight axis. Asking for it on an array that holds anything else is an argument error rather than being ignored. `biomass` deliberately stays with `plotSpectra()`: multiplying by w to count grams instead of individuals changes the quantity rather than the measure, and only means anything for a number density. Also fixes `convert_density_units()` for the newly reachable conversion to a logarithmic measure, which has to remove the per-size factor rather than swap it: `1/g` becomes dimensionless and `g^-1/year` becomes `1/year`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Added a second commit:
plot(initialN(params), per_log_size = TRUE) # x w
plot(initialN(params), size_axis = "l", per_log_size = TRUE) # x b*w
plot(initialNResource(params), per_log_size = TRUE) # the resource tooThree things worth noting:
Asking for This also fixed a latent bug the feature made reachable: Suite is at 4266 passing, 0 failures. No new lints. |
Summary
Mizer arrays now carry a
typeattribute saying what kind of quantity they hold —"value"(the default),"density"or"proportion"— replacing thedensity_wrtattribute added earlier in this development cycle (unreleased, so nothing external depended on it).Why the change.
density_wrtcould name any of four density measures ("w","log_w","l","log_l"), but three were dead as an array attribute. Every mizer array is indexed by the model's weight grid, so a stored spectrum is per-gram by construction; per-log-size quantities are computed on the fly inside plot data frames (plotSpectra()multiplies byw^power) and never stored. Carrying four values where two occur repeated the mistake the attribute was introduced to fix — the oldunits == "1/g"branch that never changed an outcome.Meanwhile a distinction that does earn its keep was being handled ad hoc:
plotFeedingLevel()hardcoded a[0, 1]clamp whileplot(getFeedingLevel(params))— the array method for the same numbers — had none, and five other proportion-valued arrays were unknown to every plot.What each type does
type"value""density"1/g→1/cm"proportion"[0, 1]The density-measure vocabulary stays as internal plumbing:
plotSpectra(per_log_size = TRUE, size_axis = "l")genuinely needs thelog_wmeasure and itsbJacobian, so only the array-facing attribute narrowed.array_density_wrt()survives as the one-line bridge between the two.A
"proportion"range is only ever widened to include the data, never narrowed to[0, 1]—getCriticalFeedingLevel()andresource_level()can legitimately exceed 1. An explicitylimalways wins, and a logarithmic axis is left alone.Arrays that declare no type still fall back to the old test on
value_name/units, so existing code, extension packages and saved objects are unaffected.Behaviour changes for users
plot()of a feeding level, critical feeding level,maturity(),repro_prop(),psi()orresource_level()now shows the full[0, 1]y range — the rangeplotFeedingLevel()has always used, so the dedicated function and the array plot finally agree.plot(resource_level(params))gets a linear y axis instead of a logarithmic one. Passlog_y = TRUEfor the old axis.plotFeedingLevel(include_critical = TRUE)no longer draws a critical feeding level above 1 off the top of the plot.plotComparisonDataFrame()and the internalanimate_plotly()take a singledensity_wrtargument in place ofspectrum_power/spectrum_per_log_size, and the internalarray_spectrum_power()is gone. Thepowerinterface ofplotSpectra()and friends is unchanged.All of this is recorded in
NEWS.mdand ininst/skills/upgrade-mizer-code/SKILL.md, with four new rows in its symptom index.Testing
devtools::test(): 4232 passing, 0 failures. New tests cover the type attribute and its legacy inference, subsetting and slicing across all five array classes,proportion_ylim()(widening, explicit limits, log axes, non-finite values), values above 1 staying visible, and theplotFeedingLevel()fix. Two snapshot files were re-accepted for the attribute rename only. No new lints on any changed line.Note on scope
Two things in this branch predate the work and came along with it:
inst/skills/analyse-and-plot/SKILL.mdandquick-reference.mdcarry an in-progress restructuring of the plotting cheatsheet that was already in the working tree; mytypeand proportion sections sit on top of it, andvignettes/cheatsheet-analysis-and-plotting.Rmdis the regenerated result.man/getMeanWeight.Rdwas stale against its committed roxygen source and got resynced bydevtools::document().The
docs/website build in the working tree is deliberately not included.🤖 Generated with Claude Code