Skip to content
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

Api rework #34

Merged
merged 64 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
baa439e
Ignore autogenerated .vscode folder
KronosTheLate Sep 14, 2023
d235238
Make parent file for rework, define "findpeaks"
KronosTheLate Sep 14, 2023
ccb918e
implement api_rework as discussed
KronosTheLate Sep 14, 2023
5cb66c3
Remove test code from Peaks.jl
KronosTheLate Sep 14, 2023
80b8280
Update readme to temp state
KronosTheLate Sep 14, 2023
7609635
Add sentence about mutation to todo section
KronosTheLate Sep 14, 2023
24e3892
add sentence about minima finding
KronosTheLate Sep 14, 2023
aac8d5a
Remove temporary content from readme
KronosTheLate Sep 22, 2023
9ccd30a
make `findpeaks` a oneliner
KronosTheLate Sep 24, 2023
4a0d9dc
Don't discard initially calculated heights
KronosTheLate Sep 27, 2023
0e20f16
Export filterpeaks, improve internals in new API
KronosTheLate Sep 27, 2023
369c072
Change linebreaks in "filterpeaks!"
KronosTheLate Sep 27, 2023
080e970
Make use of "strict" in all functions
KronosTheLate Sep 27, 2023
37839c8
Fix some bugs
KronosTheLate Sep 27, 2023
6edefcf
export old functions
KronosTheLate Oct 31, 2023
169e5dd
improvements, polish docstrings
KronosTheLate Oct 31, 2023
67cea1b
tag breaking version
KronosTheLate Oct 31, 2023
310fbfe
misc changes
KronosTheLate Oct 31, 2023
82c5b8e
comment out bad test
KronosTheLate Oct 31, 2023
639f212
minor changes
KronosTheLate Oct 31, 2023
8690fe2
fix docstrung for peakheights
KronosTheLate Oct 31, 2023
40ad1a7
un-commented failing test
KronosTheLate Nov 26, 2023
cf0d8c3
revert exports
KronosTheLate Nov 26, 2023
3c17d0c
move export of filterpeaks!
KronosTheLate Nov 26, 2023
21312b9
remove extra exports
KronosTheLate Nov 26, 2023
7f005f6
explicit about copying data in docstring
KronosTheLate Nov 26, 2023
11eb80d
Turn --> info ->
KronosTheLate Nov 26, 2023
df8537b
add sentence about not copying data
KronosTheLate Nov 26, 2023
5c82230
minheight -> min, same for proms,widths, and max
KronosTheLate Nov 26, 2023
779081e
some changes + better length check in filterpeaks!
KronosTheLate Nov 26, 2023
c19d483
add new method for filterpeaks!
KronosTheLate Nov 26, 2023
26cdb9d
Distribute contents of rework, structure exports
KronosTheLate Nov 26, 2023
c1d553d
Do not include api_work, which does not exist
KronosTheLate Nov 26, 2023
676a0b5
fixed a couple copy-paste errors
KronosTheLate Nov 26, 2023
b70804b
Revert function reordering (clarify changes in docstrings)
halleysfifthinc Jan 22, 2024
f9ab0f2
Example compromise docstrings and ordering
halleysfifthinc Jan 23, 2024
75a588e
Revert random change (fixes tests)
halleysfifthinc Jan 23, 2024
32b16e3
Separate docstring examples for APIs
KronosTheLate Jan 25, 2024
e98362d
Improve curried docstring
KronosTheLate Jan 25, 2024
aaf8b51
make known_fields a const
KronosTheLate Jan 25, 2024
613d0f0
explain what a named tuple slice is
KronosTheLate Jan 25, 2024
8ac658d
Make filterpeaks signature match
KronosTheLate Jan 25, 2024
9848205
swap check order filterpeaks!
KronosTheLate Jan 25, 2024
42aba3c
Swap check order filterpeaks!
KronosTheLate Jan 25, 2024
83c53cb
Merge branch 'master' into api_rework
halleysfifthinc Feb 26, 2024
36b71c7
[nfc] whitespace changes
halleysfifthinc Feb 28, 2024
df11d3a
Update/rewrite docstrings and doctests
halleysfifthinc Feb 28, 2024
bd468cc
Don't need to depwarn old kwargs for new functions
halleysfifthinc Feb 28, 2024
40ea9a3
Rename `up` => `hi`
halleysfifthinc Feb 28, 2024
9b7da88
Update `peakproms!`/`peakwidths!` to use `ismaxima`/`isminima` for ar…
halleysfifthinc Feb 28, 2024
5493b52
Rewrite error for bad min/max order
halleysfifthinc Feb 28, 2024
598477c
Fix unnecessary Missing union when input array doesn't have missings
halleysfifthinc Feb 28, 2024
d5b4281
Update doctest outputs
halleysfifthinc Feb 28, 2024
d805b04
Update deprecated kwargs in tests
halleysfifthinc Feb 28, 2024
5c98dab
Test doctests when running tests
halleysfifthinc Feb 28, 2024
e791292
Test depwarns
halleysfifthinc Feb 28, 2024
1c7180d
Test error for when first peak is not an extrema
halleysfifthinc Feb 28, 2024
66df7ad
Confirm error when namedtuple has widths OR edges
halleysfifthinc Feb 28, 2024
4939c3d
Confirm error in plotpeaks when first peak isnt an extrema
halleysfifthinc Feb 28, 2024
862ad61
Update docstrings/doctests for utils functions
halleysfifthinc Feb 28, 2024
adc1439
Add `filterpeaks!` to the docs
halleysfifthinc Feb 28, 2024
925c9af
Add julia cache to CI docs build
halleysfifthinc Feb 28, 2024
b2ec0a6
Dont try linking to Base docs (for the moment)
halleysfifthinc Feb 28, 2024
f8b6f0e
fix errant ref
halleysfifthinc Feb 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Peaks.jl

[![version](https://juliahub.com/docs/Peaks/version.svg)](https://juliahub.com/ui/Packages/Peaks/3TWUM)
[![pkgeval](https://juliahub.com/docs/Peaks/pkgeval.svg)](https://juliahub.com/ui/Packages/Peaks/3TWUM)
[![stable-docs](https://img.shields.io/badge/docs-stable-blue.svg)](https://halleysfifthinc.github.io/Peaks.jl/stable)
Expand Down
8 changes: 5 additions & 3 deletions src/Peaks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ module Peaks

using Compat

export argmaxima, argminima, maxima, minima, findmaxima, findminima, findnextmaxima,
findnextminima, peakproms, peakproms!, peakwidths, peakwidths!, peakheights,
peakheights!, ismaxima, isminima
# Old exports:
# export argmaxima, argminima, maxima, minima, findmaxima, findminima, findnextmaxima, findnextminima, peakproms, peakproms!, peakwidths, peakwidths!, peakheights, peakheights!, ismaxima, isminima, findpeaks
# Proposed exports:
export findpeaks, peakproms!, peakwidths!, peakheights!
KronosTheLate marked this conversation as resolved.
Show resolved Hide resolved

include("minmax.jl")
include("peakprom.jl")
include("peakwidth.jl")
include("peakheight.jl")
include("api_rework.jl")
include("plot.jl")

end # module Peaks
116 changes: 116 additions & 0 deletions src/api_rework.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# I am putting everything in here for now. The contents of this file should be moved around in the future.
"""
findpeaks(x) -> NamedTuple

Find the peaks in a vector `x`.
A `NamedTuple` is returned with the original vector
in the field `data`, and the indices of the peaks
in the field `inds`.

This function serves as the entry-point for other
functions such as `peakproms!` and `peakwidths!`
"""
findpeaks(x::AbstractVector) = (data=x, inds=findmaxima(x)[1])

"""
_filter_fields!(pks, new_inds)

Internal function to mutate the vectors in all fields of `pks`
to remove any element with a corresponding `pks.inds` entry
not present in `new_inds`.
"""
function _filter_fields!(pks, new_inds)
# Add checks on `pks` to see if it has fields "data" and "inds"?
mask = pks.inds .∉ Ref(new_inds)
for field in filter(!=(:data), propertynames(pks)) # Do not want to mutate data vector
KronosTheLate marked this conversation as resolved.
Show resolved Hide resolved
v_to_be_mutated = getfield(pks, field)
if length(v_to_be_mutated) != length(mask)
error("Internal error: The length of the vector in field `$field` is $(length(v_to_be_mutated)), but was expected to be $(length(mask))")
end
halleysfifthinc marked this conversation as resolved.
Show resolved Hide resolved
deleteat!(getfield(pks, field), mask)
end
return nothing
end

"""
peakproms!(pks::NamedTuple; min=0, max=Inf, strict=true)
peakproms!(; min=0, max=Inf, strict=true)

Find the prominences of the peaks in `pks`, and filter out any peak
with a prominence smaller than `min` or greater than `max`.
The prominences are returned in the field `:proms` of the returned named tuple.

If the positional argument `pks` is omitted, an anonymus function is returned
that performs the action (adding field `:proms` and filtering) to its input.
"""
function peakproms!(pks::NamedTuple; min=0, max=Inf, strict=true)
if !hasproperty(pks, :proms)
# Avoid filtering by min/max/strict here, so that it always happens outside if-statement.
# Pro: one less edge case. Con: More internal allocations
_, proms = _peakproms(pks.inds, pks.data)
pks = merge(pks, (; proms))
end
new_inds, _ = _peakproms(pks.inds, pks.data; minprom=min, maxprom=max, strict)
_filter_fields!(pks, new_inds)
KronosTheLate marked this conversation as resolved.
Show resolved Hide resolved
return pks
end
peakproms!(; kwargs...) = pks -> peakproms!(pks; kwargs...)


"""
peakwidths!(pks::NamedTuple; min=0, max=Inf, relheight=0.5, strict=true)
peakwidths!(; min=0, max=Inf, relheight=0.5, strict=true)

Find the widths of the peaks in `pks`, and filter out any peak
with a width smaller than `min` or greater than `max`.
The widths are returned in the field `:widths` of the returned named tuple.
The edges of the peaks are also added in the field `:edges`.

If the positional argument `pks` is omitted, an anonymus function is returned
that performs the action (adding fields `:widths` and `:edges` and filtering) to its input.

Note: If `pks` does not have a field `proms`, it is added. This is
because it is needed to calculate the peak width.
"""
function peakwidths!(pks::NamedTuple; min=0, max=Inf, relheight=0.5, strict=true)
if !hasproperty(pks, :proms) # Add proms if needed
pks = peakproms!(pks)
end
if xor(hasproperty(pks, :widths), hasproperty(pks, :edges))
throw(ArgumentError("The named tuple `pks` (first argument to `peakwidths!` is expected have both the fields `:widths` and `:edges`, or to have neither of them. The provided `pks` only has one of them."))
end
if !hasproperty(pks, :widths)
# Avoid filtering by min/max/strict here, so that it always happens outside if-statement.
# Pro: one less edge case. Con: More internal allocations
_, widths, leftedges, rightedges = _peakwidths(pks.inds, pks.data, pks.proms)
pks = merge(pks, (; widths, edges=collect(zip(leftedges, rightedges))))
end
new_inds, _ = _peakwidths(pks.inds, pks.data, pks.proms; minwidth=min, maxwidth=max, relheight, strict)
_filter_fields!(pks, new_inds)
return pks
end
peakwidths!(; kwargs...) = pks -> peakwidths!(pks; kwargs...)

"""
peakheights!(pks::NamedTuple; min=0, max=Inf)
peakheights!(; min=0, max=Inf)

Find the heights of the peaks in `pks`, and filter out any peak
with a heights smaller than `min` or greater than `max`.
The heights are returned in the field `:heights` of the returned named tuple.

If the positional argument `pks` is omitted, an anonymus function is returned
that performs the action (adding field `heights` and filtering) to its input.
"""
function peakheights!(pks::NamedTuple; min=0, max=Inf)
if !hasproperty(pks, :heights)
# Avoid filtering by min/max here, so that it always happens outside if-statement.
# Pro: one less edge case. Con: More internal allocations
heights = pks.data[pks.inds]
pks = merge(pks, (; heights))
end
new_inds, _ = _peakheights(pks.inds, pks.heights; minheight=min, maxheight=max)
_filter_fields!(pks, new_inds)
return pks
end
peakheights!(; kwargs...) = pks -> peakheights!(pks; kwargs...)
6 changes: 3 additions & 3 deletions src/peakheight.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ julia> peakheights(xpks, vals; minheight=4.5)
([2], [5])
```
"""
function peakheights(
function _peakheights(
peaks::AbstractVector{Int}, heights::AbstractVector;
minheight=nothing, maxheight=nothing
)
peakheights!(copy(peaks), copy(heights); minheight=minheight, maxheight=maxheight)
_peakheights!(copy(peaks), copy(heights); minheight=minheight, maxheight=maxheight)
end

"""
Expand All @@ -54,7 +54,7 @@ julia> xpks, vals
([4, 7], [3, 4])
```
"""
function peakheights!(
function _peakheights!(
peaks::Vector{Int}, heights::AbstractVector{T};
minheight=nothing, maxheight=nothing
) where {T}
Expand Down
30 changes: 15 additions & 15 deletions src/peakprom.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ julia> peakproms(xpks, x; strict=false)
([2, 4, 7], Union{Missing, Int64}[5, 1, 3])
```
"""
function peakproms(peaks::AbstractVector{Int}, x::AbstractVector{T};
function _peakproms(peaks::AbstractVector{Int}, x::AbstractVector{T};
strict=true, minprom=nothing, maxprom=nothing
) where T
) where {T}
if !isnothing(minprom) || !isnothing(maxprom)
_peaks = copy(peaks)
else
# peaks will not be modified
_peaks = peaks
end
return peakproms!(_peaks, x; strict=strict, minprom=minprom, maxprom=maxprom)
return _peakproms!(_peaks, x; strict=strict, minprom=minprom, maxprom=maxprom)
end

"""
Expand All @@ -66,9 +66,9 @@ prominences.

See also: [`peakproms`](@ref), [`findminima`](@ref), [`findmaxima`](@ref)
"""
function peakproms!(peaks::AbstractVector{Int}, x::AbstractVector{T};
function _peakproms!(peaks::AbstractVector{Int}, x::AbstractVector{T};
strict=true, minprom=nothing, maxprom=nothing
) where T
) where {T}
if !isnothing(minprom) && !isnothing(maxprom)
minprom < maxprom || throw(ArgumentError("minprom must be less than maxprom"))
end
Expand All @@ -91,9 +91,9 @@ function peakproms!(peaks::AbstractVector{Int}, x::AbstractVector{T};
Float64 <: T ? NaN :
Float32 <: T ? NaN32 :
Float16 <: T ? NaN16 :
missing
missing
KronosTheLate marked this conversation as resolved.
Show resolved Hide resolved

proms = similar(peaks,promote_type(T,typeof(_ref)))
proms = similar(peaks, promote_type(T, typeof(_ref)))

if strict
lbegin, lend = firstindex(x), lastindex(x)
Expand All @@ -106,16 +106,16 @@ function peakproms!(peaks::AbstractVector{Int}, x::AbstractVector{T};
lend)

# Find extremum of left and right bounds
if isempty(lb:(peaks[i] - 1))
if isempty(lb:(peaks[i]-1))
lref = _ref
else
lref = exm(view(x, lb:(peaks[i] - 1)))
lref = exm(view(x, lb:(peaks[i]-1)))
end

if isempty((peaks[i] + 1):rb)
if isempty((peaks[i]+1):rb)
rref = _ref
else
rref = exm(view(x, (peaks[i] + 1):rb))
rref = exm(view(x, (peaks[i]+1):rb))
end

proms[i] = abs(x[peaks[i]] - exa(lref, rref))
Expand All @@ -140,15 +140,15 @@ function peakproms!(peaks::AbstractVector{Int}, x::AbstractVector{T};
j = searchsorted(peaks′, peaks[i])

# Find left and right bounding peaks
_lb = findprev(y -> cmp(x[y], x[peaks[i]]) === true, peaks′, first(j)-1)
_lb = findprev(y -> cmp(x[y], x[peaks[i]]) === true, peaks′, first(j) - 1)
peaks′[j] === peaks[i] && (j += 1)
_rb = findnext(y -> cmp(x[y], x[peaks[i]]) === true, peaks′, last(j)+1)
_rb = findnext(y -> cmp(x[y], x[peaks[i]]) === true, peaks′, last(j) + 1)

# Find left and right reverse peaks just inside the bounding peaks
lb = isnothing(_lb) ? firstindex(notm) :
searchsortedfirst(notm, peaks′[_lb])
searchsortedfirst(notm, peaks′[_lb])
rb = isnothing(_rb) ? lastindex(notm) :
searchsortedlast(notm, peaks′[_rb])
searchsortedlast(notm, peaks′[_rb])

k = searchsortedfirst(notm, peaks[i])

Expand Down
32 changes: 16 additions & 16 deletions src/peakwidth.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ julia> peakwidths(xpks, x, [1]; strict=false)
([2], [1.0], [1.5], [2.5])
```
"""
function peakwidths(
function _peakwidths(
peaks::AbstractVector{Int}, x::AbstractVector, proms::AbstractVector;
strict=true, relheight=0.5, minwidth=nothing, maxwidth=nothing,
strict=true, relheight=0.5, minwidth=nothing, maxwidth=nothing
)
if !isnothing(minwidth) || !isnothing(maxwidth)
_peaks = copy(peaks)
else
# peaks will not be modified
_peaks = peaks
end
peakwidths!(_peaks, x, proms; strict=strict, relheight=relheight,
_peakwidths!(_peaks, x, proms; strict=strict, relheight=relheight,
minwidth=minwidth, maxwidth=maxwidth)
end

Expand All @@ -69,10 +69,10 @@ Returns the modified peaks, widths, and the left and right edges at the referenc

See also: [`peakwidths`](@ref), [`peakproms`](@ref), [`findminima`](@ref), [`findmaxima`](@ref)
"""
function peakwidths!(
function _peakwidths!(
peaks::AbstractVector{Int}, x::AbstractVector{T}, proms::AbstractVector{U};
strict=true, relheight=0.5, minwidth=nothing, maxwidth=nothing,
) where {T, U}
strict=true, relheight=0.5, minwidth=nothing, maxwidth=nothing
) where {T,U}
if !isnothing(minwidth) && !isnothing(maxwidth)
minwidth < maxwidth || throw(ArgumentError("maxwidth must be greater than minwidth"))
end
Expand All @@ -89,7 +89,7 @@ function peakwidths!(
cmp = pktype === :maxima ? (≤) : (≥)
op = pktype === :maxima ? (-) : (+)

V1 = promote_type(T,U)
V1 = promote_type(T, U)
_bad = Missing <: V1 ? missing : float(Int)(NaN)

V = promote_type(V1, typeof(_bad))
Expand All @@ -109,22 +109,22 @@ function peakwidths!(
redge[i] = _bad
ledge[i] = _bad
else
ht = op(x[peaks[i]], relheight*proms[i])
lo = findprev(v -> !ismissing(v) && cmp(v,ht), x, peaks[i])
up = findnext(v -> !ismissing(v) && cmp(v,ht), x, peaks[i])
ht = op(x[peaks[i]], relheight * proms[i])
lo = findprev(v -> !ismissing(v) && cmp(v, ht), x, peaks[i])
up = findnext(v -> !ismissing(v) && cmp(v, ht), x, peaks[i])

if !strict
if !isnothing(lo)
lo1 = findnext(v -> !ismissing(v) && cmp(ht,v), x, lo+1)
lo += (ht - x[lo])/(x[lo1] - x[lo])*(lo1 - lo)
lo1 = findnext(v -> !ismissing(v) && cmp(ht, v), x, lo + 1)
lo += (ht - x[lo]) / (x[lo1] - x[lo]) * (lo1 - lo)
end
if !isnothing(up)
up1 = findprev(v -> !ismissing(v) && cmp(ht,v), x, up-1)
up -= (ht - x[up])/(x[up1] - x[up])*(up - up1)
up1 = findprev(v -> !ismissing(v) && cmp(ht, v), x, up - 1)
up -= (ht - x[up]) / (x[up1] - x[up]) * (up - up1)
end
else
!isnothing(lo) && (lo += (ht - x[lo])/(x[lo+1] - x[lo]))
!isnothing(up) && (up -= (ht - x[up])/(x[up-1] - x[up]))
!isnothing(lo) && (lo += (ht - x[lo]) / (x[lo+1] - x[lo]))
!isnothing(up) && (up -= (ht - x[up]) / (x[up-1] - x[up]))
end

redge[i] = something(up, lst)
Expand Down