Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ff4f948

Browse filesBrowse files
committed
comments
1 parent 1054a5d commit ff4f948
Copy full SHA for ff4f948

File tree

1 file changed

+91
-10
lines changed
Filter options

1 file changed

+91
-10
lines changed

‎examples/selection_tools/optical_physiology.py

Copy file name to clipboardExpand all lines: examples/selection_tools/optical_physiology.py
+91-10Lines changed: 91 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
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+
121
import numpy as np
222
import fastplotlib as fpl
323
from sklearn.decomposition import FastICA
@@ -7,7 +27,7 @@
727
def generate_time(
828
n_timepoints: int,
929
n_components: int,
10-
firing_prop = 0.05,
30+
firing_prop: float = 0.05,
1131
) -> np.ndarray:
1232
"""
1333
Generate some time series data using an AR process:
@@ -22,9 +42,6 @@ def generate_time(
2242
2343
n_components: int
2444
25-
noise_sigma: float
26-
add random gaussian noise with this sigma value
27-
2845
firing_prop: float
2946
3047
Returns
@@ -56,6 +73,27 @@ def gaussian_2d(x=0, y=0, mx=0, my=0, sx=1, sy=1):
5673

5774

5875
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+
5997
n_timepoints, n_components = time_components.shape
6098

6199
centers = np.random.rand(n_components, 2)
@@ -84,6 +122,7 @@ def generate_movie(time_components: np.ndarray, dims: tuple[int, int] = (50, 50)
84122

85123

86124
def decomposition(movie, n_components=5):
125+
"""Use ICA to decompose the movie into spatial and temporal components"""
87126
n_timepoints = movie.shape[0]
88127
X = movie.reshape(n_timepoints, np.prod(movie.shape[1:])).T
89128

@@ -104,6 +143,7 @@ def decomposition(movie, n_components=5):
104143
return contours, temporal_components.T
105144

106145

146+
# generate toy data
107147
n_components = 5
108148
n_timepoints = 100
109149
dims = (50, 50)
@@ -115,57 +155,98 @@ def decomposition(movie, n_components=5):
115155
)
116156

117157
np.random.seed(10)
158+
159+
# movie to decompose into spatial and temporal components
118160
movie = generate_movie(time_components, dims=dims, noise_sigma=0.1)
119161

162+
# data that will be used to represent the spatial and temporal components
120163
contours, time_series = decomposition(movie, n_components=n_components)
121164

165+
# VISUALIZATION STARTS HERE
122166

167+
# create a figure
123168
figure = fpl.Figure(
124-
(3, 1),
169+
(3, 1), # 3 rows, 1 column
125170
size=(700, 1024),
126171
names=["movie", "heatmap", "selected"],
127172
)
128173

174+
# don't maintain the aspect ratio for the temporal subplots
129175
figure["heatmap"].camera.maintain_aspect = False
130176
figure["selected"].camera.maintain_aspect = False
131177

178+
# add image using first frame of movie
132179
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
133182
contours_graphic = figure["movie"].add_line_collection(contours, cmap="tab10")
134183

184+
# heatmap of all temporal components
135185
heatmap_graphic = figure["heatmap"].add_image(time_series, cmap="viridis")
186+
187+
# selector that moves across the time dimension of the heatmap
136188
selector_time_heatmap = heatmap_graphic.add_linear_selector()
189+
190+
# selector on the heatmap to select a component
137191
selector_component_heatmap = heatmap_graphic.add_linear_selector(axis="y")
138192

193+
# add line of the first temporal component
139194
temporal_selected_graphic = figure["selected"].add_line(time_series[0])
195+
196+
# selector on the line to move across the time dimension
140197
selector_time_line = temporal_selected_graphic.add_linear_selector()
141198

199+
142200
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
145205
selector_time_heatmap.selection = timepoint
146206
selector_time_line.selection = timepoint
147207

208+
# add event handler to both linear time selectors
148209
selector_time_line.add_event_handler(set_timepoint, "selection")
149210
selector_time_heatmap.add_event_handler(set_timepoint, "selection")
150211

212+
151213
@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
153216
contours_graphic.cmap = "tab10"
217+
# get the click position, map from screen to world space
154218
pos = figure["movie"].map_screen_to_world(ev)
219+
220+
# get nearest contour
155221
index = fpl.utils.get_nearest_graphics_indices(pos, contours_graphic)[0]
156222
nearest_contour = contours_graphic.graphics[index]
223+
224+
# set color of nearest contour to white
157225
nearest_contour.colors = "w"
226+
227+
# set heatmap component selector
158228
selector_component_heatmap.selection = index
159229

160230

161231
@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
163234
index = ev.get_selected_index()
235+
236+
# reset contours colormap
164237
contours_graphic.cmap = "tab10"
238+
239+
# set selected component color to white
165240
contours_graphic.graphics[index].colors = "w"
166241

242+
# set data of line representing selected temporal component
167243
temporal_selected_graphic.data[:, 1] = time_series[index]
168244

169245

170246
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

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.