diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml index ce8d353..9b9489c 100644 --- a/.github/workflows/CompatHelper.yml +++ b/.github/workflows/CompatHelper.yml @@ -8,9 +8,15 @@ jobs: CompatHelper: runs-on: ubuntu-latest steps: - - name: Pkg.add("CompatHelper") - run: julia -e 'using Pkg; Pkg.add("CompatHelper")' - - name: CompatHelper.main() + - name: Install CompatHelper + shell: julia --color=yes {0} + run: | + using Pkg + Pkg.add("CompatHelper") + - name: Run CompatHelper + shell: julia --color=yes {0} + run: | + using CompatHelper + CompatHelper.main() env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: julia -e 'using CompatHelper; CompatHelper.main()' diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml new file mode 100644 index 0000000..704607e --- /dev/null +++ b/.github/workflows/Documentation.yml @@ -0,0 +1,27 @@ +name: Documentation +on: + push: + branches: [master] + tags: '*' + pull_request: +jobs: + build: + # These permissions are needed to: + # - Deploy the documentation: https://documenter.juliadocs.org/stable/man/hosting/#Permissions + # - Delete old caches: https://github.com/julia-actions/cache#usage + permissions: + actions: write + contents: write + pull-requests: read + statuses: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: '1' + show-versioninfo: true + - uses: julia-actions/cache@v2 + - uses: julia-actions/julia-docdeploy@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/UnitTest.yml b/.github/workflows/UnitTest.yml index d61d590..5da1c98 100644 --- a/.github/workflows/UnitTest.yml +++ b/.github/workflows/UnitTest.yml @@ -42,20 +42,13 @@ jobs: arch: ${{ matrix.julia-arch }} - name: Cache artifacts - uses: actions/cache@v4 - env: - cache-name: cache-artifacts - with: - path: ~/.julia/artifacts - key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} - restore-keys: | - ${{ runner.os }}-test-${{ env.cache-name }}- - ${{ runner.os }}-test- - ${{ runner.os }}- + uses: julia-actions/cache@v2 + - name: "Unit Test" uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v5 with: - file: lcov.info + files: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/docs/Project.toml b/docs/Project.toml index 3dfa748..1b78166 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -4,5 +4,8 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [compat] -Documenter = "~0.22" +Documenter = "1" Unitful = "≥ 0.2.6" + +[sources] +AxisArrays = {path = ".."} diff --git a/docs/make.jl b/docs/make.jl index 47ed3c8..c7c4f9a 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,6 +6,5 @@ makedocs( ) deploydocs( - deps = Deps.pip("mkdocs", "python-markdown-math"), repo = "github.com/JuliaArrays/AxisArrays.jl.git" ) diff --git a/docs/src/index.md b/docs/src/index.md index f9e1d80..6430853 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,25 +1,7 @@ -```@meta -DocTestSetup = quote - using AxisArrays, Unitful, Random - import Unitful: s, ms, µs - rng = MersenneTwister(123) - fs = 40000 - y = randn(rng, 60*fs+1)*3 - for spk = (sin.(0.8:0.2:8.6) .* [0:0.01:.1; .15:.1:.95; 1:-.05:.05] .* 50, - sin.(0.8:0.4:8.6) .* [0:0.02:.1; .15:.1:1; 1:-.2:.1] .* 50) - i = rand(rng, round(Int,.001fs):1fs) - while i+length(spk)-1 < length(y) - y[i:i+length(spk)-1] += spk - i += rand(rng, round(Int,.001fs):1fs) - end - end - A = AxisArray([y 2y], Axis{:time}(0s:1s/fs:60s), Axis{:chan}([:c1, :c2])) -end -``` - # AxisArrays -[![Build Status](https://travis-ci.org/JuliaArrays/AxisArrays.jl.svg?branch=master)](https://travis-ci.org/JuliaArrays/AxisArrays.jl) [![Coverage Status](https://coveralls.io/repos/github/JuliaArrays/AxisArrays.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaArrays/AxisArrays.jl?branch=master) +[![Build Status](https://travis-ci.org/JuliaArrays/AxisArrays.jl.svg?branch=master)](https://travis-ci.org/JuliaArrays/AxisArrays.jl) +[![Coverage Status](https://coveralls.io/repos/github/JuliaArrays/AxisArrays.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaArrays/AxisArrays.jl?branch=master) This package for the Julia language provides an array type (the `AxisArray`) that knows about its dimension names and axis values. This allows for indexing with the axis name without incurring any runtime overhead. @@ -29,16 +11,25 @@ In contrast to similar approaches in [Images.jl](https://github.com/timholy/Imag Collaboration is welcome! This is still a work-in-progress. See [the roadmap](https://github.com/JuliaArrays/AxisArrays.jl/issues/7) for the project's current direction. +## Installation +From the Julia REPL, type +```julia-repl +julia> import Pkg; Pkg.add("AxisArrays") +``` + ## Example of currently-implemented behavior: -```julia -julia> Pkg.add("AxisArrays") +```jldoctest index julia> using AxisArrays, Unitful, Random + julia> import Unitful: s, ms, µs -julia> rng = MersenneTwister(123) # Seed a random number generator for repeatable examples -julia> fs = 40000 # Generate a 40kHz noisy signal, with spike-like stuff added for testing -julia> y = randn(rng, 60*fs+1)*3 +julia> rng = MersenneTwister(123); # Seed a random number generator for repeatable examples + +julia> fs = 40000; # Generate a 40kHz noisy signal, with spike-like stuff added for testing + +julia> y = randn(rng, 60*fs+1)*3; + julia> for spk = (sin.(0.8:0.2:8.6) .* [0:0.01:.1; .15:.1:.95; 1:-.05:.05] .* 50, sin.(0.8:0.4:8.6) .* [0:0.02:.1; .15:.1:1; 1:-.2:.1] .* 50) i = rand(rng, round(Int,.001fs):1fs) @@ -49,32 +40,32 @@ julia> for spk = (sin.(0.8:0.2:8.6) .* [0:0.01:.1; .15:.1:.95; 1:-.05:.05] .* 50 end ``` -```jldoctest +```jldoctest index julia> A = AxisArray([y 2y], Axis{:time}(0s:1s/fs:60s), Axis{:chan}([:c1, :c2])) 2-dimensional AxisArray{Float64,2,...} with axes: - :time, 0.0 s:2.5e-5 s:60.0 s - :chan, Symbol[:c1, :c2] -And data, a 2400001×2 Array{Float64,2}: - 3.5708 7.14161 - 6.14454 12.2891 - 3.42795 6.85591 - 1.37825 2.75649 - -1.19004 -2.38007 - -1.99414 -3.98828 - 2.9429 5.88581 - -0.226449 -0.452898 - 0.821446 1.64289 - -0.582687 -1.16537 + :time, (0.0:2.5e-5:60.0) s + :chan, [:c1, :c2] +And data, a 2400001×2 Matrix{Float64}: + 0.970572 1.94114 + -5.70694 -11.4139 + 0.46122 0.92244 + -1.5859 -3.17181 + 2.17776 4.35552 + -5.12439 -10.2488 + 2.79996 5.59992 + 2.10746 4.21493 + -5.5069 -11.0138 + 4.29289 8.58578 ⋮ - -3.50593 -7.01187 - 2.26783 4.53565 - -0.16902 -0.33804 - -3.84852 -7.69703 - 0.226457 0.452914 - 0.560809 1.12162 - 4.67663 9.35326 - -2.41005 -4.8201 - -3.71612 -7.43224 + 2.05448 4.10897 + -5.12668 -10.2534 + -0.215907 -0.431814 + 4.94344 9.88689 + 4.55252 9.10503 + 3.44757 6.89514 + 2.4722 4.9444 + 2.64475 5.2895 + -0.113071 -0.226143 ``` @@ -82,23 +73,23 @@ AxisArrays behave like regular arrays, but they additionally use the axis information to enable all sorts of fancy behaviors. For example, we can specify indices in *any* order, just so long as we annotate them with the axis name: -```jldoctest +```jldoctest index julia> A[Axis{:time}(4)] 1-dimensional AxisArray{Float64,1,...} with axes: - :chan, Symbol[:c1, :c2] -And data, a 2-element Array{Float64,1}: - 1.378246861221241 - 2.756493722442482 + :chan, [:c1, :c2] +And data, a 2-element Vector{Float64}: + -1.5859040701220763 + -3.1718081402441527 julia> A[Axis{:chan}(:c2), Axis{:time}(1:5)] 1-dimensional AxisArray{Float64,1,...} with axes: - :time, 0.0 s:2.5e-5 s:0.0001 s -And data, a 5-element Array{Float64,1}: - 7.141607285917661 - 12.28907824673544 - 6.855905417203194 - 2.756493722442482 - -2.380074475771338 + :time, (0.0:2.5e-5:0.0001) s +And data, a 5-element Vector{Float64}: + 1.9411443944557378 + -11.413887226381497 + 0.9224399858897993 + -3.1718081402441527 + 4.3555157371883935 ``` @@ -107,21 +98,21 @@ selects all values between two endpoints `a .. b` or the axis values directly. Notice that the returned AxisArray still has axis information itself... and it still has the correct time information for those datapoints! -```jldoctest +```jldoctest index julia> A[40µs .. 220µs, :c1] 1-dimensional AxisArray{Float64,1,...} with axes: - :time, 5.0e-5 s:2.5e-5 s:0.0002 s -And data, a 7-element Array{Float64,1}: - 3.427952708601597 - 1.378246861221241 - -1.190037237885669 - -1.994137635575063 - 2.9429034802756004 - -0.22644919919326786 - 0.8214461136364685 + :time, (5.0e-5:2.5e-5:0.0002) s +And data, a 7-element Vector{Float64}: + 0.4612199929448996 + -1.5859040701220763 + 2.1777578685941967 + -5.12439070316173 + 2.7999612778949685 + 2.10746429190499 + -5.506904656367615 julia> AxisArrays.axes(ans, 1) -Axis{:time,StepRangeLen{Unitful.Quantity{Float64,𝐓,Unitful.FreeUnits{(s,),𝐓,nothing}},Base.TwicePrecision{Unitful.Quantity{Float64,𝐓,Unitful.FreeUnits{(s,),𝐓,nothing}}},Base.TwicePrecision{Unitful.Quantity{Float64,𝐓,Unitful.FreeUnits{(s,),𝐓,nothing}}}}}(5.0e-5 s:2.5e-5 s:0.0002 s) +Axis{:time, StepRangeLen{Quantity{Float64, 𝐓, Unitful.FreeUnits{(s,), 𝐓, nothing}}, Base.TwicePrecision{Quantity{Float64, 𝐓, Unitful.FreeUnits{(s,), 𝐓, nothing}}}, Base.TwicePrecision{Quantity{Float64, 𝐓, Unitful.FreeUnits{(s,), 𝐓, nothing}}}, Int64}}((5.0e-5:2.5e-5:0.0002) s) ``` @@ -129,28 +120,28 @@ You can also index by a single value on an axis using `atvalue`. This will drop a dimension. Indexing with an `Interval` type retains dimensions, even when the ends of the interval are equal: -```jldoctest +```jldoctest index julia> A[atvalue(2.5e-5s), :c1] -6.14453912336772 +-5.706943613190749 julia> A[2.5e-5s..2.5e-5s, :c1] 1-dimensional AxisArray{Float64,1,...} with axes: - :time, 2.5e-5 s:2.5e-5 s:2.5e-5 s -And data, a 1-element Array{Float64,1}: - 6.14453912336772 + :time, (2.5e-5:2.5e-5:2.5e-5) s +And data, a 1-element Vector{Float64}: + -5.706943613190749 ``` You can even index by multiple values by broadcasting `atvalue` over an array: -```jldoctest +```jldoctest index julia> A[atvalue.([2.5e-5s, 75.0µs])] 2-dimensional AxisArray{Float64,2,...} with axes: - :time, Unitful.Quantity{Float64,𝐓,Unitful.FreeUnits{(s,),𝐓,nothing}}[2.5e-5 s, 7.5e-5 s] - :chan, Symbol[:c1, :c2] -And data, a 2×2 Array{Float64,2}: - 6.14454 12.2891 - 1.37825 2.75649 + :time, Quantity{Float64, 𝐓, Unitful.FreeUnits{(s,), 𝐓, nothing}}[2.5e-5 s, 7.5e-5 s] + :chan, [:c1, :c2] +And data, a 2×2 Matrix{Float64}: + -5.70694 -11.4139 + -1.5859 -3.17181 ``` @@ -160,18 +151,18 @@ to 220µs) might be more clearly expressed as a symmetrical window about a specific index where we know something interesting happened. To represent this, we use the `atindex` function: -```jldoctest +```jldoctest index julia> A[atindex(-90µs .. 90µs, 5), :c2] 1-dimensional AxisArray{Float64,1,...} with axes: - :time_sub, -7.5e-5 s:2.5e-5 s:7.5e-5 s -And data, a 7-element Array{Float64,1}: - 12.28907824673544 - 6.855905417203194 - 2.756493722442482 - -2.380074475771338 - -3.988275271150126 - 5.885806960551201 - -0.4528983983865357 + :time_sub, (-7.5e-5:2.5e-5:7.500000000000002e-5) s +And data, a 7-element Vector{Float64}: + -11.413887226381497 + 0.9224399858897993 + -3.1718081402441527 + 4.3555157371883935 + -10.24878140632346 + 5.599922555789937 + 4.21492858380998 ``` @@ -180,34 +171,34 @@ interval about the given index! This simple concept can be extended to some very powerful behaviors. For example, let's threshold our data and find windows about those threshold crossings. -```jldoctest +```jldoctest index julia> idxs = findall(diff(A[:,:c1] .< -15) .> 0); julia> spks = A[atindex(-200µs .. 800µs, idxs), :c1] 2-dimensional AxisArray{Float64,2,...} with axes: - :time_sub, -0.0002 s:2.5e-5 s:0.0008 s - :time_rep, Unitful.Quantity{Float64,𝐓,Unitful.FreeUnits{(s,),𝐓,nothing}}[0.161275 s, 0.489925 s, 0.957175 s, 1.1457 s, 1.40185 s, 1.84193 s, 2.07365 s, 2.32947 s, 2.7763 s, 2.79275 s … 57.6724 s, 57.7152 s, 57.749 s, 58.1109 s, 58.3783 s, 58.4178 s, 58.921 s, 59.1693 s, 59.6546 s, 59.7824 s] -And data, a 41×273 Array{Float64,2}: - -2.47171 -1.72242 4.54491 … 2.74969 3.1869 -2.00435 - 6.78576 3.65903 5.14183 -0.98535 3.96603 -5.74065 - 1.56584 1.88131 0.470257 2.66664 5.27674 0.0610194 - 4.78242 3.20142 3.28502 5.20484 -3.66085 1.16247 - 3.23148 -1.24878 -0.0252124 5.46585 4.88651 3.64283 - 6.5714 0.572557 3.038 … -0.974689 2.61297 7.3496 - 4.46643 -0.444754 -4.52857 0.304449 -1.54659 -2.53197 - -9.57806 -1.29114 -2.23969 -9.10793 -6.35711 -5.06038 - -12.2567 -5.06283 -8.53581 -11.9826 -14.868 -14.0543 - -24.5458 -19.9823 -20.0798 -20.3065 -18.5437 -25.3609 - ⋮ ⋱ ⋮ - 2.14059 -0.365031 1.36771 -4.23763 5.9211 -3.84708 - 3.58157 2.87076 0.835568 -2.27752 1.18686 2.3412 - 6.7953 -1.32384 -3.0897 0.464151 -1.12327 -2.14844 - 1.19649 2.44709 -5.16029 … -0.965397 2.37465 -2.36185 - -1.57253 0.526027 0.831144 0.6505 3.61602 1.93462 - 0.739684 -1.74925 -6.18072 -7.36229 -0.187708 1.97774 - 0.645211 1.04006 -1.33676 4.30262 -4.46544 -0.278097 - 1.32901 -1.74821 1.94781 0.780325 3.22951 -0.436806 - 0.387814 0.128453 -0.00287742 … -1.51196 -2.10081 -2.26663 + :time_sub, (-0.0002:2.5e-5:0.0008) s + :time_rep, Quantity{Float64, 𝐓, Unitful.FreeUnits{(s,), 𝐓, nothing}}[0.20055 s, 0.59915 s, 0.650875 s, 0.70895 s, 1.263325 s, 1.389425 s, 1.428375 s, 1.43655 s, 2.032475 s, 2.33395 s … 57.9839 s, 58.406025 s, 58.74015 s, 58.838825 s, 59.059425 s, 59.125575 s, 59.166875 s, 59.2176 s, 59.380075 s, 59.876725 s] +And data, a 41×245 Matrix{Float64}: + 2.86123 8.26514 2.14515 … -1.41962 -0.413538 0.993419 + -1.8017 3.12209 0.813224 -0.64579 2.85579 2.90282 + 1.46148 3.81187 0.347986 3.01351 1.13241 2.248 + 8.81351 -0.708326 1.25017 1.07955 3.53002 -2.37983 + 4.68316 8.03222 1.61818 4.41109 -2.1597 -2.92392 + 3.04843 2.27116 4.77934 … -0.101335 2.23039 3.14383 + -5.72907 5.21333 -8.02832 -0.317973 -4.00314 -1.21328 + -2.90782 -7.01524 -12.6477 -5.24058 -10.0462 -0.43427 + -11.8745 -8.11785 -11.6229 -13.7435 -13.045 -5.46208 + -18.2097 -17.9164 -21.7609 -26.3422 -19.0823 -16.9439 + ⋮ ⋱ + 9.2757 5.04953 0.334538 0.174179 2.18744 4.68741 + 3.32367 0.772247 3.98974 -1.20394 6.08171 7.80668 + -8.12102 4.83328 2.07419 0.509321 -0.0284023 -3.71894 + -6.20857 1.68384 0.525361 … -0.238838 -1.54597 -4.42312 + -4.07384 -2.05483 -0.858261 -0.14345 -0.282987 -1.4149 + -3.30074 1.96526 1.23548 -0.146952 2.57137 0.230237 + 2.12521 -1.13537 7.22253 -0.235542 -4.34315 5.48822 + 1.77073 3.18589 1.61067 0.532888 -3.33085 0.522522 + 4.54472 1.73379 -4.65332 … 5.93919 1.16357 0.386667 ``` diff --git a/docs/src/reference.md b/docs/src/reference.md index 2f91218..9dd3e60 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -1,3 +1,5 @@ +# API Reference + ```@autodocs -Modules = [AxisArrays] +Modules = [AxisArrays, AxisArrays.Extrapolated] ``` diff --git a/src/combine.jl b/src/combine.jl index c172c8d..e735104 100644 --- a/src/combine.jl +++ b/src/combine.jl @@ -117,10 +117,10 @@ end #merge Combines AxisArrays with matching axis names into a single AxisArray. Unlike `merge`, the inputs are joined along a newly created axis (optionally specified with the `newaxis` keyword argument). The `method` keyword argument can be used to specify the join type: -`:inner` - keep only those array values at axis values common to all AxisArrays to be joined -`:left` - keep only those array values at axis values present in the first AxisArray passed -`:right` - keep only those array values at axis values present in the last AxisArray passed -`:outer` (default) - keep all array values: create an AxisArray spanning all of the input axis values +- `:inner` - keep only those array values at axis values common to all AxisArrays to be joined +- `:left` - keep only those array values at axis values present in the first AxisArray passed +- `:right` - keep only those array values at axis values present in the last AxisArray passed +- `:outer` (default) - keep all array values: create an AxisArray spanning all of the input axis values If an array value in the output array is not defined in any of the input arrays (i.e. in the case of a left, right, or outer join), it takes the value of the optional `fillvalue` keyword argument (default zero). """ @@ -218,11 +218,11 @@ axis of type `Axis{:collapsed, CategoricalVector{Tuple}}`. ### Examples -``` +```julia-repl julia> price_data = AxisArray(rand(10), Axis{:time}(Date(2016,01,01):Day(1):Date(2016,01,10))) 1-dimensional AxisArray{Float64,1,...} with axes: - :time, 2016-01-01:1 day:2016-01-10 -And data, a 10-element Array{Float64,1}: + :time, Date("2016-01-01"):Day(1):Date("2016-01-10") +And data, a 10-element Vector{Float64}: 0.885014 0.418562 0.609344 @@ -236,9 +236,9 @@ And data, a 10-element Array{Float64,1}: julia> size_data = AxisArray(rand(10,2), Axis{:time}(Date(2016,01,01):Day(1):Date(2016,01,10)), Axis{:measure}([:area, :volume])) 2-dimensional AxisArray{Float64,2,...} with axes: - :time, 2016-01-01:1 day:2016-01-10 - :measure, Symbol[:area, :volume] -And data, a 10×2 Array{Float64,2}: + :time, Date("2016-01-01"):Day(1):Date("2016-01-10") + :measure, [:area, :volume] +And data, a 10×2 Matrix{Float64}: 0.159434 0.456992 0.344521 0.374623 0.522077 0.313256 @@ -252,9 +252,9 @@ And data, a 10×2 Array{Float64,2}: julia> collapsed = collapse(Val(1), (:price, :size), price_data, size_data) 2-dimensional AxisArray{Float64,2,...} with axes: - :time, 2016-01-01:1 day:2016-01-10 + :time, Date("2016-01-01"):Day(1):Date("2016-01-10") :collapsed, Tuple{Symbol,Vararg{Symbol,N} where N}[(:price,), (:size, :area), (:size, :volume)] -And data, a 10×3 Array{Float64,2}: +And data, a 10×3 Matrix{Float64}: 0.885014 0.159434 0.456992 0.418562 0.344521 0.374623 0.609344 0.522077 0.313256