Skip to content

Commit

Permalink
Api rework (#34)
Browse files Browse the repository at this point in the history
* Ignore autogenerated .vscode folder

* Make parent file for rework, define "findpeaks"

* implement api_rework as discussed

* Remove test code from Peaks.jl

* Update readme to temp state

* Add sentence about mutation to todo section

* add sentence about minima finding

* Remove temporary content from readme

* make `findpeaks` a oneliner

* Don't discard initially calculated heights

* Export filterpeaks, improve internals in new API

* Change linebreaks in "filterpeaks!"

* Make use of "strict" in all functions

* Fix some bugs

* export old functions

* improvements, polish docstrings

* tag breaking version

* misc changes

* comment out bad test

* minor changes

* fix docstrung for peakheights

* un-commented failing test

* revert exports

* move export of filterpeaks!

* remove extra exports

* explicit about copying data in docstring

* Turn --> info ->

* add sentence about not copying data

* minheight -> min, same for proms,widths, and max

* some changes + better length check in filterpeaks!

* add new method for filterpeaks!

* Distribute contents of rework, structure exports

* Do not include api_work, which does not exist

* fixed a couple copy-paste errors

* Revert function reordering (clarify changes in docstrings)

* Example compromise docstrings and ordering

* Revert random change (fixes tests)

* Separate docstring examples for APIs

* Improve  curried docstring

* make known_fields a const

* explain what a named tuple slice is

* Make filterpeaks signature match

* swap check order filterpeaks!

* Swap check order filterpeaks!

* [nfc] whitespace changes

* Update/rewrite docstrings and doctests

* Don't need to depwarn old kwargs for new functions

* Rename `up` => `hi`

* Update `peakproms!`/`peakwidths!` to use `ismaxima`/`isminima` for argument validation

* Rewrite error for bad min/max order

* Fix unnecessary Missing union when input array doesn't have missings

* Update doctest outputs

* Update deprecated kwargs in tests

* Test doctests when running tests

* Test depwarns

* Test error for when first peak is not an extrema

* Confirm error when namedtuple has widths OR edges

* Confirm error in plotpeaks when first peak isnt an extrema

* Update docstrings/doctests for utils functions

* Add `filterpeaks!` to the docs

* Add julia cache to CI docs build

* Dont try linking to Base docs (for the moment)

* fix errant ref

---------

Co-authored-by: Allen Hill <[email protected]>
  • Loading branch information
KronosTheLate and halleysfifthinc authored Feb 28, 2024
1 parent 3fd7f81 commit a3f2ddd
Show file tree
Hide file tree
Showing 19 changed files with 648 additions and 198 deletions.
1 change: 1 addition & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
- uses: julia-actions/setup-julia@v1
with:
version: '1'
- uses: julia-actions/cache@v1
- run: |
julia --project=docs -e '
using Pkg
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
Manifest.toml
docs/build/
docs/site/

.vscode/
7 changes: 0 additions & 7 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,3 @@ Compat = "2.1, 3, 4"
RecipesBase = "1.3"
julia = "1.6"

[extras]
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "OffsetArrays", "Plots"]
1 change: 1 addition & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ peakwidths
peakwidths!
peakheights
peakheights!
filterpeaks!
findnextmaxima
findnextminima
ismaxima
Expand Down
3 changes: 2 additions & 1 deletion src/Peaks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ using Compat

export argmaxima, argminima, maxima, minima, findmaxima, findminima, findnextmaxima,
findnextminima, peakproms, peakproms!, peakwidths, peakwidths!, peakheights,
peakheights!, ismaxima, isminima
peakheights!, ismaxima, isminima, filterpeaks!

include("minmax.jl")
include("utils.jl")
include("peakprom.jl")
include("peakwidth.jl")
include("peakheight.jl")
Expand Down
42 changes: 32 additions & 10 deletions src/minmax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,19 +186,31 @@ function maxima(
end

"""
findmaxima(x[, w=1; strict=true]) -> (idxs, vals)
findmaxima(x[, w=1; strict=true]) -> (;indices, heights, data)
Find the indices and values of local maxima in `x`, where each maxima `i` is either the
maximum of `x[i-w:i+w]` or the first index of a plateau.
Find the indices and values of local maxima in `x`, where each maxima `i` is
either the maximum of `x[i-w:i+w]` or the first index of a plateau.
Returns a `NamedTuple` contains the fields `indices`, `heights`, `data`, which are
equivalent to `heights = data[indices]`. The `data` field is a reference (not a copy) to
the argument `x`.
A plateau is defined as a maxima with consecutive equal (`===`/egal) maximal values which
are bounded by lesser values immediately before and after the consecutive maximal values.
See also: [`argmaxima`](@ref), [`findnextmaxima`](@ref)
# Examples
```jldoctest
julia> data = [1, 5, 1, 3, 2];
julia> pks = findmaxima(data)
(indices = [2, 4], heights = [5, 3], data = [1, 5, 1, 3, 2])
```
"""
function findmaxima(x, w::Int=1; strict::Bool=true)
idxs = argmaxima(x, w; strict=strict)
return (idxs, x[idxs])
return (;indices=idxs, heights=x[idxs], data=x)
end

"""
Expand Down Expand Up @@ -230,7 +242,6 @@ julia> findnextminima([3,2,3,1,1,3], 3)
```
"""
findnextminima(x, i, w=1; strict=true) = findnextextrema(>, x, i, w, strict)

"""
isminima(i, x[, w=1; strict=true]) -> Bool
Expand Down Expand Up @@ -313,18 +324,29 @@ function minima(
end

"""
findminima(x[, w=1; strict=true]) -> (idxs, vals)
findminima(x[, w=1; strict=true]) -> (;indices, heights, data)
Find the indices and values of local minima in `x`, where each minima `i` is either the
minimum of `x[i-w:i+w]` or the first index of a plateau.
Find the indices and values of local minima in `x`, where each minima `i` is
either the minimum of `x[i-w:i+w]` or the first index of a plateau.
Returns a `NamedTuple` contains the fields `indices`, `heights`, `data`, which are
equivalent to `heights = data[indices]`. The `data` field is a reference (not a copy) to
the argument `x`.
A plateau is defined as a minima with consecutive equal (`===`/egal) minimal values which
are bounded by greater values immediately before and after the consecutive minimal values.
See also: [`argminima`](@ref), [`findnextminima`](@ref)
# Examples
```jldoctest
julia> data = [1, 5, 1, 3, 2];
julia> valleys = findminima(data)
(indices = [3], heights = [1], data = [1, 5, 1, 3, 2])
```
"""
function findminima(x, w::Int=1; strict::Bool=true)
idxs = argminima(x, w; strict=strict)
return (idxs, x[idxs])
return (;indices=idxs, heights=x[idxs], data=x)
end

123 changes: 92 additions & 31 deletions src/peakheight.jl
Original file line number Diff line number Diff line change
@@ -1,73 +1,134 @@
"""
peakheights(peaks, heights;
minheight=nothing,
maxheight=nothing
) -> (peaks, heights)
peakheights(indices, heights; [min, max]) -> (indices, heights)
peakheights(pks::NamedTuple; [min, max]) -> NamedTuple
Return a copy of `peaks` and `heights` where peak heights are removed if less than
`minheight` and/or greater than `maxheight`.
Return a copy of `indices` and `heights` where peaks are removed if their height is less than
`min` and/or greater than `max`.
If a NamedTuple `pks` is given, a new NamedTuple is returned with filtered copies of fields
from `pks`. `pks` must have `:indices` and `:heights` fields. The fields `:proms`,
`:widths`, and `:edges` will be filtered if present, and any remaining fields will be
copied unmodified.
See also: [`peakproms`](@ref), [`peakwidths`](@ref), [`findmaxima`](@ref)
# Examples
```jldoctest
julia> x = [0,5,2,3,3,1,4,0];
julia> xpks, vals = findmaxima(x)
([2, 4, 7], [5, 3, 4])
julia> pks = findmaxima(x)
(indices = [2, 4, 7], heights = [5, 3, 4], data = [0, 5, 2, 3, 3, 1, 4, 0])
julia> peakheights(xpks, vals; maxheight=4)
([4, 7], [3, 4])
julia> peakheights(pks; max=4)
(indices = [4, 7], heights = [3, 4], data = [0, 5, 2, 3, 3, 1, 4, 0])
julia> peakheights(xpks, vals; minheight=4.5)
([2], [5])
julia> inds, heights = peakheights(pks.indices, pks.heights; max=4)
([4, 7], [3, 4])
```
"""
function peakheights(
peaks::AbstractVector{Int}, heights::AbstractVector;
minheight=nothing, maxheight=nothing
indices::AbstractVector{Int}, heights::AbstractVector;
minheight=nothing, maxheight=nothing,
min=minheight, max=maxheight
)
peakheights!(copy(peaks), copy(heights); minheight=minheight, maxheight=maxheight)
if !isnothing(minheight)
Base.depwarn("Keyword `minheight` has been renamed to `min`", :peakheights!)
end
if !isnothing(maxheight)
Base.depwarn("Keyword `maxheight` has been renamed to `max`", :peakheights!)
end
peakheights!(copy(indices), copy(heights); min=min, max=max)
end

peakheights(pks::NamedTuple; kwargs...) = peakheights!(deepcopy(pks); kwargs...)

"""
peakheights(; [min, max]) -> Function
Create a function, `f(pks::NamedTuple)`, that copies and filters the peak heights of its
argument, `pks`, using any given keyword arguments.
# Examples
```jldoctest
julia> findmaxima([0, 5, 2, 3, 3, 1, 4, 0]) |> peakheights(; max=4)
(indices = [4, 7], heights = [3, 4], data = [0, 5, 2, 3, 3, 1, 4, 0])
```
"""
peakheights(; kwargs...) = function _curried_peakheights(pks)
return peakheights(deepcopy(pks); kwargs...)
end

"""
peakheights!(peaks, heights;
minheight=nothing,
maxheight=nothing
) -> (peaks, heights)
peakheights!(indices, heights; [min, max]) -> (indices, heights)
peakheights!(pks::NamedTuple; [min, max]) -> NamedTuple
Filter (mutate) and return `indices` and `heights` by removing peaks that are less than `min`
and/or greater than `max`.
Modify and return `peaks` and `heights` by removing peaks that are less than `minheight` or greater
than `maxheight`.
If a NamedTuple `pks` is given, a new NamedTuple is returned with the same fields
(references) from `pks`. `pks` must have `:indices` and `:heights` fields. The fields
`:proms`, `:widths`, and `:edges` will be filtered (mutated) if present, and any remaining
fields will be referenced unmodified.
See also: [`peakproms`](@ref), [`peakwidths`](@ref), [`findmaxima`](@ref)
[`filterpeaks!`](@ref)
# Examples
```jldoctest
julia> x = [0,5,2,3,3,1,4,0];
julia> xpks, vals = findmaxima(x)
([2, 4, 7], [5, 3, 4])
julia> pks = findmaxima(x)
(indices = [2, 4, 7], heights = [5, 3, 4], data = [0, 5, 2, 3, 3, 1, 4, 0])
julia> peakheights!(xpks, vals; maxheight=4);
julia> peakheights!(pks; max=4)
(indices = [4, 7], heights = [3, 4], data = [0, 5, 2, 3, 3, 1, 4, 0])
julia> xpks, vals
([4, 7], [3, 4])
julia> inds, heights = peakheights!(pks.indices, pks.heights; min=3.5)
([7], [4])
```
"""
function peakheights!(
peaks::Vector{Int}, heights::AbstractVector{T};
minheight=nothing, maxheight=nothing
minheight=nothing, maxheight=nothing,
min=minheight, max=maxheight
) where {T}
if !isnothing(minheight)
Base.depwarn("Keyword `minheight` has been renamed to `min`", :peakheights!)
end
if !isnothing(maxheight)
Base.depwarn("Keyword `maxheight` has been renamed to `max`", :peakheights!)
end
length(peaks) == length(heights) || throw(DimensionMismatch("length of `peaks`, $(length(peaks)), does not match the length of `heights`, $(length(heights))"))
if !isnothing(minheight) || !isnothing(maxheight)
lo = something(minheight, typemin(Base.nonmissingtype(T)))
up = something(maxheight, typemax(Base.nonmissingtype(T)))
matched = findall(x -> !(lo x up), heights)
if !isnothing(min) || !isnothing(max)
lo = something(min, typemin(Base.nonmissingtype(T)))
hi = something(max, typemax(Base.nonmissingtype(T)))
matched = findall(x -> !(lo x hi), heights)
deleteat!(peaks, matched)
deleteat!(heights, matched)
end

return peaks, heights
end

function peakheights!(pks::NamedTuple; min=nothing, max=nothing)
filterpeaks!(pks, :heights; min, max)
return pks
end

"""
peakheights!(; [min, max]) -> Function
Create a function, `f(pks::NamedTuple)`, that calculates peak heights and then filters
(mutates) the fields of its argument, `pks`, using any given keyword arguments.
# Examples
```jldoctest
julia> findmaxima([0, 5, 2, 3, 3, 1, 4, 0]) |> peakheights!(; max=4)
(indices = [4, 7], heights = [3, 4], data = [0, 5, 2, 3, 3, 1, 4, 0])
```
"""
peakheights!(; kwargs...) = function _curried_peakheights!(pks)
return peakheights!(pks; kwargs...)
end

Loading

0 comments on commit a3f2ddd

Please sign in to comment.