debug and add unittests 2
This commit is contained in:
241
unittests/testFrequencyFunctions.py
Normal file
241
unittests/testFrequencyFunctions.py
Normal file
@@ -0,0 +1,241 @@
|
||||
import unittest
|
||||
import numpy as np
|
||||
import helperFunctions as hF
|
||||
import matplotlib.pyplot as plt
|
||||
from warnings import warn
|
||||
|
||||
|
||||
class FrequencyFunctionsTester(unittest.TestCase):
|
||||
|
||||
noise_levels = [0, 0.05, 0.1, 0.2]
|
||||
frequencies = [0, 1, 5, 30, 100, 500, 750, 1000]
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_calculate_eod_frequency(self):
|
||||
start = 0
|
||||
end = 5
|
||||
step = 0.1 / 1000
|
||||
freqs = [0, 1, 10, 500, 700, 1000]
|
||||
for freq in freqs:
|
||||
time = np.arange(start, end, step)
|
||||
eod = np.sin(freq*(2*np.pi) * time)
|
||||
self.assertEqual(freq, round(hF.calculate_eod_frequency(time, eod), 2))
|
||||
|
||||
def test_mean_freq_of_spiketimes_after_time_x(self):
|
||||
simulation_time = 8
|
||||
for freq in self.frequencies:
|
||||
for n in self.noise_levels:
|
||||
spikes = generate_jittered_spiketimes(freq, n, end=simulation_time)
|
||||
sim_freq = hF.mean_freq_of_spiketimes_after_time_x(spikes, 0.00005, simulation_time/4, time_in_ms=False)
|
||||
|
||||
max_diff = round(n*(10+0.7*np.sqrt(freq)), 2)
|
||||
# print("noise: {:.2f}".format(n), "\texpected: {:.2f}".format(freq), "\tgotten: {:.2f}".format(round(sim_freq, 2)), "\tfreq diff: {:.2f}".format(abs(freq-round(sim_freq, 2))), "\tmax_diff:", max_diff)
|
||||
self.assertTrue(abs(freq-round(sim_freq)) <= max_diff, msg="expected freq: {:.2f} vs calculated: {:.2f}. max diff was {:.2f}".format(freq, sim_freq, max_diff))
|
||||
|
||||
def test_calculate_isi_frequency(self):
|
||||
simulation_time = 1
|
||||
sampling_interval = 0.00005
|
||||
|
||||
for freq in self.frequencies:
|
||||
for n in self.noise_levels:
|
||||
spikes = generate_jittered_spiketimes(freq, n, end=simulation_time)
|
||||
sim_freq = hF.calculate_isi_frequency_trace(spikes, sampling_interval, time_in_ms=False)
|
||||
|
||||
isis = np.diff(spikes)
|
||||
step_length = isis / sampling_interval
|
||||
rounded_step_length = np.around(step_length)
|
||||
expected_length = sum(rounded_step_length)
|
||||
|
||||
length = len(sim_freq)
|
||||
self.assertEqual(expected_length, length)
|
||||
|
||||
def test_calculate_isi_frequency_trace(self):
|
||||
sampling_intervals = [0.00005, 0.001, 0.01, 0.2, 0.5, 1]
|
||||
|
||||
test1 = [0, 1, 2, 3, 4] # 1-1-1-1 only 1s in the result
|
||||
test2 = [0, 1, 3, 5, 6] # 1-2-2-1
|
||||
test3 = [0, 3, 10, 12, 15] # 3-7-2-3
|
||||
pos_tests = [test1, test2, test3]
|
||||
|
||||
test4 = generate_jittered_spiketimes(100, 0.2)
|
||||
test5 = generate_jittered_spiketimes(500, 0.2)
|
||||
test6 = generate_jittered_spiketimes(1000, 0)
|
||||
realistic_tests = [test4, test5, test6]
|
||||
|
||||
test_neg_isi = [0, 3, 4, 2, 5] # should raise error non sorted spiketimes
|
||||
test_too_small_sampling_rate = [0.001, 0.0015, 0.002]
|
||||
neg_tests = [test_neg_isi, test_too_small_sampling_rate]
|
||||
|
||||
for test in pos_tests:
|
||||
for sampling_interval in sampling_intervals:
|
||||
calculated_trace = hF.calculate_isi_frequency_trace(test, sampling_interval, time_in_ms=False)
|
||||
diffs = np.diff(test)
|
||||
j = 0
|
||||
count = 0
|
||||
value = 1/diffs[j]
|
||||
for i in range(len(calculated_trace)):
|
||||
if calculated_trace[i] == value:
|
||||
count += 1
|
||||
else:
|
||||
expected_length = round(diffs[j] / sampling_interval)
|
||||
|
||||
# if there are multiple isis of the same length after each other add them together
|
||||
while expected_length < count and value == 1/diffs[j+1]:
|
||||
j += 1
|
||||
expected_length += round(diffs[j] / sampling_interval, 0)
|
||||
|
||||
self.assertEqual(count, expected_length, msg="Length of isi frequency part is not right: expected {:.1f} vs {:.1f}".format(float(count), expected_length))
|
||||
j += 1
|
||||
value = 1/diffs[j]
|
||||
count = 1
|
||||
|
||||
for test in neg_tests:
|
||||
self.assertRaises(ValueError, hF.calculate_isi_frequency_trace, test, 0.2, False)
|
||||
|
||||
def test_calculate_time_and_frequency_trace(self):
|
||||
|
||||
# !!! the produced frequency trace is tested in the test function for specifically the freq_Trace function
|
||||
sampling_intervals = [0.0001, 0.1, 0.5, 1]
|
||||
|
||||
test1 = [0, 1, 2, 5, 7]
|
||||
test2 = [1, 3, 5, 6, 7, 10]
|
||||
test3 = [-1, 2, 4, 5, 11]
|
||||
|
||||
pos_tests = [test1, test2, test3]
|
||||
|
||||
for sampling_interval in sampling_intervals:
|
||||
for test in pos_tests:
|
||||
time, freq = hF.calculate_time_and_frequency_trace(test, sampling_interval, time_in_ms=False)
|
||||
|
||||
self.assertEqual(test[0], time[0])
|
||||
self.assertEqual(test[-1], round(time[-1]+sampling_interval))
|
||||
|
||||
def test_calculate_mean_of_frequency_traces(self):
|
||||
# TODO expand this test to more than this single test case
|
||||
test1_f = [0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1]
|
||||
test1_t = np.arange(0, 8, 0.5)
|
||||
test2_f = [1, 2, 2, 3, 3, 4]
|
||||
test2_t = np.arange(0.5, 7.5, 0.5)
|
||||
|
||||
time_traces = [test1_t, test2_t]
|
||||
freq_traces = [test1_f, test2_f]
|
||||
time, mean = hF.calculate_mean_of_frequency_traces(time_traces, freq_traces, 0.5)
|
||||
|
||||
expected_time = np.arange(0.5, 7.5, 0.5)
|
||||
|
||||
expected_mean = [0.75, 1.25, 1.25, 2, 2, 2.5]
|
||||
time_equal = np.all([time[i] == expected_time[i] for i in range(len(time))])
|
||||
mean_equal = np.all([mean[i] == expected_mean[i] for i in range(len(mean))])
|
||||
self.assertTrue(time_equal)
|
||||
self.assertTrue(mean_equal, msg="expected:\n" + str(expected_mean) + "\n actual: \n" + str(mean))
|
||||
self.assertEqual(len(expected_mean), len(mean))
|
||||
self.assertEqual(len(expected_time), len(time), msg="expected:\n" + str(expected_time) + "\n actual: \n" + str(time))
|
||||
|
||||
# TODO:
|
||||
# all_calculate_mean_isi_frequency_traces(spiketimes, sampling_interval, time_in_ms=True):
|
||||
|
||||
|
||||
def generate_jittered_spiketimes(frequency, noise_level=0., start=0, end=5, method='normal'):
|
||||
|
||||
if method is 'normal':
|
||||
return normal_dist_jittered_spikes(frequency, noise_level, start, end)
|
||||
|
||||
elif method is 'poisson':
|
||||
if noise_level != 0:
|
||||
warn("Poisson jittered spike trains don't support a noise level! ")
|
||||
return poisson_jittered_spikes(frequency, start, end)
|
||||
|
||||
|
||||
def poisson_jittered_spikes(frequency, start, end):
|
||||
if frequency == 0:
|
||||
return []
|
||||
|
||||
mean_isi = 1 / frequency
|
||||
|
||||
spikes = []
|
||||
for part in np.arange(start, end+mean_isi, mean_isi):
|
||||
num_spikes_in_part = np.random.poisson(1)
|
||||
positions = np.sort(np.random.random(num_spikes_in_part))
|
||||
|
||||
while not __poisson_min_dist_test__(positions):
|
||||
positions = np.sort(np.random.random(num_spikes_in_part))
|
||||
|
||||
for pos in positions:
|
||||
spikes.append(part+pos*mean_isi)
|
||||
|
||||
while spikes[-1] > end:
|
||||
del spikes[-1]
|
||||
|
||||
return spikes
|
||||
|
||||
|
||||
def __poisson_min_dist_test__(positions):
|
||||
if len(positions) > 1:
|
||||
diffs = np.diff(positions)
|
||||
if len(diffs[diffs < 0.0001]) > 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def normal_dist_jittered_spikes(frequency, noise_level, start, end):
|
||||
if frequency == 0:
|
||||
return []
|
||||
|
||||
mean_isi = 1 / frequency
|
||||
if noise_level == 0:
|
||||
return np.arange(start, end, mean_isi)
|
||||
|
||||
isis = np.random.normal(mean_isi, noise_level*mean_isi, int((end-start)*1.05/mean_isi))
|
||||
spikes = np.cumsum(isis) + start
|
||||
spikes = np.sort(spikes)
|
||||
|
||||
if spikes[-1] > end:
|
||||
return spikes[spikes < end]
|
||||
|
||||
else:
|
||||
additional_spikes = [spikes[-1] + np.random.normal(mean_isi, noise_level*mean_isi)]
|
||||
|
||||
while additional_spikes[-1] < end:
|
||||
next_isi = np.random.normal(mean_isi, noise_level*mean_isi)
|
||||
additional_spikes.append(additional_spikes[-1] + next_isi)
|
||||
|
||||
additional_spikes = np.sort(np.array(additional_spikes[:-1]))
|
||||
spikes = np.concatenate((spikes, additional_spikes))
|
||||
|
||||
return spikes
|
||||
|
||||
|
||||
def test_distribution():
|
||||
simulation_time = 5
|
||||
freqs = [5, 30, 100, 500, 1000]
|
||||
noise_level = [0.05, 0.1, 0.2, 0.3]
|
||||
repetitions = 1000
|
||||
for freq in freqs:
|
||||
diffs_per_noise = []
|
||||
for n in noise_level:
|
||||
diffs = []
|
||||
print("#### - freq:", freq, "noise level:", n )
|
||||
for reps in range(repetitions):
|
||||
spikes = generate_jittered_spiketimes(freq, n, end=simulation_time)
|
||||
sim_freq = hF.mean_freq_of_spiketimes_after_time_x(spikes, 0.0002, simulation_time / 4, time_in_ms=False)
|
||||
diffs.append(sim_freq-freq)
|
||||
|
||||
diffs_per_noise.append(diffs)
|
||||
|
||||
fig, axs = plt.subplots(1, len(noise_level), figsize=(3.5*len(noise_level), 4), sharex='all')
|
||||
|
||||
for i in range(len(diffs_per_noise)):
|
||||
max_diff = np.max(np.abs(diffs_per_noise[i]))
|
||||
print("Freq: ", freq, "noise: {:.2f}".format(noise_level[i]), "mean: {:.2f}".format(np.mean(diffs_per_noise[i])), "max_diff: {:.4f}".format(max_diff))
|
||||
bins = np.arange(-max_diff, max_diff, 2*max_diff/100)
|
||||
axs[i].hist(diffs_per_noise[i], bins=bins)
|
||||
axs[i].set_title('Noise level: {:.2f}'.format(noise_level[i]))
|
||||
|
||||
plt.show()
|
||||
plt.close()
|
||||
@@ -15,16 +15,6 @@ class HelperFunctionsTester(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_calculate_eod_frequency(self):
|
||||
start = 0
|
||||
end = 5
|
||||
step = 0.1 / 1000
|
||||
freqs = [0, 1, 10, 500, 700, 1000]
|
||||
for freq in freqs:
|
||||
time = np.arange(start, end, step)
|
||||
eod = np.sin(freq*(2*np.pi) * time)
|
||||
self.assertEqual(freq, round(hF.calculate_eod_frequency(time, eod), 2))
|
||||
|
||||
def test__vector_strength__is_1(self):
|
||||
length = 2000
|
||||
rel_spike_times = np.full(length, 0.3)
|
||||
@@ -40,91 +30,8 @@ class HelperFunctionsTester(unittest.TestCase):
|
||||
|
||||
self.assertEqual(0, round(hF.__vector_strength__(rel_spike_times, eod_durations), 5))
|
||||
|
||||
def test_mean_freq_of_spiketimes_after_time_x(self):
|
||||
simulation_time = 8
|
||||
for freq in self.frequencies:
|
||||
for n in self.noise_levels:
|
||||
spikes = generate_jittered_spiketimes(freq, n, end=simulation_time)
|
||||
sim_freq = hF.mean_freq_of_spiketimes_after_time_x(spikes, 0.00005, simulation_time/4, time_in_ms=False)
|
||||
|
||||
max_diff = round(n*(10+0.7*np.sqrt(freq)), 2)
|
||||
# print("noise: {:.2f}".format(n), "\texpected: {:.2f}".format(freq), "\tgotten: {:.2f}".format(round(sim_freq, 2)), "\tfreq diff: {:.2f}".format(abs(freq-round(sim_freq, 2))), "\tmax_diff:", max_diff)
|
||||
self.assertTrue(abs(freq-round(sim_freq)) <= max_diff, msg="expected freq: {:.2f} vs calculated: {:.2f}. max diff was {:.2f}".format(freq, sim_freq, max_diff))
|
||||
|
||||
def test_calculate_isi_frequency(self):
|
||||
simulation_time = 1
|
||||
sampling_interval = 0.00005
|
||||
|
||||
for freq in self.frequencies:
|
||||
for n in self.noise_levels:
|
||||
spikes = generate_jittered_spiketimes(freq, n, end=simulation_time)
|
||||
sim_freq = hF.calculate_isi_frequency(spikes, sampling_interval, time_in_ms=False)
|
||||
|
||||
isis = np.diff(spikes)
|
||||
step_length = isis / sampling_interval
|
||||
rounded_step_length = np.around(step_length)
|
||||
expected_length = sum(rounded_step_length)
|
||||
|
||||
length = len(sim_freq)
|
||||
self.assertEqual(expected_length, length)
|
||||
|
||||
|
||||
|
||||
# def test(self):
|
||||
# test_distribution()
|
||||
|
||||
def generate_jittered_spiketimes(frequency, noise_level, start=0, end=5):
|
||||
if frequency == 0:
|
||||
return []
|
||||
|
||||
mean_isi = 1 / frequency
|
||||
if noise_level == 0:
|
||||
return np.arange(start, end, mean_isi)
|
||||
|
||||
spikes = [start]
|
||||
count = 0
|
||||
while True:
|
||||
next_isi = np.random.normal(mean_isi, noise_level*mean_isi)
|
||||
if next_isi <= 0:
|
||||
count += 1
|
||||
continue
|
||||
next_spike = spikes[-1] + next_isi
|
||||
if next_spike > end:
|
||||
break
|
||||
spikes.append(spikes[-1] + next_isi)
|
||||
|
||||
# print("count: {:} percentage of missed: {:.2f}".format(count, count/len(spikes)))
|
||||
if count > 0.01*len(spikes):
|
||||
print("!!! Danger of lowering actual simulated frequency")
|
||||
pass
|
||||
return spikes
|
||||
|
||||
|
||||
def test_distribution():
|
||||
simulation_time = 5
|
||||
freqs = [5, 30, 100, 500, 1000]
|
||||
noise_level = [0.05, 0.1, 0.2, 0.3]
|
||||
repetitions = 1000
|
||||
for freq in freqs:
|
||||
diffs_per_noise = []
|
||||
for n in noise_level:
|
||||
diffs = []
|
||||
print("#### - freq:", freq, "noise level:", n )
|
||||
for reps in range(repetitions):
|
||||
spikes = generate_jittered_spiketimes(freq, n, end=simulation_time)
|
||||
sim_freq = hF.mean_freq_of_spiketimes_after_time_x(spikes, 0.0002, simulation_time / 4, time_in_ms=False)
|
||||
diffs.append(sim_freq-freq)
|
||||
|
||||
diffs_per_noise.append(diffs)
|
||||
|
||||
fig, axs = plt.subplots(1, len(noise_level), figsize=(3.5*len(noise_level), 4), sharex='all')
|
||||
|
||||
for i in range(len(diffs_per_noise)):
|
||||
max_diff = np.max(np.abs(diffs_per_noise[i]))
|
||||
print("Freq: ", freq, "noise: {:.2f}".format(noise_level[i]), "mean: {:.2f}".format(np.mean(diffs_per_noise[i])), "max_diff: {:.4f}".format(max_diff))
|
||||
bins = np.arange(-max_diff, max_diff, 2*max_diff/100)
|
||||
axs[i].hist(diffs_per_noise[i], bins=bins)
|
||||
axs[i].set_title('Noise level: {:.2f}'.format(noise_level[i]))
|
||||
|
||||
plt.show()
|
||||
plt.close()
|
||||
Reference in New Issue
Block a user