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

Add cummax() and cummin()? #1649

Closed
johnmyleswhite opened this issue Dec 1, 2012 · 9 comments
Closed

Add cummax() and cummin()? #1649

johnmyleswhite opened this issue Dec 1, 2012 · 9 comments

Comments

@johnmyleswhite
Copy link
Member

Should we have cummax() and cummin() functions to complement the cumsum() and cumprod() functions?

@StefanKarpinski
Copy link
Member

I think we can have these but in general we ought to be able to do scans with arbitrary functions easily (we probably already can, actually, on phone atm).

@johnmyleswhite
Copy link
Member Author

I agree. I think the right way to do this is to define an arbitrary scan function, like use it to define all of cumsum, cumprod, cummin and cummax.

@StefanKarpinski
Copy link
Member

We can't currently do that without a performance hit. Once code gen specializes on function values, that limitation will go away and a lot of metapeogramming with eval can be replaced with straightforward higher-order functions.

@johnmyleswhite
Copy link
Member Author

Good to know that you're hoping to change code gen to be more efficient for those kind of higher-order functions used function as function definitions.

@johnmyleswhite
Copy link
Member Author

In the hopes of bumping this, I pushed e6bc5ad, which adds these functions and simple tests of their validity. If I've screwed something up, please let me know and I'll undo the changes.

@JeffBezanson
Copy link
Member

Looks ok to me.

@s-celles
Copy link
Contributor

I was also looking for cummin and cummax function.
Unfortunately they doesn't seems to exist anymore (or at least I haven't found them)

See https://discourse.julialang.org/t/cumulative-min-cumulative-max/25516/4

Now accumulate(min, s) can replace cummin but having such functions "out the box" (or in a separate package) will be great.

Looking at https://github.com/JuliaLang/julia/blob/39e5742557cf56f26e1eccd28ab52d258ae19863/base/accumulate.jl

I did

import Base: promote_op

# cumulative min

cummin!(B::AbstractArray{T}, A; dims::Integer) where {T} =
    accumulate!(min, B, A, dims=dims)

function cummin(A::AbstractArray{T}; dims::Integer) where T
    out = similar(A, promote_op(min, T, T))
    cummin!(out, A, dims=dims)
end
cummin(x::AbstractVector) = cummin(x, dims=1)


# cumulative max

cummax!(B::AbstractArray{T}, A; dims::Integer) where {T} =
    accumulate!(max, B, A, dims=dims)

    function cummax(A::AbstractArray{T}; dims::Integer) where T
    out = similar(A, promote_op(max, T, T))
    cummax!(out, A, dims=dims)
end
cummax(x::AbstractVector) = cummax(x, dims=1)

and test

using Test

include("accumulate.jl")

@testset "cummin" begin
    @testset "1D cummin!" begin
        a = [10, 12, 14, 9, 10, 8, 16, 20]
        cummin!(a, a, dims=1)
        expected_a_cum = [10, 10, 10, 9, 9, 8, 8, 8]
        @test a == expected_a_cum
    end

    @testset "1D cummin" begin
        a = [10, 12, 14, 9, 10, 8, 16, 20]
        a_cum = cummin(a)
        expected_a_cum = [10, 10, 10, 9, 9, 8, 8, 8]
        @test a_cum == expected_a_cum
    end
end


@testset "cummax" begin
    @testset "1D cummax!" begin
        a = [10, 12, 14, 9, 10, 8, 16, 20]
        cummax!(a, a, dims=1)
        expected_a_cum = [10, 12, 14, 14, 14, 14, 16, 20]
        @test a == expected_a_cum
    end

    @testset "1D cummax" begin
        a = [10, 12, 14, 9, 10, 8, 16, 20]
        a_cum = cummax(a)
        expected_a_cum = [10, 12, 14, 14, 14, 14, 16, 20]
        @test a_cum == expected_a_cum
    end
end

More testing and doc is probably required

@s-celles
Copy link
Contributor

An other API idea (inspired by Python / Pandas) could be the use of "expanding" functions

See https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.expanding.html#pandas.DataFrame.expanding

julia> a = [10, 12, 14, 9, 10, 8, 16, 20]
8-element Array{Int64,1}:
 10
 12
 14
  9
 10
  8
 16
 20

julia> cumsum(a)
8-element Array{Int64,1}:
 10
 22
 36
 45
 55
 63
 79
 99

julia> sum(expanding(a))
8-element Array{Int64,1}:
 10
 22
 36
 45
 55
 63
 79
 99

julia> cumprod(a)
8-element Array{Int64,1}:
        10
       120
      1680
     15120
    151200
   1209600
  19353600
 387072000

julia> prod(expanding(a))
8-element Array{Int64,1}:
        10
       120
      1680
     15120
    151200
   1209600
  19353600
 387072000

julia> cummin(a)
ERROR: UndefVarError: cummin not defined
Stacktrace:
 [1] top-level scope at none:0

julia> min(expanding(a))
8-element Array{Int64,1}:
 10
 10
 10
  9
  9
  8
  8
  8

julia> max(expanding(a))
8-element Array{Int64,1}:
 10
 12
 14
 14
 14
 14
 16
 20

Same API idea could be applied for rolling functions JeffreySarnoff/RollingFunctions.jl#14

@s-celles
Copy link
Contributor

Here is possible implementation of this "expanding" functions...

struct Expanding
    data
end

expanding(data) = Expanding(data)

import Base: sum, prod, min, max

function sum(exp::Expanding)
    accumulate(+, exp.data)
end

function prod(exp::Expanding)
    accumulate(*, exp.data)
end

function min(exp::Expanding)
    accumulate(min, exp.data)
end

function max(exp::Expanding)
    accumulate(max, exp.data)
end

and test

using Test

include("expanding.jl")


@testset "expanding" begin

    @testset "cumsum" begin
        a = [10, 12, 14, 9, 10, 8, 16, 20]
        @test sum(expanding(a)) == [10, 22, 36, 45, 55, 63, 79, 99]
    end

    @testset "cumprod" begin
        a = [10, 12, 14, 9, 10, 8, 16, 20]
        @test prod(expanding(a)) == [10, 120, 1680, 15120, 151200, 1209600, 19353600, 387072000]
    end

    @testset "cummin" begin
        a = [10, 12, 14, 9, 10, 8, 16, 20]
        @test min(expanding(a)) == [10, 10, 10, 9, 9, 8, 8, 8]
    end

    @testset "cummax" begin
        a = [10, 12, 14, 9, 10, 8, 16, 20]
        @test max(expanding(a)) == [10, 12, 14, 14, 14, 14, 16, 20]
    end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants