improved ficurves project
This commit is contained in:
3
projects/project_ficurves/Makefile
Normal file
3
projects/project_ficurves/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
ZIPFILES=
|
||||
|
||||
include ../project.mk
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
73
projects/project_ficurves/ficurves.tex
Normal file
73
projects/project_ficurves/ficurves.tex
Normal file
@@ -0,0 +1,73 @@
|
||||
\documentclass[a4paper,12pt,pdftex]{exam}
|
||||
|
||||
\newcommand{\ptitle}{F-I curves}
|
||||
\input{../header.tex}
|
||||
\firstpagefooter{Supervisor: Jan Grewe}{phone: 29 74588}%
|
||||
{email: jan.grewe@uni-tuebingen.de}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\input{../instructions.tex}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%% Questions %%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\section{Quantifying the responsiveness of a neuron by its F-I curves}
|
||||
The responsiveness of a neuron is often quantified using an F-I
|
||||
curve. The F-I curve plots the \textbf{F}iring rate of the neuron as a
|
||||
function of the stimulus \textbf{I}ntensity.
|
||||
|
||||
\begin{questions}
|
||||
\question In the accompanying datasets you find the
|
||||
\textit{spike\_times} of an P-unit electroreceptor of the weakly
|
||||
electric fish \textit{Apteronotus leptorhynchus} to a stimulus of a
|
||||
certain intensity, i.e. the \textit{contrast}. The spike times are
|
||||
given in milliseconds relative to the stimulus onset.
|
||||
\begin{parts}
|
||||
\part For each stimulus intensity estimate the average response
|
||||
(PSTH) and plot it. You will see that there are three parts. (i)
|
||||
The first 200\,ms is the baseline (no stimulus) activity. (ii)
|
||||
During the next 1000\,ms the stimulus was switched on. (iii) After
|
||||
stimulus offset the neuronal activity was recorded for further
|
||||
825\,ms.
|
||||
|
||||
\part Extract the neuron's activity for every 50\,ms after
|
||||
stimulus onset and for one 50\,ms slice before stimulus onset.
|
||||
|
||||
For each time slice plot the resulting F-I curve by plotting the
|
||||
computed firing rates against the corresponding stimulus
|
||||
intensity, respectively the contrast.
|
||||
|
||||
\part Fit a Boltzmann function to each of the F-I-curves. The
|
||||
Boltzmann function is a sigmoidal function and is defined as
|
||||
\begin{equation}
|
||||
f(x) = \frac{\alpha-\beta}{1+e^{-k(x-x_0)}}+\beta \; .
|
||||
\end{equation}
|
||||
$x$ is the stimulus intensity, $\alpha$ is the starting
|
||||
firing rate, $\beta$ the saturation firing rate, $x_0$ defines the
|
||||
position of the sigmoid, and $k$ (together with $\alpha-\beta$)
|
||||
sets the slope.
|
||||
|
||||
Before you do the fitting, familiarize yourself with the four
|
||||
parameter of the Boltzmann function. What is its value for very
|
||||
large or very small stimulus intensities? How does the Boltzmann
|
||||
function change if you change either of the parameter?
|
||||
|
||||
How could you get good initial estimates for the parameter?
|
||||
|
||||
Do the fits and show the resulting Boltzmann functions together
|
||||
with the corresponding data.
|
||||
|
||||
\part Illustrate how the F-I curves change in time also by means
|
||||
of the parameter you obtained from the fits with the Boltzmann
|
||||
function.
|
||||
|
||||
Which parameter stay the same, which ones change with time?
|
||||
|
||||
Support your conclusion with appropriate statistical tests.
|
||||
|
||||
\part Discuss you results with respect to encoding of different
|
||||
stimulus intensities.
|
||||
\end{parts}
|
||||
\end{questions}
|
||||
|
||||
\end{document}
|
||||
83
projects/project_ficurves/solution/boltzmannFit.m
Normal file
83
projects/project_ficurves/solution/boltzmannFit.m
Normal file
@@ -0,0 +1,83 @@
|
||||
function [best_params] = boltzmannFit(contrasts, onset_fi)
|
||||
% use a gradient descent approach to fit the fi curve owith a Boltzmann
|
||||
% sigmoidal function
|
||||
%
|
||||
% Arguments:
|
||||
% constrasts: the x-values (must be sorted ascendingly)
|
||||
% onset_fi: the measured neuronal responses
|
||||
%
|
||||
% Returns the parameters leading to the best fit.
|
||||
|
||||
% we will make some educated guesses for the start parameters; minimum:
|
||||
% minimum fi response; saturation: maximum fi response; inflection: zero
|
||||
% contrast; slope: response range/contrast range
|
||||
start_params = [min(onset_fi), max(onset_fi), 0, ...
|
||||
(max(onset_fi) - min(onset_fi))/(max(contrasts) - min(contrasts))];
|
||||
|
||||
best_params = gradientDescent(start_params, contrasts, onset_fi);
|
||||
|
||||
|
||||
%% subfunctions that could be separate m-files, but are only used in this context
|
||||
|
||||
function bp = gradientDescent(p, x, y)
|
||||
% performs the gradient descent. Input arguments:
|
||||
% p: the list of starting parameters for the search
|
||||
% x: the x-values
|
||||
% y: the measured y-values
|
||||
%
|
||||
% returns the parameters leading to the best fit
|
||||
|
||||
delta = 0.001;
|
||||
gradient = getGradient(p, x, y, delta);
|
||||
eps = [0.001 0.001, 0.00001, 0.00001]; % key point; use smaller changes for the more delicate parameters
|
||||
while norm(gradient) > 0.5
|
||||
p = p - eps .* gradient;
|
||||
gradient = getGradient(p, x, y, delta);
|
||||
end
|
||||
bp = p;
|
||||
end
|
||||
|
||||
function g = getGradient(p, x, y, delta)
|
||||
% calculate the gradient in the error surface for a small step
|
||||
% along all parameters
|
||||
% p: the parameter list
|
||||
% x: the x-values
|
||||
% y: the measured y-values
|
||||
% delta: the step used or the gradient
|
||||
%
|
||||
% returns the gradient
|
||||
g = zeros(size(p));
|
||||
|
||||
for i = 1:length(p)
|
||||
np = p;
|
||||
np(i) = np(i) + delta;
|
||||
g(i) = (costFunction(np, x, y) - costFunction(p, x, y)) / delta;
|
||||
end
|
||||
end
|
||||
|
||||
function cost = costFunction(p, x, y)
|
||||
% cost function that relates the model to the data. Applies a
|
||||
% Boltzman model and calculates the error in a mean square error
|
||||
% sense.
|
||||
% p: the set of parameters used for the model
|
||||
% x: the x-values
|
||||
% y: the measured y-values
|
||||
%
|
||||
% returns the costs related to that parameterization
|
||||
|
||||
y_est = boltzmannModel(p, x);
|
||||
cost = msqError(y, y_est);
|
||||
end
|
||||
|
||||
function e = msqError(y, y_est)
|
||||
% calculates the deviation between y and y_est in the mean square
|
||||
% error sense
|
||||
% y and y_est must have the same size
|
||||
%
|
||||
% returns the error
|
||||
e = mean((y - y_est).^2);
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
10
projects/project_ficurves/solution/boltzmannModel.m
Normal file
10
projects/project_ficurves/solution/boltzmannModel.m
Normal file
@@ -0,0 +1,10 @@
|
||||
function y = boltzmannModel(p, x)
|
||||
% computes the Boltmannfunction given the parameters given in p
|
||||
% the following order is assumed
|
||||
% p(1) = minimum y-value
|
||||
% p(2) = maximum y-value, i.e. the saturation
|
||||
% p(3) = the position of the inflection point
|
||||
% p(4) = the slope at the inflection
|
||||
|
||||
y = ((p(1) - p(2))) ./ (1 + exp((x-p(3))/p(4))) + p(2);
|
||||
end
|
||||
27
projects/project_ficurves/solution/convolutionRate.m
Normal file
27
projects/project_ficurves/solution/convolutionRate.m
Normal file
@@ -0,0 +1,27 @@
|
||||
function [time, rate] = convolution_rate(spikes, sigma, dt, t_max)
|
||||
% PSTH computed with convolution method.
|
||||
%
|
||||
% [time, rate] = convolution_rate(spikes, sigma, dt, t_max)
|
||||
%
|
||||
% Arguments:
|
||||
% spikes: a vector containing the spike times.
|
||||
% sigma : the standard deviation of the Gaussian kernel in seconds.
|
||||
% dt : the temporal resolution in seconds.
|
||||
% t_max : the trial duration in seconds.
|
||||
%
|
||||
% Returns: two vectors containing the time and the rate.
|
||||
|
||||
time = 0:dt:t_max - dt;
|
||||
rate = zeros(size(time));
|
||||
spike_indices = round(spikes / dt);
|
||||
rate(spike_indices) = 1;
|
||||
kernel = gaussKernel(sigma, dt);
|
||||
|
||||
rate = conv(rate, kernel, 'same');
|
||||
end
|
||||
|
||||
|
||||
function y = gaussKernel(s, step)
|
||||
x = -4 * s:step:4 * s;
|
||||
y = exp(-0.5 .* (x ./ s) .^ 2) ./ sqrt(2 * pi) / s;
|
||||
end
|
||||
12
projects/project_ficurves/solution/getFICurve.m
Normal file
12
projects/project_ficurves/solution/getFICurve.m
Normal file
@@ -0,0 +1,12 @@
|
||||
function [fi, fi_std, sort_contrasts] = getFICurve(firing_rates, time, duration, contrasts)
|
||||
|
||||
[sort_contrasts, sort_idx] = sort(contrasts);
|
||||
fi = zeros(length(sort_contrasts), 1);
|
||||
fi_std = zeros(length(sort_contrasts), 1);
|
||||
|
||||
for i = 1:length(sort_contrasts)
|
||||
responses = firing_rates{sort_idx(i)};
|
||||
onset_responses = mean(responses((time > 0) & (time <= duration), :),1);
|
||||
fi(i) = mean(onset_responses);
|
||||
fi_std(i) = std(onset_responses);
|
||||
end
|
||||
10
projects/project_ficurves/solution/getFiringRates.m
Normal file
10
projects/project_ficurves/solution/getFiringRates.m
Normal file
@@ -0,0 +1,10 @@
|
||||
function [time, rates] = getFiringRates(all_spikes, min_t, max_t, dt)
|
||||
|
||||
|
||||
time = min_t:dt:max_t-dt;
|
||||
rates = zeros(length(time), length(all_spikes));
|
||||
for i = 1:length(all_spikes)
|
||||
spike_times = all_spikes{i}/1000 - min_t;
|
||||
spike_times(spike_times == 0.0) = dt;
|
||||
[t, rates(:, i)] = convolutionRate(spike_times, 0.01, dt, max_t-min_t);
|
||||
end
|
||||
36
projects/project_ficurves/solution/main.m
Normal file
36
projects/project_ficurves/solution/main.m
Normal file
@@ -0,0 +1,36 @@
|
||||
%% solution for project onset-FI
|
||||
|
||||
clear all
|
||||
close all
|
||||
|
||||
dt = 1./20000;
|
||||
t_min = -0.2;
|
||||
t_max = 1.825;
|
||||
data_folder = strcat('..', filesep, 'data');
|
||||
files = dir(strcat(data_folder, filesep, '*.mat'));
|
||||
|
||||
contrasts = zeros(length(files), 1);
|
||||
rates = {};
|
||||
for i = 1:length(files)
|
||||
data = load(strcat(data_folder, filesep, files(i).name));
|
||||
contrasts(i) = data.contrast;
|
||||
spikes_times = data.spike_times;
|
||||
[t, rates{i}] = getFiringRates(spikes_times, t_min, t_max, dt);
|
||||
end
|
||||
|
||||
%% create a plot of the average response for the different contrasts
|
||||
plotAverageResponse(rates, t, contrasts, 'averageResponse')
|
||||
|
||||
%% extract the onset and the steady-state firing rate
|
||||
[fi, fi_std, contr] = getFICurve(rates, t, 0.015, contrasts);
|
||||
|
||||
%% plot FI curve
|
||||
plotFICurve(fi, fi_std, contr, 'ficurve')
|
||||
|
||||
%% fit FICurve with Boltzmann function
|
||||
best_params = boltzmannFit(contr, fi);
|
||||
|
||||
% plot the fi curve with the fit
|
||||
plotFICurveFit(fi, fi_std, contr, best_params, 'ficurvefit')
|
||||
|
||||
|
||||
47
projects/project_ficurves/solution/plotAverageResponse.m
Normal file
47
projects/project_ficurves/solution/plotAverageResponse.m
Normal file
@@ -0,0 +1,47 @@
|
||||
function [] = plotAverageResponse(rates, time, contrasts, filename)
|
||||
[sort_contrasts, sort_idx] = sort(contrasts);
|
||||
|
||||
figure
|
||||
set(gcf, 'paperunits', 'centimeters', 'papersize', [20 20], ...
|
||||
'paperposition', [0.0 0.0 20, 20], 'color', 'white')
|
||||
axes = cell(size(sort_contrasts));
|
||||
for i = 1:length(sort_contrasts)
|
||||
subplot(ceil(length(sort_contrasts)/2), 2, i);
|
||||
responses = rates{sort_idx(i)};
|
||||
avg_response = mean(responses, 2)';
|
||||
std_response = std(responses, [], 2)';
|
||||
f = fill([time fliplr(time)], [avg_response + std_response fliplr(avg_response - std_response)], ...
|
||||
'b', 'EdgeColor','none', 'displayname', 'std');
|
||||
alpha(f, 0.25)
|
||||
hold on
|
||||
plot(time, avg_response, 'b', 'displayname', 'average response')
|
||||
text(1.2, 500, sprintf('contrast: %i %', sort_contrasts(i)), ...
|
||||
'fontsize', 9, 'FontWeight', 'bold')
|
||||
xlim([time(1) time(end)])
|
||||
ylim([0, 600])
|
||||
set(gca, 'XTick', time(1):0.2:time(end), 'YTick', 0:200:600)
|
||||
if mod(i,2) == 1
|
||||
ylabel('firing rate [Hz]', 'fontsize', 11);
|
||||
else
|
||||
set(gca, 'yticklabels', []);
|
||||
end
|
||||
if i > length(sort_contrasts) - 2
|
||||
xlabel('time [s]', 'fontsize', 11)
|
||||
else
|
||||
set(gca, 'xticklabels', []);
|
||||
end
|
||||
|
||||
box('off')
|
||||
axes{i} = gca();
|
||||
if i == 1
|
||||
l = legend('show');
|
||||
l.FontSize = 9;
|
||||
l.Box = 'off';
|
||||
l.Orientation = 'horizontal';
|
||||
l.Position = [0.25, 0.96, .5, 0.01];
|
||||
end
|
||||
end
|
||||
|
||||
axes{1}.Position = [axes{3}.Position(1) axes{2}.Position(2) ...
|
||||
axes{2}.Position(3) axes{3}.Position(4)];
|
||||
saveas(gcf, filename, 'pdf')
|
||||
16
projects/project_ficurves/solution/plotFICurve.m
Normal file
16
projects/project_ficurves/solution/plotFICurve.m
Normal file
@@ -0,0 +1,16 @@
|
||||
function plotFICurve(fi, fi_std, contrasts, filename)
|
||||
|
||||
figure()
|
||||
set(gcf, 'paperunits', 'centimeters', 'papersize', [10 10], ...
|
||||
'paperposition', [0.0 0.0 10, 10], 'color', 'white')
|
||||
|
||||
errorbar(contrasts, fi, fi_std, 'o', 'displayname', 'onset response')
|
||||
xlim([-20, 20])
|
||||
ylim([0, 600])
|
||||
xlabel('stimulus contrast [%]', 'fontsize', 11)
|
||||
ylabel('firing rate [Hz]', 'fontsize', 11)
|
||||
box('off')
|
||||
|
||||
if ~isempty(filename)
|
||||
saveas(gcf, filename, 'pdf')
|
||||
end
|
||||
8
projects/project_ficurves/solution/plotFICurveFit.m
Normal file
8
projects/project_ficurves/solution/plotFICurveFit.m
Normal file
@@ -0,0 +1,8 @@
|
||||
function plotFICurveFit(fi, fi_error, contrasts, fit_parameter, filename)
|
||||
|
||||
plotFICurve(fi, fi_error, contrasts, '')
|
||||
fit = boltzmannModel(fit_parameter, min(contrasts):0.1:max(contrasts));
|
||||
hold on
|
||||
plot(min(contrasts):0.1:max(contrasts), fit, 'displayname', 'Boltzmann fit')
|
||||
legend('Location', 'northwest')
|
||||
saveas(gcf, filename, 'pdf')
|
||||
Reference in New Issue
Block a user