sensmodel() gains a template argument,
selecting the visual pigment template used to generate sensitivity
curves. It names both the author and the chromophore, since a template’s
shape depends on both, and defaults to "govardovskii_a1",
which is what every previous version of the function used.
"govardovskii_a2" adds the vitamin A2 template from the
same paper, and "ssh_a1" and "ssh_a2" the two
from Stavenga, Smits and Hoenders (1993). The A2 templates cover
porphyropsin pigments, found in freshwater fish, amphibians, and species
that switch chromophore seasonally, which pavo previously could not
model at all: an A2 band is close to 20% broader than the A1 band at the
same peak sensitivity, so it is not recovered by adjusting
peaksens. "govardovskii_a2" is the one to
reach for, since pairing it with the default compares chromophores
without also changing template family. The template used is recorded as
a "template" attribute on the result. Note that
beta = FALSE is recommended with either SSH template: their
beta band has a fixed peak wavelength rather than one that scales with
peaksens, so for short-wavelength pigments it pulls the
maximum of the summed curve several nm away from the value asked for,
and sensmodel() warns where that shift exceeds 5 nm. The
alpha band on its own is unaffected, both Govardovskii beta bands scale
with peaksens and do not have this problem, and Stavenga
(2010) notes the fixed beta peak to be a known shortcoming.
bootcoldist() gains a ci.type argument,
allowing bias-corrected and accelerated (BCa) confidence limits as an
alternative to the percentile limits it has always returned. Colour
distances are bounded below by zero and typically right-skewed, so
percentile limits tend to sit off-centre; BCa corrects for that skew and
for where the empirical distance falls in the bootstrap distribution.
The acceleration term comes from a jackknife leaving out one row at a
time, or one cluster where cluster is supplied. The default
remains "perc".
bootcoldist() gains a cluster argument
for hierarchically structured data, such as several patches per
individual, or repeated measurements of one individual. Bootstrap
replicates then resample whole clusters rather than rows, so
non-independent observations stay together and the effective sample size
is the number of clusters. The nesting argument controls
whether clusters span the levels of by
("crossed", one draw shared across groups, preserving their
pairing) or sit within one level ("nested", drawn
independently per group), and defaults to detecting which applies.
Empirical means are unaffected, and results are unchanged when
cluster is NULL.
bootcoldist() gains a correct argument,
which removes the upward bias in the distance between group means that
comes of those means being estimated rather than known. Distance is
convex in the error of a mean, so two noisy centroids sit further apart
than the true ones, and two samples of one population are separated by
an apparently non-zero distance. On the squared scale the displacement
is the sum over groups of the mean squared pairwise distance among a
group’s observations divided by twice their number, so it is worst for
small, variable groups and does not fall away as the true separation
approaches zero. It is therefore largest exactly where discriminability
is in question. correct = TRUE subtracts it from the
empirical distance and from every bootstrap replicate, each using the
observations it drew.
Where cluster is supplied the correction works on
clusters rather than rows, matching what is resampled; from rows it can
understate the displacement several-fold. Where the design is crossed,
the two group means are correlated and the correction uses each
individual’s own paired difference, which carries that covariance;
treating the groups as independent would subtract several times too
much. Partly crossed designs are refused, as is
nesting = "nested" where clusters in fact span groups.
The subtraction is exactly unbiased on the squared scale. Its square
root errs slightly low, so a corrected distance is a little
conservative. Exactness also assumes clusters of roughly equal size;
under marked imbalance the between-cluster part is under-corrected and
the within-cluster part over-corrected, so the net direction depends on
which dominates. The correction is unavailable under quantum noise, in
the CIELAB, CIELCh and coc spaces, for achromatic contrast in a
colourspace model, and with ci.type = "bca". The default is
FALSE, so results are unchanged unless it is asked
for.
procspec() gains a new "clip" option,
controlled by the new clip_range argument to remove entire
regions of the spectra and replace them by linear interpolation. Code
contributed for by @rscherrer, addressing issue #271.
the rimg2cimg() function has been removed in favour
of a custom as.cimg() method.
bootcoldist() with correct = TRUE now
returns the signed corrected square as the "dS.sq"
attribute of the result. Flooring at zero is a presentation choice,
since a negative squared distance cannot be reported as a distance, but
the signed value is the quantity the correction is unbiased for and the
one to carry into a further calculation.bootcoldist() now validates achromatic,
erroring where it is not a single TRUE or
FALSE.bootcoldist() now says how many bootstrap replicates
failed and why, where it previously discarded the error and reported
only that “Bootstrap sampling encountered errors”. A run in which every
replicate failed used to produce an unrelated error about a comparison
of length zero, and is now reported like any other failure.bootcoldist() now fails with an informative error when
boot.n is too small for the requested alpha.
Previously the lower quantile index rounded down to zero, and the
interval failed to assemble with an unrelated message about mismatched
dimnames. With the default alpha this affected any
boot.n below
20.explorespec() and
sensmodel() are now explicit in the function
definition.as.rspec() now works out of the box with
tibble, rather than requiring users to pass a standard
data.frame.colspace() internal functions
(dispace(), trispace(),
hexagon(), etc.) have been refactored (#263), with two
minor user-facing consequences:
tcspace() have been converted to
warnings for consistency with the other internal colspace functionscoldist() with noise = "quantum" built the
photon-noise term from log-transformed quantum catches whenever
qcatch = "Qi", rather than from the catches themselves. The
term is 2 / (Qa + Qb) and was being computed as
2 / (log Qa + log Qb), which inflates the noise, deflates
the resulting distances, and leaves the quantum model short of the
neural one however bright the illuminant. Results were correct for
qcatch = "fi" input throughout. If you have run
coldist() or bootcoldist() with
noise = "quantum" and qcatch = "Qi" on any
version from 2.4.0 onward, those distances need recalculating. Reported
by Changku Kang, issue #281.coldist(). They describe a dim, photon-limited
stimulus, and give small distances rather than negative log catches.
Non-positive catches remain an error, since the noise term is undefined
for them.coldist() with noise = "quantum" and
achromatic = TRUE returned NA for the
achromatic distances to the reference stimuli that anchor
jnd2xyz(). Those were then read as zeros, putting the
achromatic reference on top of the data. Luminance coordinates from
jnd2xyz() change for quantum-noise models; under the
default center = TRUE only the stored reference coordinates
move.sensmodel() now generates the same sensitivity curve
for a given peaksens whatever range is
requested. The alpha-band expression of Govardovskii et al.
(2000) contains a constant of 300 nm, which was coded as
range[1]. Since the two coincide at the default
range = c(300, 700), curves were correct for that range and
for any range sharing its lower bound. Users who called
sensmodel() with a lower bound other than 300 nm should
consider regenerating their sensitivities and rerun any downstream
vismodel() or coldist() results. Everyone else
is unaffected and results are unchanged.jndrot() and by extension jnd2xyz() have
been adjusted for trichromats to only allow rotations in the 2D plane.
Until now, 3D rotations were allowed and the result was projected back
in 2D but this meant that the output was no longer representing JNDs
distances. Because rotate = TRUE is the default in
jnd2xyz(), we recommend you re-run any
jnd2xyz() computation on trichromats. From our tests,
results stay qualitatively similar but specific values may change.bootcoldist() now summarises colspace()
objects by the arithmetic mean of their coordinates, rather than a
geometric mean of coordinates shifted by an arbitrary constant of 100.
Distances in a colour space are measured in the coordinates themselves,
so a group’s centroid is their arithmetic mean. Quantum catches and
luminance channels stay geometric, since those distances are linear in
the logged values. Colspace distances change slightly. CIELAB, CIELCh
and segment, whose coordinates can be negative, previously returned NaN
and now work.bootcoldist() now summarises qcatch = "fi"
models by an arithmetic mean of the log catches rather than a geometric
one, so reported distances for such models change. fi
values are already log(Qi), and their geometric mean is not
a centroid: "Qi" and "fi" models of the same
data disagreed by around 6% on the bundled sicalis data,
and the distance depended on the factor the illuminant was scaled by,
though scaling shifts every log catch equally and cannot change a
chromatic distance. Calls that returned NaN because an unscaled
illuminant made log catches negative now work.bootcoldist() now takes qcatch from a
vismodel or colspace object’s attribute, as
coldist() always has, rather than letting an argument
override it. Results change only where the two disagreed, in which case
the old behaviour was wrong.bootcoldist() now errors where any bootstrapped
distance is NA, rather than silently taking confidence
limits from the wrong order statistics. The usual cause is
achromatic = TRUE on a model built without an achromatic
channel.bootcoldist(raw = TRUE) now returns bootstrap distances
in replicate order. Each contrast was previously sorted independently
before being returned, so values sharing a row came from different
resamplings, and neither different contrasts nor dS and dL could be
compared with one another. The values themselves are unchanged, only
their order.avg.uv and avg.v visual
systems used in sensdata() and vismodel() have
been updated to match exactly the ones provided in the original source
(Endler & Mielke 2005). This may result in minute changes in the
values returned by vismodel() if you are using these
parameters.viridisLite package has been removed
in favour of recent base R functionssimulate_spec(), which allows for
the flexible simulation of naturalistic spectra (inc. reflectance,
irradiance, radiance, absorbance). See ?simulate_spec for
examples and information, and the handbook for further discussion.plot.rspec() now accepts a logical labels
argument (and labels.cex), to control whether text labels
identifying each spectrum should be added to the outer plot margins.
This was previously only available, and was required, for ‘stacked’ plot
types, but is now optional for both ‘overlay’ (the default) and
‘stacked’ spectral plots.wlmin and wlmax arguments in
summary.rspec() are being deprecated in favour of a single
lim argument, for consistency across functions.summary.rspec() has been rewritten for efficiency, and
now only calculates the required variables when subset is
used. As a result, the function is also slightly slower (0.5 x) when
calculating the full set of variables, but much faster (10 x) when
calculating a subset.margins argument from
various colourspace plots.rgl.triangles with triangles3d()
internally to avoid a deprecation issue.x and/or y is
equal to 0.aggplot() (as specified by
lwd) is now consistent with line width on the plot
(#235)procspec(spec, opt = "smooth") now works correctly with
uninterpolated spectra. Previously, it was causing a shift of the
spectra towards long-wavelengths (#234).coldist() now returns Euclidean distances when given
data from the ciexyz model.as.rspec() now preserves numeric column names when
converting from a tibble.procimg(), with details and examples available in the
function’s help documentation and the vignette.raw to bootcoldist().
When TRUE, the full raw list of bootstrapped
colour-distances are returned (equal in length to the number of
replicates specified by boot.n), rather than the summary
statistics which are returned by default.?vismodel() or
?sensdata() for instructions on selecting it.coldist() now uses the package farver when
calculating CIE2000 colour distances between points in the CIELab and
CIELch spaces.plot.colspace(gamut = TRUE) (as well as
triplot(), tcsplot(),
tetraplot()) where it would incorrectly display
"Max gamut cannot be plotted." and fail to plot the max
gamut each time.noise = 'quantum'. Taking the
root of negative values following log-transformation, as required when
noise = 'quantum', would previously produce an
uninformative error, which has been rectified.margins argument has been deprecated for cie, coc,
hexagon, segment, projplot, di-, tri-, and tetra-chromatic plots. It was
causing problems when later adding points to plots, and margin-handling
is best left to the user anyway. The default plots may therefore look
slightly different, but the margins can be adjusted to taste using the
standard par(mar = c()) pathway.bootcoldist() would not run until
supplied with receptor-noise model arguments (n, weber, weber.ref), even
when not running the receptor-noise model.bootcoldist() would sometimes fail
when given data from colourspace models containing lots of negative
coordinate values. This was particularly common for the hexagon
model.adjacent().options() and par() are now always set
locally, including in vignettes and examples, as to prevent spillover of
these changes in the user sessionbootcoldist() and adjacent() now use a
random number generator that generates statistically sound values, even
when ran in parallel. The output of these functions is thus expected to
slightly change, even if you set the seed before.Suggests,
which means it will not be installed automatically when you install pavo
from CRAN and that you will need to install it yourself if you need it
(for vol(type = "alpha"), vol(type = "alpha")
and voloverlap(type = "alpha"))vol(), tcsvol()
and voloverlap() gain a new argument
type = c("convex", "alpha") to decide how you want to
compute the colour volume. Please refer to the vignette
vignette("pavo-5-alphashapes", package = "pavo") for
more information. As a result, the argument order in these 3 function
has changed. Check the documentation to update your scripts accordingly.
The function summary.colspace() also gains an additional
column that returns that colour volume computed with an alpha-shape of
parameter alpha* in the case of tcs objects.getimg() now imports image files with uppercase
extensions (e.g., JPG or PNG), such as those produced by some camera
brands or processing software.data.maxqcatches
attribute) now works for segment “visual model” as well. As a side
effect, this removes a warning that occurred when users ran
vismodel(..., visual = "segment").sensmodel() now accepts the argument
sensnames, for specifying the names of the resulting
sensitivity curves on-the-fly.vismodel(), by allowing users to specify the illuminant and
viewer sensitivity function used when estimating XYZ values (via
illum and visual arguments in
colspace()).bootcoldist() is now laxer in its argument checks and
accept objects that are neither vismodel or
colspace objects. This means you can now use this function
on quantum catches dataframe that you obtained outside of pavo, such as
the MICA toolbox.summary.colspace() now prints a more explicit error
when the by argument value is not a multiple of the number
of rows in the colspace object (i.e., the number of spectra)teal example dataset columns have been renamed to add
an additional zero in front of single digit numbers, so that column
names now sort in the correct order by default.as.rspec() is now more lenient for wavelength trimming
when interp = FALSE and now works even if the specified
lims do not correspond to actual wl values from the input
object.summary.rspec().plot() functions for colspace
(triplot(), tcsplot(),
tetraplot()) gain a new gamut argument to plot
the maximum gamut for a given visual system and illuminant.
summary.colspace() also now returns the maximum colour
volume for a given visual system and illuminant that you can use to
compare to the realised volume by a given dataset. More information in
PR #180.future package,
which offers windows and high performance computing (HPC) environments
support. The progress bar is produced by the progressr
package and can be customised as well. As a consequence, the
cores argument in getspec(),
adjacent() and classify() has been
deprecated.projplot() twice for the background grid to
be displayed.summary.colspace() where
NULL was returned instead of
summary.data.frame() for non-tcs colourspaces.bootcooldist()pavo.colrverse.comcoldist()that prevented the calculation
of achromatic contrast when using custom quantum catch dataplot.rspec() now adds a linear spectrum alongside the
x-axis to show the hues corresponding to each wavelength (controlled by
the wl.guide boolean argument).cieplot() (and therefore plot.colspace()
for CIEXYZ model) now adds the background of the CIEXYZ colour space by
default. This can be turned off by switching the ciebg
argument to FALSE.voloverlap() uses a different algorithm to determine
volume overlaps, which means:
voloverlap() now works for trispace()
objects as wellvoloverlap() has
been renamed colsp1 and colsp2 instead of
tcsres1 and tcsres2montecarlo option has been
deprecatedvismodel() now have
more explicit messages.pavo functions (excepted vismodel()
and spec2rgb()) now work with sub-nm precision, for
rspec objects with non-integer wavelengths.summary.colspace() no longer fails for tcs objects with
only one row.vismodel() now works for monochromats as well.procspec(opt = "bin") no longer counts bin edges twice
(once in each consecutive bin). This will cause changes in the bin stops
compared to earlier versions of this function.coc() and categorical() spaces now return
Weber luminance contrast by default when passed through
coldist(), rather than nothing (as per the original
publications).coldist() can now take multiple values for
weber, when Weber fractions are known for all receptor
classessummary.rspec() no longer errors for a single spectrum
when the wavelength range does not contain 450-700nmclassify()coldist() now returns unweighted Euclidean distances
for generic di-, tri-, and tetra-chromatic colspace()
models. Users wishing to estimate noise-weighted distances (i.e. via the
receptor-noise limited model) should continue to use
coldist() directly on vismodel() objects, as
previously.coldist()vismodel(), colspace(), and
coldist() now always return lum and
dL columns, for consistencyirrad2flux() and flux2irrad() use slightly
more precise constant values, which may lead to slightly different
results (less than 0.1% difference between this new version and the
previous versions)getimg() can now import images from URL’scores argument in getimg() is
deprecated, as image import is vectorisedclassify() when using
refID and kcols for multiple imagesresize argument in procimg() now takes
a percentage, rather than scaling factorcolspace(space = segment) would
return both a B and (redundant) lum
columnvismodel()getspec() where badly encoded
characters in some spectral files would cause failurecoldist() would attempt to estimate
receptor-noise weighted distances rather than cie-distances for
cielch model resultsreclass to procimg(),
which allows users to interactively correct areas within images that
have been misclassifiedpeakshape() uses a completely different algorithm to
find the FWHM. It now works as expected for spectra with multiple peaks.
See PR #137 for a
detailed overview of the changes.bgandilum,
transmissiondata, ttvertex,
vissyst) is no longer exposed to usersis.vismodel() and
is.colspace() are exported to test whether an object is of
class vismodel or colspace, respectivelyas.rimg()pfowl and avg.v in
vismodel() and sensdata()). This removes an
unnecessary but harmless warning when colspace() was used
to place quantum catches of such species in the tetrahedral colour
space.achro argument in coldist() has been
changed for achromatic to better match the arguments from
vismodel(). Older scripts that use achro
should not be affected and still work as before.imager is no longer a dependency, and is
only loaded if using some features of procimg().mapproj is no longer a dependency, and is
only loaded if using projplot().labels.stack to
plot.rspec, which allows the use of custom spectra labels
in stacked plots.as.rspec, and can control the behaviour with
the new argument exceed.limits.as.rspec() now accepts both numeric and character
vectors to identify the wavelength column using whichwl
(eg. whichwl = "wl").classify() can now be specified
using either a numeric vector (to identify by image position in a list)
or character vector (to identify by image name).aggspec() when wavelength column was
previously removed by the user.cocplot() would failed whenever
type graphical parameter was specified.spec2rgb() has been simplified to rely more on
vismodel(). As a result, output values may be slightly
different but upon testing, we found that differences between the old
and the new version were barely noticeable.getspec() can now read Avantes binary files
(.TRM files)summary.rspec() has been
fixed to (R700 - R450)/R700. This gives the same result as before but
with a flipped sign, and better reflects the original formula in the
literature.getspec() has an additional argument
ignore.case set to TRUE by default to ignore
case in file extension matchinggetspec() would sometimes fail with
files including numbers in scientific formattetraplot() to add cone names
(u,s,m,l)getspec() can now read OceanOptics
.ProcSpec filesgetspecf() (and the argument fast = TRUE
in getspec()) have been deprecatedsummary.rspec() returned incorrect values for S7. If
you use S7, please re-run your analysessummary.rspec() now properly outputs NA
for monotonically decreasing spectrasubset.rspec() was provided with a
logical vectorsummary.colspace() was used
on a tcs objectby argument in merge.rspec() is no longer
ignoredvoloverlap() when plot = TRUEvismodel() when transmission has more than
one columnvismodel() that applied von Kries
correction to achromatic channelfill=FALSE in
voloverlap()jndplot() when suppressing the plotting of
arrowscoldist() when
noise = "quantum" and achro = TRUE were
usedjndplot() when arrow = "none"
and achro = TRUEspec2rgb() now takes into account the 390-400 nm
wavelength range into account when possibleas.rspec() no longer fails with tibblesprocspec() now works for all values of
binscolspace() where it would incorrectly
infer a preference for a general trichromatic space, when a cie model is
more appropriatebootcoldist() uses bootstrap to calculate the
confidence intervals on the mean color distance between samplescoldist() when fewer samples than the
number of photoreceptorsgetspec() that would cause it to crash
with errorstetraplot() when saving images