Sum the total on the axis it is plotted against - #540
Conversation
A total can only be formed once every line sits on the same coordinate. On a weight axis they do: every species shares the model's weight grid. On a length axis they do not, because each species — and, since the previous commit, the resource — converts weight to length with its own allometric relationship, so at a given length the lines sit at different weights. That is why the `Total` line was dropped from length-based plots. It is now formed after the conversion, at equal length rather than at equal weight, by `add_total_line()`: each series is interpolated onto the sorted union of all the size coordinates, linearly in log size, and contributes nothing outside its own range. Where the series already share a grid — always on a weight axis, and on a length axis whenever the weight-length parameters agree — the union is that grid and the interpolation reproduces the values exactly, so nothing is approximated in the cases where nothing needs to be. The weight-axis total is unchanged, verified against the previous implementation for powers 0, 1 and 2. This moves the total out of `prepare_ArraySpeciesBySize_plot_data()` and past the conversion in every path that draws one: the array plot, `addPlot()`, `plot2()` and `plotRelative()` via the two data-frame helpers, `plotSpectra()`, and both `animate()` methods. Two corrections come with it, both on the weight axis: - `plotSpectra(total = TRUE, resource = FALSE)` now leaves the resource out of the total. The argument used to control only whether the resource was drawn, so a consumers-only total was unavailable. - The total of a mizer array is now the sum of the lines drawn rather than of the whole array, so it no longer counts abundance outside a species' size range. Use `all.sizes = TRUE` for the old sum. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`total = TRUE` meant two different things. `plotSpectra()` summed the resource and every species, whatever was drawn and whichever species were selected. The array plots summed only the species selected for display, and only the sizes inside each species' own range. The first is the useful meaning: the total is a property of the model, not of the plot, so that a plot of two species can be read against the community total. Both now work that way. - `plotSpectra()` and `animate()` on a `MizerSim` keep counting the resource whether or not it is drawn. The previous commit had made the `resource` argument exclude it from the total as well as from the plot; that is reverted. - `plot()`, `addPlot()`, `plot2()`, `plotRelative()` and `animate()` on an array now sum the whole array. The total no longer moves when `species`, `all.sizes` or `background` change. The contributors are assembled by `total_contributors()`, which is just the usual preparation of the whole array, and summed after the size conversion by `append_total_line()`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Third commit (018dd99): To answer the question that prompted this: The array plots were the ones genuinely out of step, since Both now follow one rule — the total is the total of everything the object holds:
The contributors are assembled by The two "corrections" I listed in the PR description are therefore gone: the Suite: 4317 passing, 0 failures. |
`plotSpectra2()` and `plotSpectraRelative()` assembled the two spectra on the weight axis and converted afterwards, in the data frame helper. The total they had been handed was therefore already summed at equal weight, and reached the conversion with no species to convert it by, so it was silently dropped: a length-axis comparison lost its total. They now ask `plotSpectra()` for the axis they want, so the frames they receive are already converted and the total in them is the total on the axis being plotted, formed the same way as in a single spectrum plot. The data frame helpers are then called without `size_axis`, so nothing is converted twice. The `data_ylim` treatment is unchanged: on a length axis the inner call still gets `c(0, NA)`, because the user's limits apply to the converted values and are imposed at the end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Fourth commit (ba8cb0a): Both assembled the two spectra on the weight axis and converted afterwards, inside the data-frame helper. So the total they had been handed was already summed at equal weight, and reached the conversion with no species to convert it by — it was silently dropped, and a length-axis comparison lost its total. The fix turned out to need no new plumbing: they now ask Verified: for every combination of axis ( The Suite: 4333 passing, 0 failures. |
`plotSpectra()` applies `ylim` twice over: as the axis limits and as a filter on the data, the latter carrying a hard floor at 1e-20 that keeps a spectrum's numerically-zero tail from stretching a log axis. `plotSpectra2()` could not do the same on a length axis. It assembled the two spectra on the weight axis, so the values it would have filtered were a Jacobian away from the ones the user's limits described. It therefore passed `c(0, NA)` to the inner calls instead, disabling the upper cut and lowering the floor to zero, and left the limits to the scale alone. The previous commit moved the conversion into those inner calls, which removes the reason: the filter now sees exactly the values the limits describe. So `ylim` is handed straight over, and the two functions agree on both axes — with `ylim = c(median, NA)` on a length axis they now return the same 512 rows, where the comparison used to return 1024 of which half were below the limit. The plot itself is unchanged, since the axis limits hid those points either way. What changes is `return_data = TRUE`, and a series that dips below the lower limit mid-range now breaks into segments rather than running off the bottom — which is already how `plotSpectra()` draws it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Fifth commit (1fed987): the
The previous commit removed the reason for it: the inner call now converts to the requested axis, so the filter sees exactly the values the limits describe. The inconsistency this removes, with
The drawn plot is unchanged — the axis limits hid those points either way. What changes is The test from Suite: 4334 passing, 0 failures. |
Stacked on #539, which is stacked on #538 — this PR's diff is against
resource-length-params.Summary
A total can only be formed once every line sits on the same coordinate. On a weight axis they do: every species shares the model's weight grid. On a length axis they do not — each species, and now the resource, converts weight to length with its own allometric relationship, so at a given length the lines sit at different weights and their grids no longer coincide. That is why the
Totalline was dropped from length-based plots.It is now formed after the conversion, at equal length rather than at equal weight, which is the quantity that means something there: the number of organisms per unit length, whatever they are.
How the sum is formed
add_total_line()interpolates each series onto the sorted union of all the size coordinates, linearly in the logarithm of size since the grid is logarithmic, with a series contributing nothing outside its own range.The important property: where the series already share a grid, the union is that grid and interpolation reproduces the values exactly. That is always the case on a weight axis, and on a length axis whenever the weight-length parameters agree — so nothing is approximated in the cases where nothing needs to be. Interpolation only does real work when species genuinely differ in
aorb, and there it is the only correct answer.The weight-axis total is verified unchanged against the previous implementation for powers 0, 1 and 2.
This moves the total out of
prepare_ArraySpeciesBySize_plot_data()and past the conversion in every path that draws one: the arrayplot(),addPlot(),plot2()andplotRelative()via the two data-frame helpers,plotSpectra(), and bothanimate()methods.plotRelativeDataFrame()now converts before joining rather than after, so a total exists to join on; the relative difference itself is unaffected, since a common Jacobian cancels.Two corrections that come with it
Both on the weight axis, both recorded in
NEWS.mdand the upgrade skill with symptom-index rows:plotSpectra(total = TRUE, resource = FALSE)now leaves the resource out of the total. Theresourceargument used to control only whether the resource line was drawn — the total counted it either way, so a consumers-only total was not available. This is the fix agreed when we chose to keep the resource in the length-axis total: it gives a way out for anyone who does not want the two length conventions mixed.plot(<array>, total = TRUE)now sums the lines that are drawn rather than the whole array, so it no longer counts abundance outside a species' size range. On the North Sea model this moves the total at about 2% of sizes, by up to a factor of three where a species holds abundance beyond itsw_max— the old total sat visibly above the sum of the lines there.all.sizes = TRUEreproduces the old sum exactly.Testing
Full suite: 4313 passing, 0 failures. New tests cover
add_total_line()on a shared grid (exact), on grids that differ (checked against hand-computedapprox()values at each union point), and with a grouping column; that the spectrum total is unchanged on a weight axis for all three powers; that it appears on a length axis and equals the sum of the lines drawn there; that a length-axis total on species with differinga/bneeds more points than the weight grid and reduces to the single surviving species at the largest length; and both weight-axis corrections above.🤖 Generated with Claude Code