@@ -17,76 +17,85 @@ julia> Pkg.add("AxisArrays")
17
17
using AxisArrays, Unitful
18
18
import Unitful: s, ms, µs
19
19
20
- julia> fs = 40000 # Generate a 40kHz noisy signal, with spike-like stuff added for testing
21
- y = randn (60 * fs+ 1 )* 3
20
+ julia> rng = MersenneTwister (123 ) # Seed a random number generator for repeatable examples
21
+ fs = 40000 # Generate a 40kHz noisy signal, with spike-like stuff added for testing
22
+ y = randn (rng, 60 * fs+ 1 )* 3
22
23
for spk = (sin .(0.8 : 0.2 : 8.6 ) .* [0 : 0.01 : .1 ; .15 : .1 : .95 ; 1 : - .05 : .05 ] .* 50 ,
23
24
sin .(0.8 : 0.4 : 8.6 ) .* [0 : 0.02 : .1 ; .15 : .1 : 1 ; 1 : - .2 : .1 ] .* 50 )
24
- i = rand (round (Int,.001 fs): 1 fs)
25
+ i = rand (rng, round (Int,.001 fs): 1 fs)
25
26
while i+ length (spk)- 1 < length (y)
26
27
y[i: i+ length (spk)- 1 ] += spk
27
- i += rand (round (Int,.001 fs): 1 fs)
28
+ i += rand (rng, round (Int,.001 fs): 1 fs)
28
29
end
29
30
end
30
31
31
32
julia> A = AxisArray ([y 2 y], Axis {:time} (0 s: 1 s/ fs: 60 s), Axis {:chan} ([:c1 , :c2 ]))
32
33
2 - dimensional AxisArray{Float64,2 ,... } with axes:
33
34
:time , 0.0 s: 2.5e-5 s: 60.0 s
34
- :chan , [:c1 ,:c2 ]
35
- And data, a 2400001 x2 Array{Float64,2 }:
36
- - 3.06091 - 6.12181
37
- 0.152334 0.304668
38
- 7.86831 15.7366
39
- - 1.4144 - 2.82879
40
- - 2.02881 - 4.05763
41
- 9.87901 19.758
42
- ⋮
43
- - 0.0254444 - 0.0508888
44
- 0.204358 0.408717
45
- - 4.80093 - 9.60186
46
- 5.39751 10.795
47
- 0.976276 1.95255
48
- 0.336558 0.673116
35
+ :chan , Symbol[:c1 , :c2 ]
36
+ And data, a 2400001 × 2 Array{Float64,2 }:
37
+ 3.5708 7.14161
38
+ 6.14454 12.2891
39
+ 3.42795 6.85591
40
+ 1.37825 2.75649
41
+ - 1.19004 - 2.38007
42
+ - 1.99414 - 3.98828
43
+ 2.9429 5.88581
44
+ - 0.226449 - 0.452898
45
+ 0.821446 1.64289
46
+ - 0.582687 - 1.16537
47
+ ⋮
48
+ - 3.50593 - 7.01187
49
+ 2.26783 4.53565
50
+ - 0.16902 - 0.33804
51
+ - 3.84852 - 7.69703
52
+ 0.226457 0.452914
53
+ 0.560809 1.12162
54
+ 4.67663 9.35326
55
+ - 2.41005 - 4.8201
56
+ - 3.71612 - 7.43224
49
57
```
50
58
51
59
AxisArrays behave like regular arrays, but they additionally use the axis
52
60
information to enable all sorts of fancy behaviors. For example, we can specify
53
61
indices in * any* order, just so long as we annotate them with the axis name:
54
62
55
- ``` jl
63
+ ``` julia
56
64
julia> A[Axis {:time} (4 )]
57
- 2 - dimensional AxisArray{Float64,1 ,... } with axes:
58
- :chan , Symbol[:c1 ,:c2 ]
65
+ 1 - dimensional AxisArray{Float64,1 ,... } with axes:
66
+ :chan , Symbol[:c1 , :c2 ]
59
67
And data, a 2 - element Array{Float64,1 }:
60
- - 1.4144 - 2.82879
68
+ 1.37825
69
+ 2.75649
61
70
62
71
julia> A[Axis {:chan} (:c2 ), Axis {:time} (1 : 5 )]
63
72
1 - dimensional AxisArray{Float64,1 ,... } with axes:
64
73
:time , 0.0 s: 2.5e-5 s: 0.0001 s
65
74
And data, a 5 - element Array{Float64,1 }:
66
- - 6.12181
67
- 0.304668
68
- 15.7366
69
- - 2.82879
70
- - 4.05763
75
+ 7.14161
76
+ 12.2891
77
+ 6.85591
78
+ 2.75649
79
+ - 2.38007
71
80
```
72
81
73
82
We can also index by the * values* of each axis using an ` Interval ` type that
74
83
selects all values between two endpoints ` a .. b ` or the axis values directly.
75
84
Notice that the returned AxisArray still has axis information itself... and it
76
85
still has the correct time information for those datapoints!
77
86
78
- ``` jl
87
+ ``` julia
79
88
julia> A[40 µs .. 220 µs, :c1 ]
80
89
1 - dimensional AxisArray{Float64,1 ,... } with axes:
81
90
:time , 5.0e-5 s: 2.5e-5 s: 0.0002 s
82
91
And data, a 7 - element Array{Float64,1 }:
83
- 7.86831
84
- - 1.4144
85
- - 2.02881
86
- 9.87901
87
- 0.463201
88
- 2.49211
89
- - 1.97716
92
+ 3.42795
93
+ 1.37825
94
+ - 1.19004
95
+ - 1.99414
96
+ 2.9429
97
+ - 0.226449
98
+ 0.821446
90
99
91
100
julia> axes (ans, 1 )
92
101
AxisArrays. 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)
@@ -96,15 +105,15 @@ You can also index by a single value on an axis using `atvalue`. This will drop
96
105
a dimension. Indexing with an ` Interval ` type retains dimensions, even
97
106
when the ends of the interval are equal:
98
107
99
- ``` jl
108
+ ``` julia
100
109
julia> A[atvalue (2.5e-5 s), :c1 ]
101
- 0.152334
110
+ 6.14453912336772
102
111
103
112
julia> A[2.5e-5 s.. 2.5e-5 s, :c1 ]
104
113
1 - dimensional AxisArray{Float64,1 ,... } with axes:
105
114
:time , 2.5e-5 s: 2.5e-5 s: 2.5e-5 s
106
115
And data, a 1 - element Array{Float64,1 }:
107
- 0.152334
116
+ 6.14454
108
117
```
109
118
110
119
Sometimes, though, what we're really interested in is a window of time about a
@@ -113,47 +122,53 @@ to 220µs) might be more clearly expressed as a symmetrical window about a
113
122
specific index where we know something interesting happened. To represent this,
114
123
we use the ` atindex ` function:
115
124
116
- ``` jl
125
+ ``` julia
117
126
julia> A[atindex (- 90 µs .. 90 µs, 5 ), :c2 ]
118
127
1 - dimensional AxisArray{Float64,1 ,... } with axes:
119
- :time_sub , - 7.5e-5 s: 2.5e-5 s: 7.5e -5 s
120
- And data, a 7 - element SubArray{Float64, 1 , Array{Float64,2 },Tuple{AxisArrays . AxisArray{Int64, 1 ,UnitRange{Int64},Tuple{AxisArrays . Axis{ :sub ,SIUnits . SIRange{FloatRange{Float64},Float64, 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 }}}},Int64}, 0 }:
121
- 15.7366
122
- - 2.82879
123
- - 4.05763
124
- 19.758
125
- 0.926402
126
- 4.98423
127
- - 3.95433
128
+ :time_sub , - 7.5e-5 s: 2.5e-5 s: 7.500000000000002e -5 s
129
+ And data, a 7 - element Array{Float64,1 }:
130
+ 6.85591
131
+ 2.75649
132
+ - 2.38007
133
+ - 3.98828
134
+ 5.88581
135
+ - 0.452898
136
+ 1.64289
128
137
```
129
138
130
139
Note that the returned AxisArray has its time axis shifted to represent the
131
140
interval about the given index! This simple concept can be extended to some
132
141
very powerful behaviors. For example, let's threshold our data and find windows
133
142
about those threshold crossings.
134
143
135
- ``` jl
136
- julia> idxs = find (diff (A[:,:c1 ] .< - 15 ) .> 0 )
137
- 242 - element Array{Int64,1 }: ...
144
+ ``` julia
145
+ julia> idxs = find (diff (A[:,:c1 ] .< - 15 ) .> 0 );
138
146
139
147
julia> spks = A[atindex (- 200 µs .. 800 µs, idxs), :c1 ]
140
148
2 - dimensional AxisArray{Float64,2 ,... } with axes:
141
- :time_sub , - 0.000175 s: 2.5e-5 s: 0.000775 s
142
- :time_rep , Quantity{Float64, Dimensions: {𝐓}, Units: {s}}[0.178725 s,0.806825 s,0.88305 s,1.47485 s,1.50465 s,1.53805 s,1.541025 s,2.16365 s,2.368425 s,2.739 s … 57.797925 s,57.924075 s,58.06075 s,58.215125 s,58.6403 s,58.96215 s,58.990225 s,59.001325 s,59.48395 s,59.611525 s]
143
- And data, a 39 x242 Array{Float64,2 }:
144
- - 1.53038 4.72882 5.8706 … - 0.231564 0.624714 3.44076
145
- - 2.24961 2.12414 5.69936 7.00179 2.30993 5.20432
146
- 5.96311 3.9713 - 4.38335 1.32617 - 0.686648 0.443454
147
- 3.86592 5.7466 2.32469 1.30803 3.44585 1.17781
148
- 3.56837 - 3.32178 1.16106 - 3.91796 2.41779 - 6.17495
149
- - 9.52063 - 2.07014 - 1.18463 … - 3.55719 2.23117 1.76089
150
- ⋮ ⋱ ⋮
151
- 3.51708 - 1.63627 0.281915 - 2.41759 3.39403 0.101004
152
- 0.0421772 - 2.13557 - 4.71965 0.066912 3.25141 - 0.445574
153
- 3.53238 - 3.72221 1.68314 … - 4.15147 - 5.25241 - 1.77557
154
- - 4.38307 1.38275 - 1.33641 3.40342 0.272826 - 3.22013
155
- 2.54846 - 0.0194032 2.58679 - 0.000676503 - 2.71147 - 0.288483
156
- 0.260694 - 4.1724 - 0.111377 3.283 1.77147 - 0.367888
149
+ :time_sub , - 0.0002 s: 2.5e-5 s: 0.0008 s
150
+ :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]
151
+ And data, a 41 × 247 Array{Float64,2 }:
152
+ 0.672063 7.25649 0.633375 … 1.54583 5.81194 - 4.706
153
+ - 1.65182 2.57487 0.477408 3.09505 3.52478 4.13037
154
+ 4.46035 2.11313 4.78372 1.23385 7.2525 3.57485
155
+ 5.25651 - 2.19785 3.05933 0.965021 6.78414 5.94854
156
+ 7.8537 0.345008 0.960533 0.812989 0.336715 0.303909
157
+ 0.466816 0.643649 - 3.67087 … 3.92978 - 3.1242 0.789722
158
+ - 6.0445 - 13.2441 - 4.60716 0.265144 - 4.50987 - 8.84897
159
+ - 9.21703 - 13.2254 - 14.4409 - 8.6664 - 13.3457 - 11.6213
160
+ - 16.1809 - 22.7037 - 25.023 - 15.9376 - 28.0817 - 16.996
161
+ - 23.2671 - 31.2021 - 25.3787 - 24.4914 - 32.2599 - 26.1118
162
+ ⋮ ⋱ ⋮
163
+ - 0.301629 0.0683982 - 4.36574 1.92362 - 5.12333 - 3.4431
164
+ 4.7182 1.18615 4.40717 - 4.51757 - 8.64314 0.0800021
165
+ - 2.43775 - 0.151882 - 1.40817 - 3.38555 - 2.23418 0.728549
166
+ 3.2482 - 0.60967 0.471288 … 2.53395 0.468817 - 3.65905
167
+ - 4.26967 2.24747 - 3.13758 1.74967 4.5052 - 0.145357
168
+ - 0.752487 1.69446 - 1.20491 1.71429 1.81936 0.290158
169
+ 4.64348 - 3.94187 - 1.59213 7.15428 - 0.539748 4.82309
170
+ 1.09652 - 2.66999 0.521931 - 3.80528 1.70421 3.40583
171
+ - 0.94341 2.60785 - 3.34291 … 1.10584 4.31118 3.6404
157
172
```
158
173
159
174
By indexing with a repeated interval, we have * added* a dimension to the
@@ -165,21 +180,6 @@ labeled its dimensions appropriately. Not only is there the proper time
165
180
base for each waveform, but we also have recorded the event times as the axis
166
181
across the columns.
167
182
168
- Now we can do a cursory clustering analysis on these spike snippets to separate
169
- the two "neurons" back out into their own groups with Clustering.jl, and plot
170
- using Gadfly.
171
-
172
- ``` jl
173
- julia> using Clustering
174
- Ks = Clustering. kmeans (spks. data, 2 );
175
-
176
- julia> using Gadfly
177
- plot (spks, x= :time_sub , y= :data , group= :time_rep , color= DataFrames. RepeatedVector (Ks. assignments, size (spks, 1 ), 1 ), Geom. line)
178
- ```
179
-
180
- ![ clustered spike snippets] ( docs/spikes.png )
181
-
182
-
183
183
## Indexing
184
184
185
185
### Indexing axes
0 commit comments