-
Notifications
You must be signed in to change notification settings - Fork 8
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
The API is not great #24
Comments
I acknowledge there are some minor inconveniences due to the API design, but I don't think the alternatives I'm aware of (e.g. a struct) would be better overall.
Providing mutating
I don't understand what you mean here. |
On this point, I was hoping to be able to provide a vector of x-values, wher peak of index Or perhaps a much shorter of a similar message. I realize that this message is heavily taylored to what I needed to hear, and that it may not be that relevant for other users. |
This is an excellent point, and one that I have not at all thought of. My current best idea of an API is a single function The return value could be a named tuple, containing (peakkinds, widths, proms, heights, widths, leftedges, rightedges). If the user does not want to spent computation resources calculating any of the quantities, specify a related filer as This requires only knowledge of one function, and much less code to perform filtering. This is a very rough idea, and I am very curious if you feel like some solution in this direction is at all possible, and if you see the value add I do from not having separate functions, and from not having to pass around the relevant vectors outside the functions. |
I am not a fan of my earlier suggestions, and I really see the value of having ultimate controll over what is calculated and filtered by, and in what order. I believe we can achieve everything we both want by the following proposal: The proposal
This all means that the named tuple will in essence just bundle up the contents to facilitate passing the right things into the functions that filer by and calculate features. This should add negligible overhead and require no new types, while simplifying the API significantly and keep ultimate control over order of filtering. The filtering/calculating functions can also still have To take it one step further, we could make it so that if a named tuple is not passed to the filtering/calculating functions as the first argument, a function is returned that performs the action of adding the feature and filtering. This would allow easy chaining of operations:
That is almost as elegant as a single function with keyword arguments, but without giving up any control, performance, and without redundant calculations - all the strengths of the current API, as I see it. A key benefit of this approach is also that you do not need to consult the docs every time to check the order of the e.g. 4 returned vectors of How do you feel about this approach? Note - A function
This function would allow to only keep the 5 widest peaks, or the 3 highest peaks, which is non-trivial without it due to having multiple vectors in |
That sounds very functional, and would also solve my only minor gripe about the current API, which is that the order of the output arguments and input arguments prevents direct splatting pks, proms = peakproms(argmaxima(x), y))
peakwidths(pks, y, proms)
👌 Nice. I don't have time to get to this in the near future, but if you put together a PR, I will certainly review it. |
Nice. I will see if I find the time to put together a proposal soon. I will initially make the current functionality internal by prefixing _'s and not exporting, and building the new API on top of that as a first approximation. |
I have been unable to squeeze in the time to make a proposal for the API I am imagining. In a few days, I am leaving my computer behind for the summer (until about 2 weeks into september). It is crazy how a few hours of voluntary coding-work translates to weeks and months of calendar time... I expect to give this a go, then, if you do not beat me to it. But I just wanted to tell you, so that you know that I will not be working on this for a long time. |
I have realized that adding fields to a named tuple is not possible, so mutating versions of the functions could not be made. Instead, the user will have to rebind the original variable name to the output each time a feature is added. Also, adding fields changes the type. It is possble this makes type stability impossible, but I am not sure. Dictionaries could solve both problems, at the cost of adding a dependance and giving up the amazing field-access syntax My current though is that it is better to live with unmutability and potential type instability, for the field access syntax and more simplicity. |
Mutating a tuple isn't possible, but same as with structs, mutable fields in a immutable struct/object can be mutated; I think that is what would be needed here? I don't believe that using a vector in a tuple copies the vector, so it should be possible to accept a NamedTuple (containing index, values, etc vectors) as an input argument, mutate those vectors, and then return a new NamedTuple (eg containing old and new data, such as peak proms) without making any copies of the original/mutated vectors? I'm not sure type stability is a concern, since that only applies at the function return type level, and if switching to a NamedTuple design, the returned value/type would be consistent for a given function/input eltype. |
Yes, the vectors can be mutated. I was thinking more of adding fields to the named tuple when I talked of mutating. I think you may be right - a certain inout type will have a known output type. The output type is different depending on if the field existed previously or not, but as that is reflected in the input type, it is likely not an issue. NamedTuples it is. I will still be away from my computer for a month, but I do like having the exact plan ready ^_^ |
I have a draft up and running at https://github.com/KronosTheLate/Peaks.jl/tree/api_rework ! It is still missing a lot of detail, which I tried to collect into a "ToDo" section in the README. Most notably, I have just hidden the old functions by prefixing a "_", and called them internally in the new functions. I do however feel like the user-facing functions work as I imagine, and feel ready to discuss the proposed API with you (item 1 in the ToDo). If/when we get the API right, we move on to the following bullet points, and after that I believe the API will be reworked into something better! Some example code to get started:
|
That example looks promising! Please open a draft PR so that I can review/add comments & suggestions. Ideally, I'd prefer to have both interfaces (the old and the new) available if possible (for backwards compatibility, etc). |
I am a little busy these days with school, and https://juliapackagecomparisons.github.io/. I will get around to it at some point ^_^ |
No rush! I appreciate your interest and contributions. |
EDIT: See later comments, as the proposed API has changed significantly during this thread.
I find the current implementation of
pks
with the return values frompeakproms
andpeakwidths
to be rather weird and un-ergonomic. Something like what is implemented in https://github.com/tungli/Findpeaks.jl seems way more inntuitive for me, where there is only one function, and the peaks can be filtered by prominence and width directly by keyword arguments.
If a redo is on the table, I just want to mention an idea I have had:
With the package called "Peaks", I was expecting "findpeaks" to be the main function. It could return a
FoundPeaks
object (Can not be same as package, perhaps other names for the type is better) that contains the peaks, the indices, the prominence, and the widths. It would also be nice to be able to provide the x-values, and get the corresponding x-values for the peaks and widths in terms of x-values, rather than having to work with indices always. Ofc the indices should also be part of the returned object.Then, the API could be something like
The text was updated successfully, but these errors were encountered: