1
+ """
2
+ Toy neural imaging data
3
+ =======================
4
+
5
+ Example with multiple selection tools and events to explore the decomposition of toy data that represents
6
+ neural imaging data.
7
+
8
+ An `ImageGraphic` is used to represent a movie frame, and a `LineCollection` is overlaid onto it. A heatmap and single
9
+ line are added to the subplots below. `LinearSelector` tools are added to the heatmap and line to navigate through
10
+ timepoints in the movie. The image can also be clicked to select the closest component.
11
+
12
+ .. note::
13
+ The majority of the code in this example is just for generating data,
14
+ jump to "# VISUALIZATION STARTS HERE" to skip the data generation code.
15
+
16
+ """
17
+
18
+ # test_example = false
19
+ # sphinx_gallery_pygfx_docs = 'screenshot'
20
+
1
21
import numpy as np
2
22
import fastplotlib as fpl
3
23
from sklearn .decomposition import FastICA
7
27
def generate_time (
8
28
n_timepoints : int ,
9
29
n_components : int ,
10
- firing_prop = 0.05 ,
30
+ firing_prop : float = 0.05 ,
11
31
) -> np .ndarray :
12
32
"""
13
33
Generate some time series data using an AR process:
@@ -22,9 +42,6 @@ def generate_time(
22
42
23
43
n_components: int
24
44
25
- noise_sigma: float
26
- add random gaussian noise with this sigma value
27
-
28
45
firing_prop: float
29
46
30
47
Returns
@@ -56,6 +73,27 @@ def gaussian_2d(x=0, y=0, mx=0, my=0, sx=1, sy=1):
56
73
57
74
58
75
def generate_movie (time_components : np .ndarray , dims : tuple [int , int ] = (50 , 50 ), noise_sigma = 0.1 ) -> np .ndarray :
76
+ """
77
+ Generate a movie using the given time components
78
+
79
+ Parameters
80
+ ----------
81
+ time_components: np.ndarray
82
+ [n_components, n_timepoints]
83
+
84
+ dims: (int, int)
85
+ movie frame (n_rows, c_cols)
86
+
87
+ noise_sigma: float
88
+ sigma of the gaussian noise to add to the movie
89
+
90
+ Returns
91
+ -------
92
+ np.ndarray
93
+ shape is [n_timepoints, n_rows, n_cols]
94
+
95
+ """
96
+
59
97
n_timepoints , n_components = time_components .shape
60
98
61
99
centers = np .random .rand (n_components , 2 )
@@ -84,6 +122,7 @@ def generate_movie(time_components: np.ndarray, dims: tuple[int, int] = (50, 50)
84
122
85
123
86
124
def decomposition (movie , n_components = 5 ):
125
+ """Use ICA to decompose the movie into spatial and temporal components"""
87
126
n_timepoints = movie .shape [0 ]
88
127
X = movie .reshape (n_timepoints , np .prod (movie .shape [1 :])).T
89
128
@@ -104,6 +143,7 @@ def decomposition(movie, n_components=5):
104
143
return contours , temporal_components .T
105
144
106
145
146
+ # generate toy data
107
147
n_components = 5
108
148
n_timepoints = 100
109
149
dims = (50 , 50 )
@@ -115,57 +155,98 @@ def decomposition(movie, n_components=5):
115
155
)
116
156
117
157
np .random .seed (10 )
158
+
159
+ # movie to decompose into spatial and temporal components
118
160
movie = generate_movie (time_components , dims = dims , noise_sigma = 0.1 )
119
161
162
+ # data that will be used to represent the spatial and temporal components
120
163
contours , time_series = decomposition (movie , n_components = n_components )
121
164
165
+ # VISUALIZATION STARTS HERE
122
166
167
+ # create a figure
123
168
figure = fpl .Figure (
124
- (3 , 1 ),
169
+ (3 , 1 ), # 3 rows, 1 column
125
170
size = (700 , 1024 ),
126
171
names = ["movie" , "heatmap" , "selected" ],
127
172
)
128
173
174
+ # don't maintain the aspect ratio for the temporal subplots
129
175
figure ["heatmap" ].camera .maintain_aspect = False
130
176
figure ["selected" ].camera .maintain_aspect = False
131
177
178
+ # add image using first frame of movie
132
179
movie_graphic = figure ["movie" ].add_image (movie [0 ], cmap = "viridis" , vmin = movie .min (), vmax = movie .max ())
180
+
181
+ # add line collection to highlight spatial footprints
133
182
contours_graphic = figure ["movie" ].add_line_collection (contours , cmap = "tab10" )
134
183
184
+ # heatmap of all temporal components
135
185
heatmap_graphic = figure ["heatmap" ].add_image (time_series , cmap = "viridis" )
186
+
187
+ # selector that moves across the time dimension of the heatmap
136
188
selector_time_heatmap = heatmap_graphic .add_linear_selector ()
189
+
190
+ # selector on the heatmap to select a component
137
191
selector_component_heatmap = heatmap_graphic .add_linear_selector (axis = "y" )
138
192
193
+ # add line of the first temporal component
139
194
temporal_selected_graphic = figure ["selected" ].add_line (time_series [0 ])
195
+
196
+ # selector on the line to move across the time dimension
140
197
selector_time_line = temporal_selected_graphic .add_linear_selector ()
141
198
199
+
142
200
def set_timepoint (ev ):
143
- timepoint = ev .info ["value" ]
144
- movie_graphic .data [:] = movie [int (timepoint )]
201
+ timepoint = ev .info ["value" ] # selection from a linear selector
202
+ movie_graphic .data [:] = movie [int (timepoint )] # set movie frame index
203
+
204
+ # sync linear selectors so they're at the same time point
145
205
selector_time_heatmap .selection = timepoint
146
206
selector_time_line .selection = timepoint
147
207
208
+ # add event handler to both linear time selectors
148
209
selector_time_line .add_event_handler (set_timepoint , "selection" )
149
210
selector_time_heatmap .add_event_handler (set_timepoint , "selection" )
150
211
212
+
151
213
@movie_graphic .add_event_handler ("click" )
152
- def image_clicked (ev ):
214
+ def image_clicked (ev ): # called when the image is clicked
215
+ # reset the contour colors
153
216
contours_graphic .cmap = "tab10"
217
+ # get the click position, map from screen to world space
154
218
pos = figure ["movie" ].map_screen_to_world (ev )
219
+
220
+ # get nearest contour
155
221
index = fpl .utils .get_nearest_graphics_indices (pos , contours_graphic )[0 ]
156
222
nearest_contour = contours_graphic .graphics [index ]
223
+
224
+ # set color of nearest contour to white
157
225
nearest_contour .colors = "w"
226
+
227
+ # set heatmap component selector
158
228
selector_component_heatmap .selection = index
159
229
160
230
161
231
@selector_component_heatmap .add_event_handler ("selection" )
162
- def heatmap_component_changed (ev ):
232
+ def heatmap_component_changed (ev ): # called when the heatmap component selector is moved
233
+ # get component index
163
234
index = ev .get_selected_index ()
235
+
236
+ # reset contours colormap
164
237
contours_graphic .cmap = "tab10"
238
+
239
+ # set selected component color to white
165
240
contours_graphic .graphics [index ].colors = "w"
166
241
242
+ # set data of line representing selected temporal component
167
243
temporal_selected_graphic .data [:, 1 ] = time_series [index ]
168
244
169
245
170
246
figure .show ()
171
- fpl .loop .run ()
247
+
248
+ # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively
249
+ # please see our docs for using fastplotlib interactively in ipython and jupyter
250
+ if __name__ == "__main__" :
251
+ print (__doc__ )
252
+ fpl .loop .run ()
0 commit comments