Recurrence Motifs Probability Distributions
RecurrenceMicrostatesAnalysis.jl aims to be a user-friendly library with powerful capabilities. It can be used through simple function calls or more advanced configurations that offer greater control. We will begin with the simpler usage, explaining its arguments and settings, and gradually move toward more complex configurations throughout this discussion.
One-dimensional data
This section presents a run similar to the one shown on the quick start page, but with a more detailed explanation. For one-dimensional problems, such as the logistic map or the generalized Bernoulli shift map (Beta-X), you can use a vector of positions along the trajectory as input. To illustrate this, let's consider a uniform distribution:
julia> using Distributionsjulia> data = rand(Uniform(0, 1), 3000)3000-element Vector{Float64}: 0.8104600113469482 0.1276176262235338 0.14836882363089365 0.23434818346518915 0.8415741226119569 0.25932221898621255 0.6577582542734445 0.5756181502107953 0.9346628068339397 0.14704003260372311 ⋮ 0.1074390093033023 0.6264028589699819 0.1574026234707676 0.6901945332825439 0.02231746877693519 0.7472283930985045 0.29396077700923584 0.19084437273337718 0.9606713585071278
Computing the recurrence motif distribution is straightforward once the threshold and n (motif size) parameters are defined. A good value for threshold can be estimated using the find_parameters function, which we recommend using in most cases.
julia> using RecurrenceMicrostatesAnalysisjulia> th, s = find_parameters(data, 3)(0.27180754859344664, 5.750455563776671)julia> dist = distribution(data, th, 3)512-element Vector{Float64}: 0.016426346239430352 0.002578994214508233 0.0025189141076991544 0.003242100578549177 0.0027481085892300844 0.0029728526924788605 0.0029795282599020916 0.012182910547396529 0.002621272808188696 0.00338006230529595 ⋮ 0.0019737427681352916 0.010402759234534936 0.0017868268802848242 0.0017156208277703605 0.0019403649310191365 0.0016755674232309746 0.001880284824210058 0.0018958611481975968 0.010146862483311082
We do not recommend the use of find_parameters inside a loop, as it needs to compute several distributions to find the threshold value that maximizes recurrence entropy, which can significantly reduce the library's performance. For this reason, we have not created an overload of the distribution function that automatically calculates the threshold. Instead, we suggest using an average threshold value computed from a few representative snippets of your dataset using the find_parameters function.
The distribution function includes several keyword arguments for configuration. Before moving on to the next section, we will discuss these arguments, as they apply to every call of the distribution function.
Motif constrained shape
There are variations in motif constraint shapes proposed in the literature, such as the triangular motif. Supporting these shape generalizations is one of the goals of RecurrenceMicrostatesAnalysis.jl, and it is also a computational challenge. Adapting the conversion of motifs with a generic shape from a binary structure to a decimal value can be a very complex problem, and to support this in the library, we need to adapt the pipeline that converts a motif for each specific shape.
Currently, RecurrenceMicrostatesAnalysis.jl supports five shapes: square, triangle, diagonal, line, and pair. The way the library converts these motifs constrained shapes to decimal values is detailed on the motifs page. You can change the shape using the kword shape, which can be set to :square, :triangle, :diagonal, :line, or :pair. By default, the library uses :square as the default shape.
julia> dist = distribution(data, th, 3; shape = :triangle)64-element Vector{Float64}: 0.031693368936359594 0.026348464619492657 0.013555852247441033 0.012623497997329773 0.012748108589230084 0.011170449488206497 0.02346906987093903 0.020996884735202493 0.014877614597240765 0.011931464174454828 ⋮ 0.00838006230529595 0.02318647085002225 0.017750333778371163 0.007696929238985314 0.00704939919893191 0.008382287494437026 0.008021806853582555 0.019953271028037383 0.019766355140186917
The shape :pair doesn't require a value of n, since it always uses n=2. However, it is still necessary to informe a value to this parameter, that will be interpreted as the separation between two points in a diagonal.
julia> dist = distribution(data, th, 6; shape = :pair)4-element Vector{Float64}: 0.28006180588226115 0.24910758281475404 0.24910089386645737 0.22172971743652745
When workign with shape :pair, we recommend you to use the full structure of distribution function.
julia> structure = [3, 9]2-element Vector{Int64}: 3 9julia> dist = distribution(data, data, th, structure; shape = :pair)4-element Vector{Float64}: 0.2791387310173222 0.24914325720566954 0.2492056873897717 0.2225123243872366
Here, structure defines the position of the second element based on the random position of the first element.
Motifs sampling
The sampling mode defines how RecurrenceMicrostatesAnalysis.jl selects motifs from a recurrence space. Currently, the library supports four sampling modes: full, random, columnwise and triangle up. You can learn more about them on the motifs page, where we discuss how each mode works. The sampling mode can be configured using the keyword argument sampling_mode, which can be set to :full, :random, :columnwise, :columnwise_full, or :triangleup. By default, the library uses :random as the default sampling mode.
julia> dist = distribution(data, th, 3; sampling_mode = :full)512-element Vector{Float64}: 0.016399191633648582 0.002603470136417385 0.002526701145215334 0.003252223741778486 0.0027126156152133445 0.0030394957545635272 0.0031271681677044203 0.012181903790875038 0.002526701145215334 0.003252223741778486 ⋮ 0.0018614811475384302 0.010437801318290469 0.0016772355686535075 0.001606252066643495 0.0019340222812539915 0.0017200704405561011 0.0018614811475384302 0.0019101015086330625 0.010022024912316461
Not all sampling modes are compatible with certain motif constrained shapes, and the following table illustrates the compatibility between them.
:full | :random | :columnwise | :columnwise_full | :triangleup | |
|---|---|---|---|---|---|
:square | $\checkmark$ | $\checkmark$ | $\checkmark$ | $\checkmark$ | $\checkmark$ |
:triangle | $\checkmark$ | $\checkmark$ | |||
:diagonal | $\checkmark$ | ||||
:time | $\checkmark$ | ||||
:pair | $\checkmark$ | $\checkmark$ |
Run mode
RecurrenceMicrostatesAnalysis.jl has two run modes that results in a different output type. The run mode :vect allocates all required memory in beginning of the process, and return the distribution as a vector. This is the default configuration of the library for $n < 6$.
julia> dist = distribution(data, th, 4; run_mode = :vect)65536-element Vector{Float64}: 0.002240035626809174 0.00017145401914940992 0.0001514139389890893 0.00012692050768203073 0.00017145401914940992 0.00014028056112224449 0.00016032064128256512 0.0002672010688042752 0.00020485415274994434 0.00015364061456245824 ⋮ 0.00012692050768203073 9.574704965486529e-5 6.68002672010688e-5 6.234691605433089e-5 0.00011133377866844801 7.348029392117568e-5 0.00011801380538855489 0.00012692050768203073 0.0012758851035404141
The run mode :dict uses dictionaries to allocate memory just when needed. The total allocation of dictionary mode can be greater than when using vectors, but the real memory allocation is smaller.
julia> dist = distribution(data, th, 4; run_mode = :dict)Dict{Int64, Float64} with 38267 entries: 31794 => 4.45335e-6 63746 => 8.9067e-6 11950 => 8.9067e-6 45120 => 1.33601e-5 7685 => 1.33601e-5 60623 => 7.79336e-5 59930 => 1.33601e-5 3406 => 2.00401e-5 64460 => 1.78134e-5 47756 => 1.11334e-5 28804 => 1.33601e-5 28900 => 8.9067e-6 62178 => 8.9067e-6 23970 => 8.9067e-6 62961 => 8.01603e-5 28576 => 3.11735e-5 1090 => 1.55867e-5 41511 => 6.68003e-6 27640 => 2.22668e-6 ⋮ => ⋮
It is important to note that the shapes :diagonal, :line, and :pair are not compatible with run mode :dict. Additionally, sampling modes :columnwise and :columnwise_full return a matrix in which each column represents a probability distribution for a specif time value.
julia> dist = distribution(data, th, 2; sampling_mode = :columnwise)16×2999 Matrix{Float64}: 0.0166667 0.353333 0.223333 … 0.0 0.216667 0.0266667 0.0566667 0.0 0.0 0.00666667 0.0266667 0.156667 0.0833333 0.0166667 0.0566667 0.0133333 0.00333333 0.04 0.0 0.26 0.196667 0.0 0.18 0.0 0.0933333 0.0 0.0 0.00333333 0.0366667 0.116667 0.206667 0.0 0.0 … 0.18 0.0 0.213333 0.2 0.0 0.0 0.226667 0.00333333 0.15 0.0 0.0 0.0 0.03 0.0633333 0.0 0.05 0.0133333 0.0533333 0.0166667 0.01 0.0666667 0.136667 0.0 0.0 0.2 0.0 0.113333 0.156667 0.00333333 0.02 … 0.18 0.0 0.116667 0.0 0.0 0.05 0.0466667 0.01 0.0 0.0 0.18 0.226667 0.0 0.233333 0.0 0.0 0.0 0.0 0.0366667 0.0366667 0.0 0.0 0.0 0.0166667 0.0533333 0.02 0.0 0.0 0.173333 0.156667 … 0.00666667 0.16 0.0julia> dist = distribution(data, th, 2; sampling_mode = :columnwise_full)16×2999 Matrix{Float64}: 0.021007 0.334111 0.238413 … 0.00100033 0.185395 0.0453484 0.0646882 0.0 0.0 0.00866956 0.0436812 0.106702 0.0563521 0.0143381 0.0440147 0.0106702 0.00766922 0.0680227 0.0 0.232077 0.21007 0.00133378 0.193064 0.0 0.0683561 0.0 0.0 0.00766922 0.0436812 0.109036 0.208736 0.0 0.0 … 0.185395 0.0103368 0.208736 0.182061 0.0 0.0 0.1994 0.00200067 0.145048 0.0 0.0 0.0 0.0373458 0.0513505 0.0 0.0526842 0.0136712 0.0423474 0.0116706 0.00866956 0.0656886 0.185729 0.0 0.0 0.198066 0.00166722 0.147382 0.160387 0.000666889 0.00933645 … 0.198733 0.00100033 0.104035 0.0 0.00633545 0.0363454 0.0466822 0.0103368 0.0 0.0 0.232744 0.211737 0.00133378 0.192064 0.0 0.0 0.0 0.0 0.0376792 0.0516839 0.0 0.0 0.00566856 0.0346782 0.0463488 0.0110037 0.0 0.0 0.160387 0.173058 … 0.00800267 0.186395 0.0
Number of samples
With exception of sampling modes :full and :columnwise_full, all sampling modes take motifs randomly in a recurrence space. The kword num_samples defines the number of samples that will be used by the library, it can be either an integer value that specifies the exact number, or a decimal value interpreted as the percentage of samples taken from the entire available population. By default, RecurrenceMicrostatesAnalysis.jl uses $5\%$.
julia> dist = distribution(data, th, 3; num_samples = 0.1)512-element Vector{Float64}: 0.0162572318647085 0.002568980863373387 0.0025166889185580773 0.0032153983088562526 0.002769247886070316 0.003034045393858478 0.0031141522029372497 0.012182910547396529 0.002468847352024922 0.003322207387627948 ⋮ 0.0018613707165109033 0.010426123720516245 0.0016800178015131287 0.0015965732087227414 0.0019448153093012907 0.0017868268802848242 0.0018168669336893636 0.0019214508233199822 0.010210280373831775
julia> dist = distribution(data, th, 3; num_samples = 50000)512-element Vector{Float64}: 0.01612 0.00286 0.00264 0.00306 0.00286 0.00308 0.00292 0.01202 0.00244 0.00346 ⋮ 0.00184 0.0104 0.00146 0.00158 0.0018 0.00182 0.00164 0.0017 0.00976
Threads
RecurrenceMicrostatesAnalysis.jl is highly compatible with CPU asynchronous jobs, that can increase significantly the computational performance of the library. The kword threads defines if the library will use threads or not, being true by default. The number of threads used is equal to the number of threads available to Julia, being it configured by the environment variable JULIA_NUM_THREADS, or by the running argument --threads T in Julia initiation: For example, using julia --threads 8.
julia> using BenchmarkToolsjulia> @benchmark distribution(data, th, 4; sampling_mode = :full, threads = false)BenchmarkTools.Trial: 4 samples with 1 evaluation per sample. Range (min … max): 1.491 s … 1.523 s ┊ GC (min … max): 0.00% … 0.00% Time (median): 1.499 s ┊ GC (median): 0.00% Time (mean ± σ): 1.503 s ± 14.972 ms ┊ GC (mean ± σ): 0.00% ± 0.00% █ █ █ █ █▁▁█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▁ 1.49 s Histogram: frequency by time 1.52 s < Memory estimate: 1.02 MiB, allocs estimate: 27.julia> @benchmark distribution(data, th, 4; sampling_mode = :full, threads = true)BenchmarkTools.Trial: 4 samples with 1 evaluation per sample. Range (min … max): 1.313 s … 1.329 s ┊ GC (min … max): 0.00% … 0.00% Time (median): 1.320 s ┊ GC (median): 0.00% Time (mean ± σ): 1.321 s ± 6.779 ms ┊ GC (mean ± σ): 0.00% ± 0.00% █ █ █ █ █▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▁▁▁▁▁▁█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▁ 1.31 s Histogram: frequency by time 1.33 s < Memory estimate: 1.53 MiB, allocs estimate: 47.
RecurrenceMicrostatesAnalysis.jl allocates memory for each thread, so how many threads you use, more memory the library will allocate. It is done to increase the performance, and avoid the memory concurrency.
Metrics
RecurrenceMicrostatesAnalysis.jl uses the library Distances.jl to simplify the configuration of metrics, and increase the computation performance. With it, modify the metric is a easy process that can be done with the kword metric.
julia> using Distancesjulia> my_metric = KLDivergence()Distances.KLDivergence()julia> dist = distribution(data, th, 2; metric = my_metric)16-element Vector{Float64}: 0.06314654213920391 0.017573938181009562 0.016399822103624638 0.07878585723815877 0.018456748943740272 0.05544362908605737 0.0 0.06381810095619302 0.017549477429397376 0.0 0.05706693351122971 0.06273070936179675 0.07593506782299311 0.06357571714476318 0.06381810095619302 0.3456993551256393
The default recurrence functions were configured to metrics with two arguments, like euclidean(x, y), so if you need to use another type of metric, it is needed to define a new recurrence function, see Recurrence functions page to know more about it.
Recurrence functions
A recurrence function defines if two points of a trajectory recurr or not. Actually the library have two recurrence functions available
- Standard recurrence: $R(\mathbf{x}, \mathbf{y})=\Theta(\varepsilon - \|\mathbf{x}-\mathbf{y}\|)$
- Recurrence with corridor threshold: $R(\mathbf{x}, \mathbf{y})=\Theta(\|\mathbf{x}-\mathbf{y}\| - \varepsilon_{min}) \cdot \Theta(\varepsilon_{max} - \|\mathbf{x}-\mathbf{y}\|)$
RecurrenceMicrostatesAnalysis.jl automatically change between them with the type of parameters, so if you use as parameter a Float64, the library will apply the standard recurrence, or, if you use a Tuple{Float64, Float64}, the library will apply the recurrence with corridor threshold.
julia> dist = (distribution(data, th, 2))'1×16 adjoint(::Vector{Float64}) with eltype Float64: 0.107654 0.0463909 0.0473316 … 0.0307561 0.0312408 0.0809317julia> dist = (distribution(data, (0.0, th), 2))'1×16 adjoint(::Vector{Float64}) with eltype Float64: 0.106667 0.046511 0.0471915 0.0858461 … 0.030974 0.0311541 0.0805048
It is possible to write your own recurrece function, we talk more about it in the Recurrence functions page.
High-dimensionality data
If you are working with a dynamical system or a data time serie with two or more dimensions, it is important to note that RecurrenceMicrostatesAnalysis.jl effectively not works with vectors, but matrices. In this situation, each row of the matrix will represent a coordinate, and each column a set of coordinates along a trajectory. For example, if we want a uniform distribution with three dimension and 3,000 points, we will have something like:
julia> using Distributionsjulia> data = rand(Uniform(0, 1), 3, 3000)3×3000 Matrix{Float64}: 0.994217 0.923043 0.451553 0.37781 … 0.0653662 0.884488 0.274507 0.759201 0.841055 0.936537 0.940517 0.917393 0.6376 0.582953 0.208879 0.0112225 0.126509 0.366041 0.404994 0.419298 0.807877
This format of data is effectvely what the library uses. In the case of previous section, when we are working with vectors, RecurrenceMicrostatesAnalysis.jl converts it to a matrix $1\times 3,000$ but when we are working which data with a dimensionality different than one, it is necessary to use the proper format.
julia> using RecurrenceMicrostatesAnalysisjulia> th, s = find_parameters(data, 3)(0.6552728947866461, 6.128141517700486)julia> dist = distribution(data, th, 3)512-element Vector{Float64}: 0.009183355585224744 0.0035847797062750334 0.003640409434801958 0.0032799287939474855 0.0035002225189141077 0.0030796617712505565 0.0032465509568313307 0.005238095238095238 0.0035469514908767247 0.0033199821984868714 ⋮ 0.003431241655540721 0.005594125500667557 0.0030952380952380953 0.0030084557187360925 0.003484646194926569 0.003126390743213173 0.003373386737872719 0.0034690698709390296 0.00995549621717846
Continuous problems
Continuous problems means numerically integrate a differential equation problem and take the values as input to RecurrenceMicrostatesAnalysis.jl. Thinking in it, we make the library compatible with a powerful tool to solve these problems in Julia: the library DifferentialEquations.jl. The way to apply this kind of data in the library is similar with the other two cases discussed before, as we will demonstrate in this section.
The code of Lorenz system used in these examples was get from Example 2 of DifferentialEquations.jl documentation
julia> function lorenz!(du, u, p, t) du[1] = 10.0 * (u[2] - u[1]) du[2] = u[1] * (28.0 - u[3]) - u[2] du[3] = u[1] * u[2] - (8 / 3) * u[3] endlorenz! (generic function with 1 method)julia> using DifferentialEquationsjulia> u0 = [1.0; 0.0; 0.0]3-element Vector{Float64}: 1.0 0.0 0.0julia> tspan = (0.0, 1000.0)(0.0, 1000.0)julia> prob = ODEProblem(lorenz!, u0, tspan)ODEProblem with uType Vector{Float64} and tType Float64. In-place: true Non-trivial mass matrix: false timespan: (0.0, 1000.0) u0: 3-element Vector{Float64}: 1.0 0.0 0.0julia> sol = solve(prob)retcode: Success Interpolation: 3rd order Hermite t: 13309-element Vector{Float64}: 0.0 3.5678604836301404e-5 0.0003924646531993154 0.003262408731175873 0.009058076622686189 0.01695647090176743 0.027689960116420883 0.041856352219618344 0.060240411865493296 0.08368541210909924 ⋮ 999.4684386984246 999.5461655578678 999.6379068125303 999.7253820792902 999.811462486767 999.8851689329069 999.9428080600619 999.9973353539323 1000.0 u: 13309-element Vector{Vector{Float64}}: [1.0, 0.0, 0.0] [0.9996434557625105, 0.0009988049817849058, 1.781434788799189e-8] [0.9961045497425811, 0.010965399721242457, 2.1469553658389193e-6] [0.9693591548287857, 0.0897706331002921, 0.00014380191884671585] [0.9242043547708632, 0.24228915014052968, 0.0010461625485930237] [0.8800455783133068, 0.43873649717821195, 0.003424260078582332] [0.8483309823046307, 0.6915629680633586, 0.008487625469885364] [0.8495036699348377, 1.0145426764822272, 0.01821209108471829] [0.9139069585506618, 1.442559985646147, 0.03669382222358562] [1.0888638225734468, 2.0523265829961646, 0.07402573595703686] ⋮ [0.4932515482755346, 0.8715606700919747, 10.516359581195958] [0.9404189451381124, 1.7670046363918026, 8.612476815089584] [2.1798699336043352, 4.244219726761523, 7.112313060328521] [5.047985066511083, 9.898098798343868, 7.577131003969779] [11.054231264129331, 20.013355756332004, 15.432985026875338] [17.030266923750112, 21.643364279228603, 34.339086610002006] [16.197155332736422, 8.52886663217251, 43.855823473225534] [10.191550116138744, -2.3118400709623317, 39.71841399721481] [9.858747856008915, -2.613711553766342, 39.371604354846596]
With the data computed, it is easy to apply to RecurrenceMicrostatesAnalysis.jl, with a simply memory access given by DifferentialEquations.jl.
julia> using RecurrenceMicrostatesAnalysisjulia> data = sol[:, :]3×13309 Matrix{Float64}: 1.0 0.999643 0.996105 0.969359 … 16.1972 10.1916 9.85875 0.0 0.000998805 0.0109654 0.0897706 8.52887 -2.31184 -2.61371 0.0 1.78143e-8 2.14696e-6 0.000143802 43.8558 39.7184 39.3716julia> th, s = find_parameters(data, 3)(19.07814891144143, 3.343044763805463)julia> dist = distribution(data, th, 3)512-element Vector{Float64}: 0.15484776500788586 0.010980355128389897 0.0003628945362743189 0.0056277454276192 0.025773644165925366 0.00015462266422643716 0.006638835340077246 0.01768989447709077 0.00036323337337634907 0.005652254644666049 ⋮ 0.0010201255685121844 0.013246158829665686 0.017705255092382806 0.0006583604892446327 0.027279323301646792 0.0015845152347937815 0.0010213679712196284 0.0039793029262423915 0.25949692629570176
Although it is possible to compute the distribution as demonstrated above, we strongly advise against doing so in this way.
We recommend you to apply a transient into your data and take a correct time resolution while doing the process of discretization, it is needed to maximize the information available. RecurrenceMicrostatesAnalysis.jl has a utilitary function to help with this process.
julia> prepared_data = prepare(sol, 0.2; transient = 10000, K = 1000)3×1000 Matrix{Float64}: -10.719 -6.7762 -3.09341 -12.7639 … -2.68218 -7.09519 -10.68 -15.3216 -1.71365 -4.16461 -17.1177 -2.00772 -11.6349 -3.69038 23.3945 30.9561 17.7139 27.2908 21.6974 16.2567 36.4084julia> th, s = find_parameters(prepared_data, 3)(18.202650220261273, 5.4163749628248095)julia> dist = distribution(prepared_data, th, 3)512-element Vector{Float64}: 0.0005823293172690763 0.0020682730923694778 0.0023092369477911647 0.006044176706827309 0.0011445783132530121 0.004799196787148594 0.0033132530120481927 0.0011445783132530121 0.0019076305220883535 0.0056024096385542165 ⋮ 0.0023092369477911647 0.009116465863453816 0.001606425702811245 0.0004417670682730924 0.0032329317269076305 0.00018072289156626507 0.0022289156626506025 0.0017269076305220883 0.032951807228915664
If you have the threshold parameter, it is also possible to simplify the call using:
julia> dist = distribution(sol, th, 3, 0.2; transient = 10000, K = 1000)512-element Vector{Float64}: 0.0007028112449799197 0.0027710843373493976 0.0020883534136546186 0.005562248995983936 0.0012248995983935743 0.004558232931726908 0.0034136546184738957 0.0013654618473895582 0.0019477911646586345 0.006024096385542169 ⋮ 0.001967871485943775 0.009397590361445784 0.0018473895582329317 0.00014056224899598393 0.003172690763052209 0.00010040160642570282 0.0021485943775100404 0.0015060240963855422 0.03263052208835342
Spatial data
RecurrenceMicrostatesAnalysis.jl is compatible with generalised recurrence plot analysis for spatial data proposed by Marwan, Kurths and Saparin at 2006 [9]. It allow the library to calculate a probability distribution of motifs in a tensorial recurrence space, for example, to images the recurrence space have four dimensions.
Since this is an open research field, the library is designed to support exploration and estimation for research purposes. We don’t recommend applying it in production environments 😉
The application of RecurrenceMicrostatesAnalysis.jl to spatial data is very similar to the others presented before, but the input format is more complex. Instead to matrices we need to use abstract arrays with dimension $D$, where the first dimension will be interpreted as a coordinate dimension (such as for high-dimensionaly data), and rest of the dimensions will be the spatial data dimensionality. To illustrate it, let an image with RGB. It can be represented as an abstract array with 3 dimensions, where the first dimension will have a length 3, being each element a color value (red, blue and green), and the others two dimensions are relative to each pixel that compose the image. We will demonstrate it using a uniform distribution, where each position can be interpreted as a RGB pixel for an image 100x100.
julia> using Distributionsjulia> data = rand(Uniform(0, 1), 3, 100, 100)3×100×100 Array{Float64, 3}: [:, :, 1] = 0.930512 0.685091 0.400719 0.598883 … 0.474625 0.705602 0.677165 0.333871 0.152929 0.858387 0.0966122 0.0679173 0.630751 0.115315 0.431917 0.079195 0.281837 0.0321147 0.963463 0.991465 0.307691 [:, :, 2] = 0.4944 0.702796 0.0461865 0.851894 … 0.69983 0.557202 0.752729 0.489717 0.00829937 0.174998 0.361437 0.900985 0.434267 0.518935 0.300541 0.235064 0.051467 0.236407 0.634239 0.449685 0.856544 [:, :, 3] = 0.476605 0.774933 0.694877 0.518359 … 0.250346 0.207986 0.929247 0.565359 0.162767 0.0364833 0.760339 0.74481 0.673143 0.93361 0.698185 0.506192 0.318814 0.389869 0.500124 0.668594 0.355678 ;;; … [:, :, 98] = 0.647837 0.515496 0.980569 0.220374 … 0.357407 0.863349 0.0437883 0.291345 0.960614 0.683101 0.0224864 0.310691 0.0430254 0.323424 0.69384 0.746136 0.801814 0.623409 0.898161 0.298798 0.674252 [:, :, 99] = 0.402018 0.470444 0.154148 0.0487609 … 0.965543 0.753302 0.4996 0.648283 0.545017 0.575287 0.317912 0.205886 0.0707082 0.604057 0.490343 0.185378 0.625347 0.0121554 0.447258 0.417955 0.808113 [:, :, 100] = 0.985901 0.339386 0.436336 0.869166 … 0.787128 0.409054 0.228148 0.621966 0.26746 0.749075 0.672641 0.893482 0.469762 0.8457 0.626858 0.722872 0.96448 0.680114 0.755131 0.318975 0.135761
When we work with spatial data is necessarity to use the complete structure of distribution function, defining a vector structure where each value represents the length of a motif constrained side. For example, to a square tensorial motif constrained with side 2, we can use:
julia> using RecurrenceMicrostatesAnalysisjulia> dist = distribution(data, data, 0.5, [2, 2, 2, 2])65536-element Vector{Float64}: 0.8023712361908648 0.0041166109373763785 0.004098288978925584 0.0005044784696167795 0.004489712636738025 0.0002717063156623596 0.00047345606269441056 0.00010535126109207201 0.004332934969539744 0.00037247708714173287 ⋮ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
Since the recurrence space has four dimensions, in this examples, it is necessary for structure has the same number of elements, where each element will represent the motif' side lenght for each dimension.