-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simulation output utils #87
Conversation
Hi @alaindanet, and 💔 :'( The tests in To avoid this in the future, make sure, at least once per test file, that they are actually run by intentionally breaking them and then watching them fail. If they don't fail when you can tell that they are broken, then maybe it's because they are not actually run. |
It should good now!! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @alaindanet and good job for this PR. FWIU you are essentially extending the set of possible post-processing/analysis of the trajectories resulting from simulate()
, and you are taking this opportunity to refresh functionning.jl
, which was a rather old file (and thank you for that <3). There are a lot of tiny comments in my review here, and I haven't written all of them, but here is the general essence of this review :)
-
I trust you regarding the analyses, you know what a
CV
is better than me ;) -
I think your overall design is good. Most analyses are based on the result of
filter_sim
, extracting only the last few timestep in the solution. I also like how you are returning explicit named tuples from your functions, this is very comfortable for the callers :) Be careful though:- If
filter_sim
is never going to have another use than extracting the last timesteps, maybe it's worth renaming it to something more explicit likelast_steps_of(solution; species, n)
ortail(solution; n, species)
ortrajectory_end(solution; species, n)
or something? -
filter_sim
currently returns a read/write alias to the underlying solution tables, and is exposed to user. This makes it possible to (inadvertently) modify the solution by modifying the output offilter_sim
. If this is undesired, consider returning a copy instead. - BONUS: would it be useful that species be alternately requested by name instead of index in
filter_sim
and all other methods usingidxs
?
- If
-
I think I understand that
functionning.jl
andstability.jl
were old files not exactly synchronized with the rest of the project in terms of coding style, and you have started fixing that. Unfortunately we are not quite there yet ^ ^" Here are a few common style nitpicks I have spotted throughout the PR:- unnecessary type constrainst in methods parameters (like
::Float64
or::Int64
), you can remove them all unless they are truly needed for method disambiguation. - unnecessary
return
keywords at the end of blocks. - uninformative docstrings for alternate methods of functions. You can merge them with the "main" docstring to make things clearer.
- a few old docstrings still violate line length limits (
92
) - missing references in docstrings for literature-based calculations, or missing links/dois.
- Maybe all doctests can be made more compact/readable the following way: instead of
- unnecessary type constrainst in methods parameters (like
julia> line 1;
julia> line 2;
julia> line 3
expected_output
Maybe we should turn all these to:
julia> line 1;
line 2;
line 3
expected_output
(note that this is not only for this PR but valid throughout the project ^ ^")
Tell me what you think, and when/whether you have time to address all this :)
Thank you for the feedbacks, I think that I covered all your comments! Specifically for the |
Great, thank you :) Maybe I can review it on Friday 🤞 |
This pull request should also solve #97 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @alaindanet, and thank you for your modifications. Your PR is becoming nicer every time I read it :) I also appreciate how you've taken this opportunity to address #97 <3
I'm sorry to spot new things in my comments now that I could maybe have seen last time, and I hope you'll forgive me to suggest yet another round. Here is the general trend of my review:
-
Strongest first: resetting the
last
option without user consent is a rather bad thing to do IMO, especially when it's possible to just silent the associated warning withquiet = true
. I suggest you better issue a hard error when you think it's impossible to extract the number of timesteps that the user is asking for. -
I am not sure whether you are defending that
filter_sim
is a good name for this function, or if you have reasons to think that my other suggestions here are inadequate? -
Regardless of its name,
filter_sim
seems central to this PR since almost every other function depends on it. One problem then is that options tofilter_sim
are repeated within every other function. You can make the design DRY-er here by making use ofkwargs...
in functions that would then just forward the options tofilter_sim
as-is (explanations in the comments). -
I think your idea of specifying the
last
timesteps in percentage rather than an absolute number of steps is rather good. And maybe the user could benefit from it.last = "10%"
could become the default option, even thoughlast = 10
would still be understood. -
When type-based method call disambiguation relies on
::Vector
or::Matrix
constraint, then it's relying on exactly these two (concrete) types, so the dispatching will not happen as you expect if the caller uses aBitVector
value or aSparseMatrix
value. Indeed:
julia> BitVector <: Vector
false
julia> SparseMatrix <: Matrix
false
To overcome this, remember to keep type constraints as flexible as possible in Julia's methods arguments. Here for instance with ::AbstractVector
and ::AbstractMatrix
. If you did not know about them, you can now query Julia with e.g. subtypes(AbstractVector)
and supertypes(Vector)
;)
- The overall files coding style has been improved a lot since I last reviewed this, good job :) You can now take this one step further by making more frequent uses of markdown's
*
and`
markers to better format the docstrings. Think about how happy you feel when you are reading nice docs online, I'm sure you would be happy to produce the same kind of good-quality docs ;)
I think this PR is getting riper overall, and I guess maybe it'll be ready to land next time and close #79, #87, #97 ^ ^ Keep up with the good pace!
Thank you so much @iago-lito for the review and thanks guys for the suggestions regarding naming and featuring of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @alaindanet and thank you for improving your PR again :)
It may not be immediately clear from the number of comments in this new review, but I swear this is becoming better every time ^ ^" In particular, I see no incorrect design accross functions anymore (code style, kwargs
..) and all the suggested fixes are now scoped within their own functions now, which I guess is the sign that your code is becoming stable.
-
There is confusion which functions are
export
ed and which are not, leading to confusion which functions need to be prefixed withBEFWM2.
and which do not. I suspect that you were initially intending toexport
all of them, so thatBEFWM2.
would be not needed anywhere.. but maybe I'm wrong? -
You have removed too many
return
keywords and some of them were actually necessary ^ ^" I have marked them. This is a fortunate accident though, because, since you have not noticed the problem, it's revealing that the associated test cases were missing from the doctests or thetest/
folder. Better adding them now :P -
I think there is much confusion regarding type-checking in
extract_last_timesteps
. This function is complicated indeed because it accepts various possible types of arguments, which requires careful processing. On the other hand, by forwarding arguments to it via thekwargs
variables, you have successfully narrowed this complexity down to this function only, so there should be no problem left once this one is fixed. I have alternate designs in my comments below. -
A few items from previous reviews have not yet been addressed, especially these type constraints which were too hard. Don't hesitate to skim through this entire github thread and use it as a giant TODO list. I think you can even tick the boxes in my own messages :P
Alé, good luck with your last round (maybe? 🤞). Looking forward to merging this soon.
PS: Your branch is late wrt dev
and I was expecting many conflicts because of @Thomalpas's pass through the docs. Well it turns out that rebasing it was not that difficult in the end. So I have rebased and squashed your commits if you fancy updating it :) The result can be found at simulation_output_utils-squashed-rebased
, and also usecase_competition-rebased
if you need.
c7bce8b
to
157938b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @alaindanet and thank you again :)
You have finally agreed to drop the autofix_last
argument, and the code in utils.jl
has become much clearer! I need to congratulate you because I suppose it hasn't been easy to drop this feature you liked, but I hope you also find like me that the extract_last_timesteps
function has become much neater :)
Most my comments here are minor lexical/wording nitpicks that you may choose to address or not.
Only three are non-minor, but they should not require deep refactorings either:
-
autofix_last
is no more, but it's still documented as a possible option. You need to update your docstring :P -
NEW: I am now confident that Julia deprecates abbreviations in naming. As a consequence, maybe consider again renaming
idxs
toindexes
, and named tuples fields likeavg
,sp
,com
toaverage
,species
,<??>
. I think it's okay to keepcv
because it's rather an "acronym" than an "abbreviation", andcoefficient_of_variation
would be not concise enough in a named tuple. Also,std
is, well, standard enough to be kept as-is ;) -
NEW: I worry that it's unclear why you are using the
corrected = false
variants ofstd()
andcov()
. Can you double-check with @skefi / @andbeck / @evadelmas that this is the right way to calculate these statistics? And that it would not incur biases in downstream analyses performed by our users?
See you next round, but I swear it really seems like there should be not many more <3
PS: I have rebased your branch to 157938b to integrate the latest fix on dev
. This required a force-push. I know you know how to handle this, but don't hesitate to ping me if you need help :)
157938b
to
524bd68
Compare
I have completed the review finally, thank you so much for your feedback! Some introduced changes:
I have some concerns about my code, I would be grateful to receive your feedback on it: Argument typeI have to specify an argument type to avoid wrong dispatch of function max_trophic_level(solution::SciMLBase.ODESolution; threshold = 0, kwargs...)
...
end
max_trophic_level(A::Matrix;) = max_trophic_level(trophic_levels(A)) Filter extinct speciesFor now, the idenfication of extinct species relies on the fact that there are Tuple namesSome naming are a bit too much again, particularly in (:max, :average, :weighted_average, :alive_species, :alive_trophic_level , :alive_A) |
9e711d8
to
a683fb9
Compare
src/measures/stability.jl
Outdated
""" | ||
function avg_cv_sp(mat) | ||
function species_cv(solution::SciMLBase.ODESolution; threshold = 0, last = "10%", kwargs...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at supertypes(SciMLBase.ODESolution)
and supertypes(SciMLBase.RODESolution)
, it looks like the types SciMLBase.AbstractODESolution
or SciMLBase.AbstractTimeseriesSolution
would be more stable on the long run
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solved by @iago-lito 😄
f413f66
3eec5e7
to
d5b316c
Compare
Add various functions to post-process the solutions trajectories returned by `simulate`. Take this opportunity to refresh a few old files under `src/measures/`.
- Vertical whitespace. - Multiline strings. - `; arg = arg` elisions. - ..
6077960
to
cc36b0f
Compare
This pull request contains refactorisation of output analysis from
simulate()
.It contains several outputs:
species_richness()
,species_persistence()
,living_species()
,get_extinct_species()
,shannon(),
simpson()
,evenness()
biomass()
returns total biomass but also of each species.producer_growth()
returns, mean, sd, and all thetimeseries of producer production
trophic_structure()
returns max, mean, biomass weightedmean trophic level (keeping only the living species), the list of living species and the living network.
The functions takes a
solution
object as primary argument but you add arguments such aslast
(over the last timestep),
threshold
(to consider that biomass is 0) for many of them. Now there is alsoidxs
argument in some functions to pick the species you want to compute themetric of interest (exemple I want the species persistence of producer only, or of
non-producer only).