Skip to content

Commit f98ec14

Browse files
authored
Vemundss (#18)
* Fixes division by zero * Adds nancorrelate2d * Formatting changes to 'numpy'doctstring * Small reformatting to 'numpy'docstring * return empty list before crash on idexing * Blacks. And updates mode param in nancorrelate2d * Off by one * NEW VERSION - Simplifies SpatialMap() usage --------- Co-authored-by: Vemund Sigmundson Schøyen <[email protected]>
1 parent 75e11b1 commit f98ec14

File tree

4 files changed

+181
-103
lines changed

4 files changed

+181
-103
lines changed

spatial_maps/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
from .stats import (
99
sparsity, selectivity, information_rate, information_specificity,
1010
prob_dist)
11-
from .tools import autocorrelation, fftcorrelate2d
11+
from .tools import autocorrelation, fftcorrelate2d, nancorrelate2d

spatial_maps/fields.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def find_peaks(image):
2525
indices = np.arange(1, num_objects+1)
2626
peaks = ndimage.maximum_position(image, labels=labels, index=indices)
2727
peaks = np.array(peaks)
28-
center = np.array(image.shape) / 2
28+
center = (np.array(image.shape) - 1) / 2
2929
distances = np.linalg.norm(peaks - center, axis=1)
3030
peaks = peaks[distances.argsort()]
3131
return peaks
@@ -211,6 +211,9 @@ def calculate_field_centers(rate_map, labels, center_method='maxima'):
211211
else:
212212
raise ValueError(
213213
"invalid center_method flag '{}'".format(center_method))
214+
if not bc:
215+
# empty list
216+
return bc
214217
bc = np.array(bc)
215218
bc[:,[0, 1]] = bc[:,[1, 0]] # y, x -> x, y
216219
return bc

spatial_maps/maps.py

Lines changed: 66 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ def _adjust_bin_size(box_size, bin_size=None, bin_count=None):
1717
box_size = np.array([box_size, box_size])
1818

1919
if bin_size is None:
20-
bin_size = np.array([box_size[0] / bin_count[0],
21-
box_size[1] / bin_count[1]])
20+
bin_size = np.array([box_size[0] / bin_count[0], box_size[1] / bin_count[1]])
2221

2322
# round bin size of to closest requested bin size
24-
bin_size = np.array([box_size[0] / int(box_size[0] / bin_size[0]),
25-
box_size[1] / int(box_size[1] / bin_size[1])])
23+
bin_size = np.array(
24+
[
25+
box_size[0] / int(box_size[0] / bin_size[0]),
26+
box_size[1] / int(box_size[1] / bin_size[1]),
27+
]
28+
)
2629

2730
return box_size, bin_size
2831

@@ -42,115 +45,113 @@ def smooth_map(rate_map, bin_size, smoothing, **kwargs):
4245
def _occupancy_map(x, y, t, xbins, ybins):
4346
t_ = np.append(t, t[-1] + np.median(np.diff(t)))
4447
time_in_bin = np.diff(t_)
45-
values, _, _ = np.histogram2d(
46-
x, y, bins=[xbins, ybins], weights=time_in_bin)
48+
values, _, _ = np.histogram2d(x, y, bins=[xbins, ybins], weights=time_in_bin)
4749
return values
4850

4951

5052
def _spike_map(x, y, t, spike_times, xbins, ybins):
5153
t_ = np.append(t, t[-1] + np.median(np.diff(t)))
5254
spikes_in_bin, _ = np.histogram(spike_times, t_)
53-
values, _, _ = np.histogram2d(
54-
x, y, bins=[xbins, ybins], weights=spikes_in_bin)
55+
values, _, _ = np.histogram2d(x, y, bins=[xbins, ybins], weights=spikes_in_bin)
5556
return values
5657

5758

58-
def interpolate_nan_2D(array, method='nearest'):
59+
def interpolate_nan_2D(array, method="nearest"):
5960
from scipy import interpolate
61+
6062
x = np.arange(0, array.shape[1])
6163
y = np.arange(0, array.shape[0])
62-
#mask invalid values
64+
# mask invalid values
6365
array = np.ma.masked_invalid(array)
6466
xx, yy = np.meshgrid(x, y)
65-
#get only the valid values
67+
# get only the valid values
6668
x1 = xx[~array.mask]
6769
y1 = yy[~array.mask]
6870
newarr = array[~array.mask]
6971

7072
return interpolate.griddata(
71-
(x1, y1), newarr.ravel(), (xx, yy),
72-
method=method, fill_value=0)
73+
(x1, y1), newarr.ravel(), (xx, yy), method=method, fill_value=0
74+
)
7375

7476

7577
class SpatialMap:
76-
def __init__(self, x, y, t, spike_times, box_size, bin_size, bin_count=None):
78+
def __init__(
79+
self, smoothing=0.05, box_size=[1.0, 1.0], bin_size=0.02, bin_count=None
80+
):
81+
"""
82+
Parameters
83+
----------
84+
smoothing : float
85+
Smoothing of spike_map and occupancy_map before division
86+
box_size : Sequence-like
87+
Size of box in x and y direction
88+
bin_size : float
89+
Resolution of spatial maps
90+
"""
7791
box_size, bin_size = _adjust_bin_size(box_size, bin_size, bin_count)
7892
xbins, ybins = _make_bins(box_size, bin_size)
79-
self.spike_pos = _spike_map(x, y, t, spike_times, xbins, ybins)
80-
self.time_pos = _occupancy_map(x, y, t, xbins, ybins)
81-
assert all(self.spike_pos[self.time_pos == 0] == 0)
8293

94+
self.smoothing = smoothing
8395
self.bin_size = bin_size
8496
self.box_size = box_size
8597
self.xbins = xbins
8698
self.ybins = ybins
8799

88-
def spike_map(self, smoothing, mask_zero_occupancy=False, **kwargs):
89-
if smoothing == 0.0:
90-
spmap = copy(self.spike_pos)
91-
else:
92-
spmap = smooth_map(self.spike_pos, self.bin_size, smoothing, **kwargs)
93-
100+
def spike_map(self, x, y, t, spike_times, mask_zero_occupancy=True, **kwargs):
101+
spmap = _spike_map(x, y, t, spike_times, self.xbins, self.ybins)
102+
spmap = (
103+
smooth_map(spmap, self.bin_size, self.smoothing, **kwargs)
104+
if self.smoothing
105+
else spmap
106+
)
94107
if mask_zero_occupancy:
95-
spmap[self.time_pos == 0] = np.nan
96-
108+
spmap[_occupancy_map(x, y, t, self.xbins, self.ybins) == 0] = np.nan
97109
return spmap
98110

99-
def occupancy_map(self, smoothing, mask_zero_occupancy=False, threshold=None, **kwargs):
100-
'''Compute occupancy map as a histogram of postition
101-
weighted with time spent.
102-
Parameters:
103-
-----------
104-
mask_zero_occupancy : bool
105-
Set zero occupancy to nan
106-
threshold : float
107-
Set low occupancy times to zero as not to get spurious
108-
large values in rate.
109-
110-
'''
111-
ocmap = copy(self.time_pos)
112-
if threshold is not None:
113-
scmap[self.time_pos <= threshold] = 0
114-
if smoothing > 0:
115-
ocmap = smooth_map(ocmap, self.bin_size, smoothing, **kwargs)
116-
111+
def occupancy_map(self, x, y, t, mask_zero_occupancy=True, **kwargs):
112+
ocmap = _occupancy_map(x, y, t, self.xbins, self.ybins)
113+
ocmap_copy = copy(ocmap) # to mask zero occupancy after smoothing
114+
ocmap = (
115+
smooth_map(ocmap, self.bin_size, self.smoothing, **kwargs)
116+
if self.smoothing
117+
else ocmap
118+
)
117119
if mask_zero_occupancy:
118-
ocmap[self.time_pos == 0] = np.nan
119-
120+
ocmap[ocmap_copy == 0] = np.nan
120121
return ocmap
121122

122-
def rate_map(self, smoothing, mask_zero_occupancy=False, interpolate_invalid=False, threshold=None, **kwargs):
123-
'''Calculate rate map as spike_map / occupancy_map
123+
def rate_map(
124+
self,
125+
x,
126+
y,
127+
t,
128+
spike_times,
129+
mask_zero_occupancy=True,
130+
interpolate_invalid=False,
131+
**kwargs
132+
):
133+
"""Calculate rate map as spike_map / occupancy_map
124134
Parameters
125135
----------
126-
smoothing : float
127-
Smoothing of spike_map and occupancy_map before division
128136
mask_zero_occupancy : bool
129137
Set pixels of zero occupancy to nan
130138
interpolate_invalid : bool
131139
Interpolate rate_map after division to remove invalid values,
132140
if False, and mask_zero_occupancy is False,
133141
invalid values are set to zero.
134-
threshold : float
135-
Low occupancy can produce spurious high rate, a threshold sets
136-
occupancy below this to zero.
137142
kwargs : key word arguments to scipy.interpolate, when smoothing > 0
138143
Returns
139144
-------
140145
rate_map : array
141-
'''
142-
spike_map = self.spike_map(smoothing=smoothing, **kwargs)
143-
occupancy_map = self.occupancy_map(
144-
smoothing=smoothing, threshold=threshold, **kwargs)
145-
if mask_zero_occupancy:
146-
# to avoid infinity (x/0) we set zero occupancy to nan
147-
# this can be handy when analyzing low occupancy maps
148-
rate_map = spike_map / occupancy_map
149-
rate_map[self.time_pos == 0] = np.nan
146+
"""
147+
spike_map = self.spike_map(
148+
x, y, t, spike_times, mask_zero_occupancy=mask_zero_occupancy, **kwargs
149+
)
150+
# to avoid infinity (x/0) we set zero occupancy to nan
151+
occupancy_map = self.occupancy_map(x, y, t, mask_zero_occupancy=True, **kwargs)
152+
rate_map = spike_map / occupancy_map
153+
if not mask_zero_occupancy:
154+
rate_map[np.isnan(rate_map)] = 0
150155
elif interpolate_invalid:
151-
rate_map = spike_map / occupancy_map
152156
rate_map = interpolate_nan_2D(rate_map)
153-
else:
154-
rate_map = spike_map / occupancy_map
155-
rate_map[np.isinf(rate_map)] = 0
156157
return rate_map

0 commit comments

Comments
 (0)