{"id":19093704,"url":"https://github.com/juliaarrays/axisarrays.jl","last_synced_at":"2025-07-20T13:33:38.001Z","repository":{"id":27261427,"uuid":"30733953","full_name":"JuliaArrays/AxisArrays.jl","owner":"JuliaArrays","description":"Performant arrays where each dimension can have a named axis with values","archived":false,"fork":false,"pushed_at":"2024-11-18T02:09:44.000Z","size":1095,"stargazers_count":201,"open_issues_count":70,"forks_count":43,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-07-05T16:11:33.010Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://JuliaArrays.github.io/AxisArrays.jl/latest/","language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JuliaArrays.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-02-13T01:08:29.000Z","updated_at":"2025-07-03T12:52:34.000Z","dependencies_parsed_at":"2024-01-12T19:48:24.050Z","dependency_job_id":"97a3b95f-5f30-4744-8d94-83532c8fd971","html_url":"https://github.com/JuliaArrays/AxisArrays.jl","commit_stats":{"total_commits":251,"total_committers":33,"mean_commits":7.606060606060606,"dds":0.6254980079681275,"last_synced_commit":"19a548e6970d1b67f96a6de441d49e923a7d013b"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/JuliaArrays/AxisArrays.jl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaArrays%2FAxisArrays.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaArrays%2FAxisArrays.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaArrays%2FAxisArrays.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaArrays%2FAxisArrays.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JuliaArrays","download_url":"https://codeload.github.com/JuliaArrays/AxisArrays.jl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaArrays%2FAxisArrays.jl/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266135014,"owners_count":23881774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T03:25:46.775Z","updated_at":"2025-07-20T13:33:37.983Z","avatar_url":"https://github.com/JuliaArrays.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AxisArrays.jl\n\n[![][action-img]][action-url]\n[![][pkgeval-img]][pkgeval-url]\n[![][codecov-img]][codecov-url]\n\nThis package for the Julia language provides an array type (the `AxisArray`) that knows about its dimension names and axis values.\nThis allows for indexing by name without incurring any runtime overhead.\nThis permits one to implement algorithms that are oblivious to the storage order of the underlying arrays.\nAxisArrays can also be indexed by the values along their axes, allowing column names or interval selections.\n\nIn contrast to similar approaches in [Images.jl](https://github.com/timholy/Images.jl) and [NamedArrays.jl](https://github.com/davidavdav/NamedArrays), this allows for type-stable selection of dimensions and compile-time axis lookup.  It is also better suited for regularly sampled axes, like samples over time.\n\nCollaboration 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.\n\n### Note about `Axis{}` and keywords\n\nAn `AxisArray` stores an object of type `Axis{:name}` for each dimension, \ncontaining both the name (a `Symbol`) and the \"axis values\" (an `AbstractVector`).\nThese types are what made compile-time lookup possible.\nInstead of providing them explicitly, it is now possible to use keyword arguments\nfor both construction and indexing:\n\n```julia\nV = AxisArray(rand(10); row='a':'j')  # AxisArray(rand(10), Axis{:row}('a':'j'))\nV[row='c'] == V[Axis{:row}('c')] == V[row=3] == V[3] \n```\n\n### Note about `axes()` and `indices()`\n\nThe function `AxisArrays.axes` returns the tuple of such `Axis` objects. \nSince Julia version 0.7, `Base.axes(V) == (1:10,)` gives instead the range of possible \nordinary integer indices. (This was called `Base.indices`.) Since both names are exported, \nthis collision results in a warning if you try to use `axes` without qualification:\n\n```julia\njulia\u003e axes([1,2])\nWARNING: both AxisArrays and Base export \"axes\"; uses of it in module Main must be qualified\nERROR: UndefVarError: axes not defined\n```\n\nPackages that are upgrading to support Julia 0.7+ should:\n\n* Replace all uses of the `axes` function with the fully-qualified `AxisArrays.axes`\n* Replace all uses of the deprecated `indices` function with the un-qualified `axes`\n* Immediately after `using AxisArrays`, add `const axes = Base.axes`\n\nIn the future, AxisArrays will be looking for a new name for its functionality.\nThis will allow you to use the idiomatic `Base` name and offers an easy upgrade\npath to whatever the new name will be.\n\n## Example of currently-implemented behavior:\n\n```julia\njulia\u003e using Pkg; pkg\"add AxisArrays Unitful\"\njulia\u003e using AxisArrays, Unitful, Random\n\njulia\u003e fs = 40000; # Generate a 40kHz noisy signal, with spike-like stuff added for testing\njulia\u003e import Unitful: s, ms, µs\njulia\u003e rng = Random.MersenneTwister(123); # Seed a random number generator for repeatable examples\njulia\u003e y = randn(rng, 60*fs+1)*3;\njulia\u003e for spk = (sin.(0.8:0.2:8.6) .* [0:0.01:.1; .15:.1:.95; 1:-.05:.05] .* 50,\n                  sin.(0.8:0.4:8.6) .* [0:0.02:.1; .15:.1:1; 1:-.2:.1] .* 50)\n           i = rand(rng, round(Int,.001fs):1fs)\n           while i+length(spk)-1 \u003c length(y)\n               y[i:i+length(spk)-1] += spk\n               i += rand(rng, round(Int,.001fs):1fs)\n           end\n       end\n\njulia\u003e A = AxisArray(hcat(y, 2 .* y); time = (0s:1s/fs:60s), chan = ([:c1, :c2]))\n2-dimensional AxisArray{Float64,2,...} with axes:\n    :time, 0.0 s:2.5e-5 s:60.0 s\n    :chan, Symbol[:c1, :c2]\nAnd data, a 2400001×2 Array{Float64,2}:\n  3.5708     7.14161\n  6.14454   12.2891  \n  3.42795    6.85591\n  1.37825    2.75649\n -1.19004   -2.38007\n -1.99414   -3.98828\n  2.9429     5.88581\n -0.226449  -0.452898\n  0.821446   1.64289\n -0.582687  -1.16537\n  ⋮                  \n -3.50593   -7.01187\n  2.26783    4.53565\n -0.16902   -0.33804\n -3.84852   -7.69703\n  0.226457   0.452914\n  0.560809   1.12162\n  4.67663    9.35326\n -2.41005   -4.8201  \n -3.71612   -7.43224\n```\n\nAxisArrays behave like regular arrays, but they additionally use the axis\ninformation to enable all sorts of fancy behaviors. For example, we can specify\nindices in *any* order, just so long as we annotate them with the axis name:\n\n```julia\njulia\u003e A[time=4] # or A[Axis{:time}(4)]\n1-dimensional AxisArray{Float64,1,...} with axes:\n    :chan, Symbol[:c1, :c2]\nAnd data, a 2-element Array{Float64,1}:\n 1.37825\n 2.75649\n\njulia\u003e A[chan = :c2, time = 1:5] # or A[Axis{:chan}(:c2), Axis{:time}(1:5)]\n1-dimensional AxisArray{Float64,1,...} with axes:\n    :time, 0.0 s:2.5e-5 s:0.0001 s\nAnd data, a 5-element Array{Float64,1}:\n  7.14161\n 12.2891\n  6.85591\n  2.75649\n -2.38007\n```\n\nWe can also index by the *values* of each axis using an `Interval` type that\nselects all values between two endpoints `a .. b` or the axis values directly.\nNotice that the returned AxisArray still has axis information itself... and it\nstill has the correct time information for those datapoints!\n\n```julia\njulia\u003e A[40µs .. 220µs, :c1]\n1-dimensional AxisArray{Float64,1,...} with axes:\n    :time, 5.0e-5 s:2.5e-5 s:0.0002 s\nAnd data, a 7-element Array{Float64,1}:\n  3.42795\n  1.37825\n -1.19004\n -1.99414\n  2.9429  \n -0.226449\n  0.821446\n\njulia\u003e AxisArrays.axes(ans, 1)\nAxisArrays.Axis{:time,StepRangeLen{Quantity{Float64, Dimensions:{𝐓}, Units:{s}},Base.TwicePrecision{Quantity{Float64, Dimensions:{𝐓}, Units:{s}}},Base.TwicePrecision{Quantity{Float64, Dimensions:{𝐓}, Units:{s}}}}}(5.0e-5 s:2.5e-5 s:0.0002 s)\n```\n\nYou can also index by a single value using `atvalue(t)`. \nThis function is not needed for categorical axes like `:chan` here, \nas `:c1` is a `Symbol` which can't be confused with an integer index.\n\nUsing `atvalue()` will drop a dimension (like using a single integer). \nIndexing with an `Interval(lo, hi)` type retains dimensions, even\nwhen the ends of the interval are equal (like using a range `1:1`):\n\n```julia\njulia\u003e A[atvalue(2.5e-5s), :c1]\n6.14453912336772\n\njulia\u003e A[2.5e-5s..2.5e-5s, :c1]\n1-dimensional AxisArray{Float64,1,...} with axes:\n    :time, 2.5e-5 s:2.5e-5 s:2.5e-5 s\nAnd data, a 1-element Array{Float64,1}:\n 6.14454\n```\n\nYou can even index by multiple values by broadcasting `atvalue` over an array:\n\n```julia\njulia\u003e A[atvalue.([2.5e-5s, 75.0µs])]\n2-dimensional AxisArray{Float64,2,...} with axes:\n    :time, Quantity{Float64, Dimensions:{𝐓}, Units:{s}}[2.5e-5 s, 7.5e-5 s]\n    :chan, Symbol[:c1, :c2]\nAnd data, a 2×2 Array{Float64,2}:\n 6.14454  12.2891\n 1.37825   2.75649\n```\n\nSometimes, though, what we're really interested in is a window of time about a\nspecific index. One of the operations above (looking for values in the window from 40µs\nto 220µs) might be more clearly expressed as a symmetrical window about a\nspecific index where we know something interesting happened. To represent this,\nwe use the `atindex` function:\n\n```julia\njulia\u003e A[atindex(-90µs .. 90µs, 5), :c2]\n1-dimensional AxisArray{Float64,1,...} with axes:\n    :time_sub, -7.5e-5 s:2.5e-5 s:7.500000000000002e-5 s\nAnd data, a 7-element Array{Float64,1}:\n  6.85591\n  2.75649\n -2.38007\n -3.98828\n  5.88581\n -0.452898\n  1.64289\n```\n\nNote that the returned AxisArray has its time axis shifted to represent the\ninterval about the given index!  This simple concept can be extended to some\nvery powerful behaviors. For example, let's threshold our data and find windows\nabout those threshold crossings.\n\n```julia\njulia\u003e idxs = findall(diff(A[:,:c1] .\u003c -15) .\u003e 0);\n\njulia\u003e spks = A[atindex(-200µs .. 800µs, idxs), :c1]\n2-dimensional AxisArray{Float64,2,...} with axes:\n    :time_sub, -0.0002 s:2.5e-5 s:0.0008 s\n    :time_rep, Quantity{Float64, Dimensions:{𝐓}, Units:{s}}[0.162 s, 0.20045 s, 0.28495 s, 0.530325 s, 0.821725 s, 1.0453 s, 1.11967 s, 1.1523 s, 1.22085 s, 1.6253 s  …  57.0094 s, 57.5818 s, 57.8716 s, 57.8806 s, 58.4353 s, 58.7041 s, 59.1015 s, 59.1783 s, 59.425 s, 59.5657 s]\nAnd data, a 41×247 Array{Float64,2}:\n   0.672063    7.25649      0.633375  …    1.54583     5.81194    -4.706\n  -1.65182     2.57487      0.477408       3.09505     3.52478     4.13037\n   4.46035     2.11313      4.78372        1.23385     7.2525      3.57485\n   5.25651    -2.19785      3.05933        0.965021    6.78414     5.94854\n   7.8537      0.345008     0.960533       0.812989    0.336715    0.303909\n   0.466816    0.643649    -3.67087   …    3.92978    -3.1242      0.789722\n  -6.0445    -13.2441      -4.60716        0.265144   -4.50987    -8.84897\n  -9.21703   -13.2254     -14.4409        -8.6664    -13.3457    -11.6213\n -16.1809    -22.7037     -25.023        -15.9376    -28.0817    -16.996\n -23.2671    -31.2021     -25.3787       -24.4914    -32.2599    -26.1118\n   ⋮                                  ⋱                ⋮\n  -0.301629    0.0683982   -4.36574        1.92362    -5.12333    -3.4431\n   4.7182      1.18615      4.40717       -4.51757    -8.64314     0.0800021\n  -2.43775    -0.151882    -1.40817       -3.38555    -2.23418     0.728549\n   3.2482     -0.60967      0.471288  …    2.53395     0.468817   -3.65905\n  -4.26967     2.24747     -3.13758        1.74967     4.5052     -0.145357\n  -0.752487    1.69446     -1.20491        1.71429     1.81936     0.290158\n   4.64348    -3.94187     -1.59213        7.15428    -0.539748    4.82309\n   1.09652    -2.66999      0.521931      -3.80528     1.70421     3.40583\n  -0.94341     2.60785     -3.34291   …    1.10584     4.31118     3.6404\n```\n\nBy indexing with a repeated interval, we have *added* a dimension to the\noutput! The returned AxisArray's columns specify each repetition of the\ninterval, and each datapoint in the column represents a timepoint within that\ninterval, adjusted by the time of the theshold crossing. The best part here\nis that the returned matrix knows precisely where its data came from, and has\nlabeled its dimensions appropriately. Not only is there the proper time\nbase for each waveform, but we also have recorded the event times as the axis\nacross the columns.\n\n## Indexing\n\nTwo main types of Axes supported by default include:\n\n* Categorical axis -- These are vectors of labels, normally symbols or\n  strings. Elements or slices can be selected by elements or vectors\n  of elements.\n\n* Dimensional axis -- These are sorted vectors or iterators that can\n  be selected by `Intervals`. These are commonly used for sequences of\n  times or date-times. For regular sample rates, ranges can be used.\n\nHere is an example with a Dimensional axis representing a time\nsequence along rows and a Categorical axis of symbols for column\nheaders.\n\n```julia\nB = AxisArray(reshape(1:15, 5, 3), .1:.1:0.5, [:a, :b, :c])\nB[row = (0.2..0.4)] # restrict the AxisArray along the time axis\nB[0.0..0.3, [:a, :c]]   # select an interval and two of the columns\n```\n\nUser-defined axis types can be added along with custom indexing\nbehaviors.\n\n### Example: compute the intensity-weighted mean along the z axis\n```julia\nB = AxisArray(randn(100,100,100), :x, :y, :z)\nItotal = sumz = 0.0\nfor iter in CartesianIndices(Base.axes(B))  # traverses in storage order for cache efficiency\n    global Itotal, sumz\n    I = B[iter]  # intensity in a single voxel\n    Itotal += I\n    sumz += I * iter[axisdim(B, Axis{:z})]  # axisdim \"looks up\" the z dimension\nend\nmeanz = sumz/Itotal\n```\n\nThe intention is that all of these operations are just as efficient as they would be if you used traditional position-based indexing with all the inherent assumptions about the storage order of `B`.\n\n\n\u003c!-- badges --\u003e\n\n[pkgeval-img]: https://juliaci.github.io/NanosoldierReports/pkgeval_badges/A/AxisArrays.svg\n[pkgeval-url]: https://juliaci.github.io/NanosoldierReports/pkgeval_badges/report.html\n\n[action-img]: https://github.com/JuliaArrays/AxisArrays.jl/workflows/Unit%20test/badge.svg\n[action-url]: https://github.com/JuliaArrays/AxisArrays.jl/actions\n\n[codecov-img]: https://codecov.io/github/JuliaArrays/AxisArrays.jl/coverage.svg?branch=master\n[codecov-url]: https://codecov.io/gh/JuliaArrays/AxisArrays.jl\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliaarrays%2Faxisarrays.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuliaarrays%2Faxisarrays.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliaarrays%2Faxisarrays.jl/lists"}