Accessing simulation data
A single run generates a SamplePath
object which is a recursive array provided by RecursiveArrayTools.jl.
sample_path = simulate(state, model, SortingDirect(), tfinal = 100.0, save_points = 0:25:100.0)
sample_path
t: 5-element Array{Float64,1}:
0.0
25.0
50.0
75.0
100.0
x: 5-element Array{Array{Int64,1},1}:
[10, 0, 0, 0, 0]
[3, 7, 1, 9, 61]
[1, 9, 0, 13, 102]
[1, 9, 0, 11, 101]
[1, 9, 1, 12, 118]
Access the state at the first time point:
sample_path[1]
5-element Array{Int64,1}:
10
0
0
0
0
Access the third component at the second time point:
sample_path[3,2]
1
Access entire history for the third and fourth components:
sample_path[[3,4], :]
2×5 Array{Int64,2}:
0 1 0 0 1
0 9 13 11 12
Running multiple simulations generates an Ensemble
, a collection of SamplePath
objects; that is, Vector{SamplePath}
. These objects also support indexing:
# ensemble of 10 trajectories
ensemble = [simulate(state, model, SortingDirect(), tfinal = 100.0, save_points = 0:25:100.0) for _ in 1:10];
10-element Array{SamplePath{Int64,2,Array{Array{Int64,1},1},Array{Float64,1}},1}:
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [4, 6, 2, 12, 96], [0, 10, 1, 36, 265], [1, 9, 0, 30, 277], [0, 10, 0, 25, 287]]
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [0, 10, 1, 17, 159], [0, 10, 0, 15, 159], [2, 8, 0, 13, 164], [0, 10, 0, 15, 158]]
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [9, 1, 0, 1, 6], [1, 9, 1, 13, 114], [1, 9, 0, 11, 114], [0, 10, 1, 20, 187]]
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [7, 3, 0, 1, 31], [2, 8, 0, 3, 24], [1, 9, 0, 12, 49], [3, 7, 0, 11, 51]]
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [3, 7, 0, 2, 9], [7, 3, 0, 5, 11], [6, 4, 0, 4, 10], [7, 3, 2, 12, 22]]
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [2, 8, 1, 10, 47], [0, 10, 0, 15, 116], [1, 9, 0, 24, 111], [0, 10, 0, 11, 114]]
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [6, 4, 0, 3, 1], [3, 7, 0, 9, 33], [2, 8, 0, 10, 31], [5, 5, 0, 11, 33]]
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [0, 10, 1, 13, 55], [1, 9, 1, 21, 181], [2, 8, 0, 20, 248], [0, 10, 0, 23, 241]]
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [0, 10, 1, 13, 158], [1, 9, 0, 16, 160], [0, 10, 0, 11, 160], [1, 9, 0, 9, 160]]
t: [0.0, 25.0, 50.0, 75.0, 100.0]
x: Array{Int64,1}[[10, 0, 0, 0, 0], [0, 10, 1, 14, 102], [0, 10, 0, 18, 133], [1, 9, 0, 13, 135], [0, 10, 0, 10, 132]]
Retrieve an individual sample path:
ensemble[2]
t: 5-element Array{Float64,1}:
0.0
25.0
50.0
75.0
100.0
x: 5-element Array{Array{Int64,1},1}:
[10, 0, 0, 0, 0]
[0, 10, 1, 17, 159]
[0, 10, 0, 15, 159]
[2, 8, 0, 13, 164]
[0, 10, 0, 15, 158]
Index into a SamplePath:
ensemble[2][[3,4], :]
2×5 Array{Int64,2}:
0 1 0 0 0
0 17 15 13 15
Working with DataFrames
Both SamplePath
and Ensemble
implement the IterableTables.jl interface. This means that they act as data sources that can be converted into any supported data sink. For example, you can convert an Ensemble
into a DataFrame
:
using DataFrames
DataFrame(ensemble)
trial | t | X1 | X2 | X3 | X4 | X5 | |
---|---|---|---|---|---|---|---|
Int64 | Float64 | Int64 | Int64 | Int64 | Int64 | Int64 | |
1 | 1 | 0.0 | 10 | 0 | 0 | 0 | 0 |
2 | 1 | 25.0 | 4 | 6 | 2 | 12 | 96 |
3 | 1 | 50.0 | 0 | 10 | 1 | 36 | 265 |
4 | 1 | 75.0 | 1 | 9 | 0 | 30 | 277 |
5 | 1 | 100.0 | 0 | 10 | 0 | 25 | 287 |
6 | 2 | 0.0 | 10 | 0 | 0 | 0 | 0 |
7 | 2 | 25.0 | 0 | 10 | 1 | 17 | 159 |
8 | 2 | 50.0 | 0 | 10 | 0 | 15 | 159 |
9 | 2 | 75.0 | 2 | 8 | 0 | 13 | 164 |
10 | 2 | 100.0 | 0 | 10 | 0 | 15 | 158 |
11 | 3 | 0.0 | 10 | 0 | 0 | 0 | 0 |
12 | 3 | 25.0 | 9 | 1 | 0 | 1 | 6 |
13 | 3 | 50.0 | 1 | 9 | 1 | 13 | 114 |
14 | 3 | 75.0 | 1 | 9 | 0 | 11 | 114 |
15 | 3 | 100.0 | 0 | 10 | 1 | 20 | 187 |
16 | 4 | 0.0 | 10 | 0 | 0 | 0 | 0 |
17 | 4 | 25.0 | 7 | 3 | 0 | 1 | 31 |
18 | 4 | 50.0 | 2 | 8 | 0 | 3 | 24 |
19 | 4 | 75.0 | 1 | 9 | 0 | 12 | 49 |
20 | 4 | 100.0 | 3 | 7 | 0 | 11 | 51 |
21 | 5 | 0.0 | 10 | 0 | 0 | 0 | 0 |
22 | 5 | 25.0 | 3 | 7 | 0 | 2 | 9 |
23 | 5 | 50.0 | 7 | 3 | 0 | 5 | 11 |
24 | 5 | 75.0 | 6 | 4 | 0 | 4 | 10 |
25 | 5 | 100.0 | 7 | 3 | 2 | 12 | 22 |
26 | 6 | 0.0 | 10 | 0 | 0 | 0 | 0 |
27 | 6 | 25.0 | 2 | 8 | 1 | 10 | 47 |
28 | 6 | 50.0 | 0 | 10 | 0 | 15 | 116 |
29 | 6 | 75.0 | 1 | 9 | 0 | 24 | 111 |
30 | 6 | 100.0 | 0 | 10 | 0 | 11 | 114 |
31 | 7 | 0.0 | 10 | 0 | 0 | 0 | 0 |
32 | 7 | 25.0 | 6 | 4 | 0 | 3 | 1 |
33 | 7 | 50.0 | 3 | 7 | 0 | 9 | 33 |
34 | 7 | 75.0 | 2 | 8 | 0 | 10 | 31 |
35 | 7 | 100.0 | 5 | 5 | 0 | 11 | 33 |
36 | 8 | 0.0 | 10 | 0 | 0 | 0 | 0 |
37 | 8 | 25.0 | 0 | 10 | 1 | 13 | 55 |
38 | 8 | 50.0 | 1 | 9 | 1 | 21 | 181 |
39 | 8 | 75.0 | 2 | 8 | 0 | 20 | 248 |
40 | 8 | 100.0 | 0 | 10 | 0 | 23 | 241 |
41 | 9 | 0.0 | 10 | 0 | 0 | 0 | 0 |
42 | 9 | 25.0 | 0 | 10 | 1 | 13 | 158 |
43 | 9 | 50.0 | 1 | 9 | 0 | 16 | 160 |
44 | 9 | 75.0 | 0 | 10 | 0 | 11 | 160 |
45 | 9 | 100.0 | 1 | 9 | 0 | 9 | 160 |
46 | 10 | 0.0 | 10 | 0 | 0 | 0 | 0 |
47 | 10 | 25.0 | 0 | 10 | 1 | 14 | 102 |
48 | 10 | 50.0 | 0 | 10 | 0 | 18 | 133 |
49 | 10 | 75.0 | 1 | 9 | 0 | 13 | 135 |
50 | 10 | 100.0 | 0 | 10 | 0 | 10 | 132 |
Each component of the state vector appears as a column, along with trajectory trial
and timestamp t
columns. If the conversion does not produce the expected result, one may be able to force the correct behavior using the tablefy
function:
import BioSimulator: tablefy
DataFrame(tablefy(sample_path)) # DataFrame(sample_path) is currently incorrect
t | X1 | X2 | X3 | X4 | X5 | |
---|---|---|---|---|---|---|
Float64 | Int64 | Int64 | Int64 | Int64 | Int64 | |
1 | 0.0 | 10 | 0 | 0 | 0 | 0 |
2 | 25.0 | 3 | 7 | 1 | 9 | 61 |
3 | 50.0 | 1 | 9 | 0 | 13 | 102 |
4 | 75.0 | 1 | 9 | 0 | 11 | 101 |
5 | 100.0 | 1 | 9 | 1 | 12 | 118 |
Saving simulation results
Because SamplePath
and Ensemble
support iteration, you can save simulation data directly using Julia's I/O interface.
Example here
The easiest approach takes advantage of IterableTables.jl:
using CSV
CSV.write(file, result, delim = '\t')
Obtaining summary statistics
Summary statistics for ensembles are supported directly:
using Statistics
mean(ensemble)
std(result)
var(result)