fix(db): return data unchanged instead of NULL when no covariates found#4000
Conversation
arrhenius.scaling.traits() and filter_sunleaf_traits() both set
data <- NULL in their else-branch, contradicting the documented
behaviour ("data with no matching covariates will be unchanged").
When no temperature covariates exist for any observation, the correct
response is to return the input data unchanged (equivalent to assuming
all measurements were taken at missing.temp = 25 degC, making the
Arrhenius scaling a no-op). Returning NULL instead caused a hard crash
in query.trait.data() at nrow(NULL) whenever Vcmax, root_respiration_rate,
leaf_respiration_rate_m2, or stem_respiration_rate were queried for
species with no temperature covariate recorded in BETYdb.
filter_sunleaf_traits() has the identical bug: when no canopy_layer
covariate is available, all measurements should pass through unfiltered,
not be silently discarded.
6ed1fc0 to
823ed4f
Compare
|
@dlebauer since I think you originally wrote these bits many many years ago, I was looking for your feedback on which is wrong here, the code or the documentation (i.e., do we want to remove these traits if they lack covariates, or assume they correspond to defaults?). If the documentation is wrong, sounds like there are other fixed downstream that are needed |
|
When I implemented this, the default of 25C for missing measurement temperature seemed more defensible, given the dataset that we had. But now, for a temperature-dependent trait with unknown measurement or reference temperature, I think it is safer to either filter those rows out or error rather than silently assume 25C; leave it to the user to explicitly make the decision to fill in missing temperature covariate data. I think that the sunleaf case is different because the method of taking these measurements from top of canopy seems more standardized. |
|
Thanks for the context @dlebauer. That distinction makes sense.
I can update the PR whichever way you'd like. The original crash (argument is of length zero in nrow()) still needs addressing regardless of which policy we pick. |
|
I vote for 2 + warning: drop rows missing covariates, but send the user a warning letting them know that you've done so (e.g. "X rows of data on Y are dropped due to missing covariate date") |
|
I agree with @mdietze that there values should be dropped with warning. Thanks @anshul23102 ! |
|
Thanks for the decision. Here's my plan before I update the code:
Does this sound right to both of you, or should filter_sunleaf_traits follow the same drop + warn approach? |
|
Looks good to me except:
Returns empty data frame iff no input rows have covariates, else returns with rows that had covariates. And please make sure to update docs accordingly and add tests - these don't have to comprehensively cover all inputs, just the key functionality. |
…aling.traits Previously the function silently filled any missing measurement temperature with the `missing.temp` default (25 C) before applying Arrhenius scaling. For a temperature-dependent trait whose measurement temperature is unknown this produces silently wrong values, and when NO covariates at all were found the function returned NULL, crashing query.trait.data() with 'argument is of length zero'. New behaviour agreed with maintainers: - Rows that have no temperature covariate are dropped and a logger.warn() is emitted with the count of dropped rows. - If no observations have any temperature covariate, an empty data frame (zero rows, same columns) is returned instead of NULL. - filter_sunleaf_traits() continues to return data unchanged when no canopy_layer covariate is found (measurement protocol is more standardised for sun-leaf traits). Changes: - covariate.functions.R: implement drop-and-warn logic; keep missing.temp argument for backward compatibility but mark it as unused in docs. - test.covariate.functions.R: add four focused tests covering all rows present, partial drop with warning, all rows dropped, and the filter_sunleaf_traits empty-covariate path. - NEWS.md: correct the entry to describe the actual new behaviour.
|
Thanks @dlebauer and @mdietze. I have pushed the agreed implementation. Here is a summary of what changed:
No change to logic. Returns data unchanged when no Tests added ( Four new tests:
Entry corrected to describe the drop-and-warn behaviour rather than the previous "return unchanged" description. All four test scenarios pass locally with the updated code. CI will cover |
|
Failing checks look like they need a make doc and commit of updated Rmd files |
…rn behaviour The roxygen docstring changed in the previous commit (missing.temp is now unused; covariates param note updated; description updated). Regenerate the corresponding Rd file so CI tree-is-clean check passes.
|
@anshul23102 I appreciate your efforts to address the GH Actions token issue, but I'd request that you submit that as a separate PR so it can be evaluated on its own merits rather than as part of the trait workflow. When doing so please tag @infotroph and @robkooper as I'll want at least one of them evaluating the proposed change. |
4b6e15a to
bcf30c6
Compare
|
Dropped the `ci(render-quarto)` commit from this branch as requested. The GH Actions token fix will go up as a separate PR tagged @infotroph and @robkooper . What remains here:
All R package checks were passing before the push; removing the Dockerfile change should clear the Docker build failures. |
|
Thanks for splitting the patches! I'll be happy to review when it's ready. For the record this is a general rule: CI changes should always go in separately from code changes. For this PR, fortunately the rate limit is a transient issue -- if it fails again, at worst we'll wait an hour and then re-trigger the checks. |
e52c594
Bug
arrhenius.scaling.traits()andfilter_sunleaf_traits()both setdata <- NULLwhen no matching covariates are found, contradicting their own documentation.arrhenius.scaling.traits()The docstring says:
But the else-branch does:
This function is called in
query.trait.data()forVcmax,root_respiration_rate,leaf_respiration_rate_m2, andstem_respiration_rate. When no temperature covariate is recorded in BETYdb for a species,databecomesNULL, and the very next lineif (nrow(result) == 0)throws:The entire trait query pipeline crashes silently. The
missing.temp = 25parameter was designed to handle exactly this case (assume 25°C → scaling is a no-op), but it is never reached.filter_sunleaf_traits()The same
data <- NULLpattern: when nocanopy_layercovariate exists, all observations should pass through unfiltered. Instead they are all discarded.Fix
Remove
data <- NULLfrom both else-branches so the functions fall through toreturn(data)unchanged, as documented.Impact
Any PEcAn meta-analysis run that queries a temperature-sensitive trait for a species with no temperature covariate in BETYdb will crash at
query.trait.data(). This is a common situation for less-studied species or older database entries.