First come, first served: neuronal processing of multi-echo streams in the auditory cortex of echolocating bats.
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
MATLAB · 804 lines · 27 KB · CC-BY-4.0
- function [hImage, hText, hXText] = heatmap(mat, xlab, ylab, textmat, varargin)
- % HEATMAP displays a matrix as a heatmap image
- %
- % USAGE:
- % [hImage, hText, hTick] = heatmap(matrix, xlabels, ylabels, textmatrix, 'param', value, ...)
- %
- % INPUTS:
- % * HEATMAP displays "matrix" as an image whose color intensities reflect
- % the magnitude of the values in "matrix".
- %
- % * "xlabels" (and "ylabels") can be either a numeric vector or cell array
- % of strings that represent the columns (or rows) of the matrix. If either
- % is not specified or empty, no labels will be drawn.
- %
- % * "textmat" can either be: 1 (or true), in which case the "matrix" values will be
- % displayed in each square, a format string, in which case the matrix
- % values will be displayed formatted according to the string specified, a numeric
- % matrix the size of "matrix", in which case those values will be displayed as
- % strings or a cell matrix of strings the size of "matrix", in which case each
- % string will be displayed. If not specified or empty, no text will be
- % displayed on the image
- %
- % OTHER PARAMETERS (passed as parameter-value pairs)
- % * 'Colormap': Either a matrix of size numLevels-by-3 representing the
- % colormap to be used or a string or function handle representing a
- % function that returns a colormap, example, 'jet', 'hsv' or @cool.
- % Non-standard colormaps available within HEATMAP include 'money' and 'red'.
- % By default, the current figure's colormap is used.
- %
- % * 'ColorLevels': The number of distinct levels in the colormap (default:
- % 64). If more levels are specified than are present in the colormap, the
- % levels in the colormap are interpolated. If fewer are specified the
- % colormap is downsampled.
- %
- % * 'UseLogColormap': A true/false value which, if true, specifies that the
- % intensities displayed should match the log of the "matrix" values. Use
- % this if the data is naturally on a logarithmic scale (default: false)
- %
- % * 'UseFigureColormap': Specifies whether the figure's colormap should be
- % used. If false, the color intensities after applying the
- % specified/default colormap will be hardcoded, so that the image will be
- % independent of the figure's colormap. If this option is true, the figure
- % colormap in the end will be replaced by specified/default colormap.
- % (default = true)
- %
- % * 'NaNColor': A 3-element [R G B] vector specifying the color used to display NaN
- % or missing value. [0 0 0] corresponds to black and [1 1 1] to white. By
- % default MATLAB displays NaN values using the color assigned to the
- % lowest value in the colormap. Specifying this option automatically sets
- % the 'UseFigureColormap' option to false because the color mapping must
- % be computed prior to setting the nan color.
- %
- % * 'MinColorValue': A scalar number corresponding to the value of the data
- % that is mapped to the lowest color of the colormap. By default this is
- % the minimum value of the matrix input.
- %
- % * 'MaxColorValue': A scalar number corresponding to the value of the data
- % that is mapped to the highest color of the colormap. By default this is
- % the maximum value of the matrix input.
- %
- % * 'Parent': Handle to an axes object
- %
- % * 'TextColor': Either a color specification of all the text displayed on
- % the image or a string 'xor' which sets the EraseMode property of the text
- % objects to 'xor'. This will display all the text labels in a color that
- % contrasts its background.
- %
- % * 'FontSize': The initial fontSize of the text labels on the image. As
- % the image size is scaled the fontSize is shrunk appropriately.
- %
- % * 'ColorBar': Display colorbar. The corresponding value parameter should
- % be either logical 1 or 0 or a cell array of any additional parameters
- % you wish to pass to the colorbar function (such as location)
- %
- % * 'GridLines': Draw grid lines separating adjacent sections of the
- % heatmap. The value of the parameter is a LineStyle specification, for example,
- % :, -, -. or --. By default, no grid lines are drawn.
- %
- % * 'TickAngle': Angle of rotation of tick labels on x-axis. (Default: 0)
- %
- % * 'ShowAllTicks': Set to 1 or true to force all ticks and labels to be
- % drawn. This can make the axes labels look crowded. (Default: false)
- %
- % * 'TickFontSize': Font size of the X and Y tick labels. Default value is
- % the default axes font size, usually 10. Set to a lower value if many
- % tick labels are being displayed
- %
- % * 'TickTexInterpreter': Set to 1 or true to render tick labels using a TEX
- % interpreter. For example, '_b' and '^o' would be rendered as subscript
- % b and the degree symbol with the TEX interpreter. This parameter is only
- % available in MATLAB R2014b and above (Default: false)
- %
- % OUTPUTS:
- % * hImage: handle to the image object
- % * hText : handle to the text objects (empty if no text labels are drawn)
- % * hTick : handle to the X-tick label text objects if tick angle is not 0
- % (empty otherwise)
- %
- % Notes:
- % * The 'money' colormap displays a colormap where 0 values are mapped to
- % white, negative values displayed in varying shades of red and positive
- % values in varying shades of green
- % * The 'red' colormap maps 0 values to white and higher values to red
- %
- % EXAMPLES:
- % data = reshape(sort(randi(100,10)),10,10)-50;
- % heatmap(data, cellstr(('A':'J')'), mean(data,2), '%0.0f%%',...
- % 'Colormap', 'money', 'Colorbar', true, 'GridLines', ':',...
- % 'TextColor', 'b')
- % For detailed examples, see the associated document heatmap_examples.m
- % Copyright The MathWorks, Inc. 2009-2014
- % Handle missing inputs
- if nargin < 1, error('Heatmap requires at least one input argument'); end
- if nargin < 2, xlab = []; end
- if nargin < 3, ylab = []; end
- if nargin < 4, textmat = []; end
- % Parse parameter/value inputs
- p = parseInputs(mat, varargin{:});
- % Get heatmap axes information if it already exists
- p.axesInfo = getHeatmapAxesInfo(p.hAxes);
- % Calculate the colormap based on inputs
- p = calculateColormap(p, mat);
- % Create heatmap image
- p = plotHeatmap(p, mat); % New properties hImage and cdata added
- % Generate grid lines if selected
- generateGridLines(p);
- % Set axes labels
- [p, xlab, ylab, hXText, origPos] = setAxesTickLabels(p, xlab, ylab);
- % Set text labels
- [p, displayText, fontScaleFactor] = setTextLabels(p, mat, textmat);
- % Add colorbar if selected
- addColorbar(p, mat, textmat)
- % Store heatmap properties in axes for callbacks
- axesInfo = struct('Type', 'heatmap', 'Parameters', p, 'FontScaleFactor', ...
- fontScaleFactor, 'mat', mat, 'hXText', hXText, ...
- 'origAxesPos', origPos);
- axesInfo.xlab = xlab;
- axesInfo.ylab = ylab;
- axesInfo.displayText = displayText;
- set(p.hAxes, 'UserData', axesInfo);
- % Define callbacks
- dObj = datacursormode(p.hFig);
- set(dObj, 'Updatefcn', @cursorFun);
- zObj = zoom(p.hFig);
- set(zObj, 'ActionPostCallback', @(obj,evd)updateLabels(evd.Axes,true));
- pObj = pan(p.hFig);
- % set(pObj, 'ActionPreCallback', @prePan);
- set(pObj, 'ActionPostCallback', @(obj,evd)updateLabels(evd.Axes,true));
- set(p.hFig, 'ResizeFcn', @resize)
- % Set outputs
- hImage = p.hImage;
- hText = p.hText;
- end
- % ---------------------- Heatmap Creation Functions ----------------------
- % Parse PV inputs & return structure of parameters
- function param = parseInputs(mat, varargin)
- p = inputParser;
- p.addParamValue('Colormap',[]); %#ok<*NVREPL>
- p.addParamValue('ColorLevels',[]);
- p.addParamValue('TextColor',[0 0 0]);
- p.addParamValue('UseFigureColormap',true);
- p.addParamValue('UseLogColormap',false);
- p.addParamValue('Parent',NaN);
- p.addParamValue('FontSize',[]);
- p.addParamValue('Colorbar',[]);
- p.addParamValue('GridLines','none');
- p.addParamValue('TickAngle',0);
- p.addParamValue('ShowAllTicks',false);
- p.addParamValue('TickFontSize',[]);
- p.addParamValue('TickTexInterpreter',false);
- p.addParamValue('NaNColor', [NaN NaN NaN], @(x)isnumeric(x) && length(x)==3 && all(x>=0) && all(x<=1));
- p.addParamValue('MinColorValue', nan, @(x)isnumeric(x) && isscalar(x));
- p.addParamValue('MaxColorValue', nan, @(x)isnumeric(x) && isscalar(x));
- p.parse(varargin{:});
- param = p.Results;
- if ~ishandle(param.Parent) || ~strcmp(get(param.Parent,'type'), 'axes')
- param.Parent = gca;
- end
- ind = ~isinf(mat(:)) | isnan(mat(:));
- if isnan(param.MinColorValue)
- param.MinColorValue = min(mat(ind));
- end
- if isnan(param.MaxColorValue)
- param.MaxColorValue = max(mat(ind));
- end
- % Add a few other parameters
- param.hAxes = param.Parent;
- param.hFig = ancestor(param.hAxes, 'figure');
- param.IsGraphics2 = ~verLessThan('matlab','8.4');
- param.ExplicitlyComputeImage = ~all(isnan(param.NaNColor)) ... NaNColor is specified
- || ~param.IsGraphics2 && ~param.UseFigureColormap;
- % if param.IsGraphics2 && ~param.UseFigureColormap && ~isempty(param.ColorBar) % graphics v2
- % warning('heatmap:graphics2figurecolormap', 'The UseFigureColormap false option with colorbar is not supported in versions R2014b and above. In most such cases UseFigureColormap false is unnecessary');
- % end
- end
- % Visualize heatmap image
- function p = plotHeatmap(p, mat)
- p.cdata = [];
- if p.UseLogColormap
- p.Colormap = resamplecmap(p.Colormap, p.ColorLevels, ...
- logspace(0,log10(p.ColorLevels),p.ColorLevels));
- end
- if p.ExplicitlyComputeImage
- % Calculate the color data explicitly and then display it as an image.
- n = p.MinColorValue;
- x = p.MaxColorValue;
- if x == n, x = n+1; end
- p.cdata = round((mat-n)/(x-n)*(p.ColorLevels-1)+1);
- %p.cdata = ceil((mat-n)/(x-n)*p.ColorLevels);
- p.cdata(p.cdata<1) = 1; % Clipping
- p.cdata(p.cdata>p.ColorLevels) = p.ColorLevels; % Clipping
- nanInd = find(isnan(p.cdata));
- p.cdata(isnan(p.cdata)) = 1;
- p.cdata = reshape(p.Colormap(p.cdata(:),:),[size(p.cdata) 3]);
- % Handle NaNColor case
- if ~all(isnan(p.NaNColor))
- p.cdata(nanInd ) = p.NaNColor(1); % Set red color level of nan indices
- p.cdata(nanInd + numel(p.cdata)/3) = p.NaNColor(2); % Set green color level of nan indices
- p.cdata(nanInd + 2*numel(p.cdata)/3) = p.NaNColor(3); % set blue color level of nan indices
- end
- % Add a small dummy image so that colorbar subsequently works
- [indr, indc] = find(~isnan(mat),1);
- imagesc(indr, indc, mat(indr,indc),'Parent',p.hAxes);
- nextplot = get(p.hAxes,'nextplot');
- set(p.hAxes,'nextplot','add');
- p.hImage = image(p.cdata, 'Parent', p.hAxes);
- set(p.hAxes,'nextplot',nextplot);
- axis(p.hAxes,'tight');
- else
- % Use a scaled image plot. Axes CLims and colormap will be set later
- p.hImage = imagesc(mat, 'Parent', p.hAxes);
- end
- set(p.hAxes, 'CLim', [p.MinColorValue p.MaxColorValue]); % Ensure proper clipping for colorbar
- if p.UseFigureColormap
- set(p.hFig,'Colormap',p.Colormap);
- elseif p.IsGraphics2
- % Set the axes colormap and limits
- colormap(p.hAxes, p.Colormap);
- %set(p.hAxes, 'CLim', [p.MinColorValue p.MaxColorValue]);
- end
- end
- % Generate grid lines
- function generateGridLines(p)
- if ~strcmp(p.GridLines,'none')
- xlim = get(p.hAxes,'XLim');
- ylim = get(p.hAxes,'YLim');
- for i = 1:diff(xlim)-1
- line('Parent',p.hAxes,'XData',[i i]+.5, 'YData', ylim, 'LineStyle', p.GridLines);
- end
- for i = 1:diff(ylim)-1
- line('Parent',p.hAxes,'XData',xlim, 'YData', [i i]+.5, 'LineStyle', p.GridLines);
- end
- end
- end
- % Add color bar
- function addColorbar(p, mat, textmat)
- if isempty(p.Colorbar)
- return;
- elseif iscell(p.Colorbar)
- c = colorbar(p.Colorbar{:});
- else
- c = colorbar;
- end
- if p.IsGraphics2
- c.Limits = p.hAxes.CLim;
- ticks = get(c,'Ticks');
- else
- if p.ExplicitlyComputeImage || ~p.UseFigureColormap
- d = findobj(get(c,'Children'),'Tag','TMW_COLORBAR'); % Image
- set(d,'YData', get(p.hAxes,'CLim'));
- set(c,'YLim', get(p.hAxes,'CLim'));
- end
- ticks = get(c,'YTick');
- tickAxis = 'Y';
- if isempty(ticks)
- ticks = get(c,'XTick');
- tickAxis = 'X';
- end
- end
- if ~isempty(ticks)
- if ischar(textmat) % If format string, format colorbar ticks in the same way
- ticklabels = arrayfun(@(x){sprintf(textmat,x)},ticks);
- else
- ticklabels = num2str(ticks(:));
- end
- if p.IsGraphics2
- set(c, 'TickLabels', ticklabels);
- else
- set(c, [tickAxis 'TickLabel'], ticklabels);
- end
- end
- end
- % ------------------------- Tick Label Functions -------------------------
- % Set axes tick labels
- function [p, xlab, ylab, hXText, origPos] = setAxesTickLabels(p, xlab, ylab)
- if isempty(p.axesInfo) % Not previously a heatmap axes
- origPos = [get(p.hAxes,'Position') get(p.hAxes,'OuterPosition')];
- else
- origPos = p.axesInfo.origAxesPos;
- set(p.hAxes, 'Position', origPos(1:4), 'OuterPosition', origPos(5:8));
- end
- if isempty(p.TickFontSize)
- p.TickFontSize = get(p.hAxes, 'FontSize');
- else
- set(p.hAxes, 'FontSize', p.TickFontSize);
- end
- if isempty(ylab) % No ticks or labels
- set(p.hAxes,'YTick',[],'YTickLabel','');
- else
- if isnumeric(ylab) % Numeric tick labels
- ylab = arrayfun(@(x){num2str(x)},ylab);
- end
- if ischar(ylab)
- ylab = cellstr(ylab);
- end
- ytick = get(p.hAxes, 'YTick');
- ytick(ytick<1|ytick>length(ylab)) = [];
- if p.ShowAllTicks || length(ytick) > length(ylab)
- ytick = 1:length(ylab);
- end
- set(p.hAxes,'YTick',ytick,'YTickLabel',ylab(ytick));
- end
- if p.IsGraphics2
- if p.TickTexInterpreter
- set(p.hAxes,'TickLabelInterpreter','tex');
- else
- set(p.hAxes,'TickLabelInterpreter','none');
- end
- end
- % Xlabels are trickier because they could have a TickAngle
- hXText = []; % Default value
- if isempty(xlab)
- set(p.hAxes,'XTick',[],'XTickLabel','');
- else
- if isnumeric(xlab)
- xlab = arrayfun(@(x){num2str(x)},xlab);
- end
- if ischar(xlab)
- xlab = cellstr(xlab);
- end
- xtick = get(p.hAxes, 'XTick');
- xtick(xtick<1|xtick>length(xlab)) = [];
- if p.ShowAllTicks || length(xtick) > length(xlab)
- xtick = 1:length(xlab);
- end
- if p.IsGraphics2
- set(p.hAxes,'XTick',xtick,'XTickLabel',xlab(xtick),'XTickLabelRotation', p.TickAngle);
- else
- if p.TickAngle == 0
- set(p.hAxes,'XTick',xtick,'XTickLabel',xlab(xtick));
- else
- hXText = createXTicks(p.hAxes, p.TickAngle, xtick, xlab(xtick), p.TickTexInterpreter);
- adjustAxesToAccommodateTickLabels(p.hAxes, hXText);
- end
- end
- end
- end
- % Create Rotated X Tick Labels (Graphics v1)
- function hXText = createXTicks(hAxes, tickAngle, xticks, xticklabels, texInterpreter)
- axXLim = get(hAxes, 'XLim');
- [xPos, yPos] = calculateTextTickPositions(hAxes, axXLim, xticks);
- if texInterpreter
- interpreter = 'tex';
- else
- interpreter = 'none';
- end
- hXText = text(xPos, yPos, cellstr(xticklabels), 'Units', 'normalized', ...
- 'Parent', hAxes, 'FontSize', get(hAxes,'FontSize'), ...
- 'HorizontalAlignment', 'right', 'Rotation', tickAngle,...
- 'Interpreter', interpreter);
- set(hAxes, 'XTick', xticks, 'XTickLabel', '');
- end
- % Calculate positions of X tick text objects in normalized units
- function [xPos, yPos] = calculateTextTickPositions(hAxes, xlim, ticks)
- oldunits = get(hAxes,'Units');
- set(hAxes,'units','pixels');
- axPos = get(hAxes,'position');
- set(hAxes,'units',oldunits);
- xPos = (ticks - xlim(1))/diff(xlim);
- %yPos = -.08 * ones(size(xPos));
- yPos = -7.82/axPos(4) * ones(size(xPos));
- end
- % Adjust axes and tick positions so that everything fits well on screen
- function adjustAxesToAccommodateTickLabels(hAxes, hXText)
- % The challenge here is that the axes container, especially in a subplot is
- % not well defined. The outer position property does not fully span or
- % contain the x tick text objects. So here we just shrink the axes height
- % just a little so that the axes and tick labels take the same room as the
- % axes would have without the ticks.
- [axPosP, axPosN, axOPP, axOPN, coPosP, textPosP] = ...
- getGraphicsObjectsPositions(hAxes, hXText); %#ok<ASGLU>
- header = axOPP(4) + axOPP(2) - axPosP(4) - axPosP(2); % Distance between top of axes and container in pixels;
- delta = 5; % To adjust for overlap between area designated for regular ticks and area occupied by rotated ticks
- axHeightP = axOPP(4) - header - delta - textPosP(4);
- % Fudge axis position if labels are taking up too much room
- if textPosP(4)/(textPosP(4)+axHeightP) > .7 % It's taking up more than 70% of total height
- axHeightP = (1/.7-1) * textPosP(4); % Minimum axis
- end
- axHeightN = max(0.0001, axHeightP / coPosP(4));
- axPosN = axPosN + [0 axPosN(4)-axHeightN 0 axHeightN-axPosN(4)];
- set(hAxes,'Position', axPosN)
- end
- % Calculate graphics objects positions in pixels and normalized units
- function [axPosP, axPosN, axOPP, axOPN, coPosP, textPosP, textPosN] =...
- getGraphicsObjectsPositions(hAxes, hXText)
- axPosN = get(hAxes, 'Position'); axOPN = get(hAxes, 'OuterPosition');
- set(hAxes,'Units','Pixels');
- axPosP = get(hAxes, 'Position'); axOPP = get(hAxes, 'OuterPosition');
- set(hAxes,'Units','Normalized');
- hContainer = get(hAxes,'Parent');
- units = get(hContainer,'Units');
- set(hContainer,'Units','Pixels');
- coPosP = get(hContainer,'Position');
- set(hContainer,'Units',units);
- set(hXText,'Units','pixels'); % Measure height in pixels
- extents = get(hXText,'Extent'); % Get heights for all text objects
- extents = vertcat(extents{:}); % Collect heights in one matrix
- textPosP = [min(extents(:,1)) min(extents(:,2)) ...
- max(extents(:,3)+extents(:,1))-min(extents(:,1)) ...
- max(extents(:,4))]; % Find dimensions of text label block
- set(hXText,'Units','normalized'); % Restore previous behavior
- extents = get(hXText,'Extent'); % Get heights for all text objects
- extents = vertcat(extents{:}); % Collect heights in one matrix
- textPosN = [min(extents(:,1)) min(extents(:,2)) ...
- max(extents(:,3)+extents(:,1))-min(extents(:,1)) ...
- max(extents(:,4))]; % Find dimensions of text label block
- end
- % -------------------------- Callback Functions --------------------------
- % Update x-, y- and text-labels with respect to axes limits
- function updateLabels(hAxes, axesLimitsChanged)
- axInfo = getHeatmapAxesInfo(hAxes);
- if isempty(axInfo), return; end
- p = axInfo.Parameters;
- % Update text font size to fill the square
- if ~isempty(p.hText) && ishandle(p.hText(1))
- fs = axInfo.FontScaleFactor * getBestFontSize(hAxes);
- if fs > 0
- set(p.hText,'fontsize',fs,'visible','on');
- else
- set(p.hText,'visible','off');
- end
- end
- if axesLimitsChanged && ~isempty(axInfo.displayText) % If limits change & text labels are displayed
- % Get positions of text objects
- textPos = get(p.hText,'Position');
- textPos = vertcat(textPos{:});
- % Get axes limits
- axXLim = get(hAxes, 'XLim');
- axYLim = get(hAxes, 'YLim');
- % Find text objects within axes limit
- ind = textPos(:,1) > axXLim(1) & textPos(:,1) < axXLim(2) & ...
- textPos(:,2) > axYLim(1) & textPos(:,2) < axYLim(2);
- set(p.hText(ind), 'Visible', 'on');
- set(p.hText(~ind), 'Visible', 'off');
- end
- % Modify Y Tick Labels
- if ~isempty(axInfo.ylab)
- axYLim = get(hAxes, 'YLim');
- if p.ShowAllTicks
- yticks = ceil(axYLim(1)):floor(axYLim(2));
- else
- set(hAxes, 'YTickMode', 'auto');
- yticks = get(hAxes, 'YTick');
- yticks = yticks( yticks == floor(yticks) );
- yticks(yticks<1|yticks>length(axInfo.ylab)) = [];
- end
- ylabels = repmat({''},1,max(yticks));
- ylabels(1:length(axInfo.ylab)) = axInfo.ylab;
- set(hAxes, 'YTick', yticks, 'YTickLabel', ylabels(yticks));
- end
- if ~isempty(axInfo.xlab)
- axXLim = get(hAxes, 'XLim');
- if p.ShowAllTicks
- xticks = ceil(axXLim(1)):floor(axXLim(2));
- else
- set(hAxes, 'XTickMode', 'auto');
- xticks = get(hAxes, 'XTick');
- xticks = xticks( xticks == floor(xticks) );
- xticks(xticks<1|xticks>length(axInfo.xlab)) = [];
- end
- xlabels = repmat({''},1,max(xticks));
- xlabels(1:length(axInfo.xlab)) = axInfo.xlab;
- if ~isempty(axInfo.hXText) % Rotated X tick labels exist
- try delete(axInfo.hXText); end %#ok<TRYNC>
- axInfo.hXText = createXTicks(hAxes, p.TickAngle, xticks, xlabels(xticks), p.TickTexInterpreter);
- set(hAxes, 'UserData', axInfo);
- else
- set(hAxes, 'XTick', xticks, 'XTickLabel', xlabels(xticks));
- end
- %adjustAxesToAccommodateTickLabels(hAxes, axInfo.hXText)
- end
- end
- % Callback for data cursor
- function output_txt = cursorFun(obj, eventdata)
- hAxes = ancestor(eventdata.Target, 'axes');
- axInfo = getHeatmapAxesInfo(hAxes);
- pos = eventdata.Position;
- if ~isempty(axInfo)
- try
- val = axInfo.displayText{pos(2), pos(1)};
- catch %#ok<CTCH>
- val = num2str(axInfo.mat(pos(2), pos(1)));
- end
- if isempty(axInfo.xlab), i = int2str(pos(1)); else i = axInfo.xlab{pos(1)}; end
- if isempty(axInfo.ylab), j = int2str(pos(2)); else j = axInfo.ylab{pos(2)}; end
- output_txt = sprintf('X: %s\nY: %s\nVal: %s', i, j, val);
- else
- if length(pos) == 2
- output_txt = sprintf('X: %0.4g\nY: %0.4g', pos(1), pos(2));
- else
- output_txt = sprintf('X: %0.4g\nY: %0.4g\nZ: %0.4g', pos(1), pos(2), pos(3));
- end
- end
- end
- % Callback for resize event
- function resize(obj, evd)
- hAxes = findobj(obj, 'type', 'axes');
- for i = 1:length(hAxes)
- updateLabels(hAxes(i), false);
- end
- end
- % Extract heatmap parameters for callback
- function axInfo = getHeatmapAxesInfo(axH)
- axInfo = get(axH, 'UserData');
- try
- if ~strcmp(axInfo.Type, 'heatmap')
- axInfo = [];
- end
- catch %#ok<CTCH>
- axInfo = [];
- end
- end
- % ------------------------- Text Label Functions -------------------------
- % Create text labels
- function [p, displaytext, factor] = setTextLabels(p, mat, textmat)
- if isempty(textmat)
- p.hText = [];
- displaytext = {};
- factor = 0;
- return
- end
- if isscalar(textmat) && textmat % If true convert mat to text
- displaytext = arrayfun(@(x){num2str(x)},mat);
- elseif ischar(textmat) % If a format string, convert mat to text with specific format
- displaytext = arrayfun(@(x){sprintf(textmat,x)},mat);
- elseif isnumeric(textmat) && numel(textmat)==numel(mat) % If numeric, convert to text
- displaytext = arrayfun(@(x){num2str(x)},textmat);
- elseif iscellstr(textmat) && numel(textmat)==numel(mat) % If cell array of strings, it is already formatted
- displaytext = textmat;
- else
- error('texmat is incorrectly specified');
- end
- if ischar(p.TextColor) && strcmp(p.TextColor,'xor')
- colorprop = 'EraseMode';
- else
- colorprop = 'Color';
- end
- autoFontSize = getBestFontSize(p.hAxes);
- if isempty(p.FontSize)
- p.FontSize = autoFontSize;
- end
- [xpos,ypos] = meshgrid(1:size(mat,2),1:size(mat,1));
- if p.FontSize > 0
- p.hText = text(xpos(:),ypos(:),displaytext(:),'FontSize',p.FontSize,...
- 'HorizontalAlignment','center', colorprop, p.TextColor,'Parent',p.hAxes);
- else
- p.hText = text(xpos(:),ypos(:),displaytext(:),'Visible','off',...
- 'HorizontalAlignment','center', colorprop, p.TextColor,'Parent',p.hAxes);
- end
- % Calculate factor to scale font size in future callbacks
- factor = p.FontSize/autoFontSize;
- if isnan(factor), factor = 1; end
- if isinf(factor), factor = p.FontSize/6; end
- % % Set up listeners to handle appropriate zooming
- % addlistener(p.hAxes,{'XLim','YLim'},'PostSet',@(obj,evdata)resizeText);
- % try
- % addlistener(p.hFig,'SizeChange',@(obj,evdata)resizeText);
- % catch
- % addlistener(p.hFig,'Resize',@(obj,evdata)resizeText);
- % end
- % function resizeText
- % if ~isempty(hText) && ishandle(hText(1))
- % fs = factor*getBestFontSize(hAxes);
- % if fs > 0
- % set(hText,'fontsize',fs,'visible','on');
- % else
- % set(hText,'visible','off');
- % end
- % end
- % end
- end
- % Guess best font size from axes size using heuristics
- function fs = getBestFontSize(imAxes)
- hFig = ancestor(imAxes,'figure');
- magicNumber = 80;
- nrows = diff(get(imAxes,'YLim'));
- ncols = diff(get(imAxes,'XLim'));
- if ncols < magicNumber && nrows < magicNumber
- ratio = max(get(hFig,'Position').*[0 0 0 1])/max(nrows,ncols);
- elseif ncols < magicNumber
- ratio = max(get(hFig,'Position').*[0 0 0 1])/ncols;
- elseif nrows < magicNumber
- ratio = max(get(hFig,'Position').*[0 0 0 1])/nrows;
- else
- ratio = 1;
- end
- fs = min(9,ceil(ratio/4)); % the gold formula
- if fs < 4
- fs = 0;
- end
- end
- % -------------------------- Colormap Functions --------------------------
- % Determine the colormap to use
- function p = calculateColormap(p, mat)
- if isempty(p.Colormap)
- if p.IsGraphics2 && ~p.UseFigureColormap
- p.Colormap = colormap(p.hAxes);
- else
- p.Colormap = get(p.hFig,'Colormap');
- end
- if isempty(p.ColorLevels)
- p.ColorLevels = size(p.Colormap,1);
- else
- p.Colormap = resamplecmap(p.Colormap, p.ColorLevels);
- end
- elseif ischar(p.Colormap) || isa(p.Colormap,'function_handle')
- if isempty(p.ColorLevels), p.ColorLevels = 64; end
- if strcmp(p.Colormap, 'money')
- p.Colormap = money(mat, p.ColorLevels);
- else
- p.Colormap = feval(p.Colormap,p.ColorLevels);
- end
- elseif iscell(p.Colormap)
- p.Colormap = feval(p.Colormap{1}, p.Colormap{2:end});
- p.ColorLevels = size(p.Colormap,1);
- elseif isnumeric(p.Colormap) && size(p.Colormap,2) == 3
- p.ColorLevels = size(p.Colormap,1);
- else
- error('Incorrect value for colormap parameter');
- end % p.Colormap is now a p.ColorLevels-by-3 rgb vector
- assert(p.ColorLevels == size(p.Colormap,1));
- end
- % Resample a colormap by interpolation or decimation
- function cmap = resamplecmap(cmap, clevels, xi)
- t = cmap;
- if nargin < 3
- xi = linspace(1,clevels,size(t,1));
- end
- xi([1 end]) = [1 clevels]; % These need to be exact for the interpolation to
- % work and we don't want machine precision messing it up
- cmap = [interp1(xi, t(:,1), 1:clevels);...
- interp1(xi, t(:,2), 1:clevels);...
- interp1(xi, t(:,3), 1:clevels)]';
- end
- % Generate Red-White-Green color map
- function cmap = money(data, clevels)
- % Function to make the heatmap have the green, white and red effect
- n = min(data(:));
- x = max(data(:));
- if x == n, x = n+1; end
- zeroInd = round(-n/(x-n)*(clevels-1)+1);
- if zeroInd <= 1 % Just green
- b = interp1([1 clevels], [1 0], 1:clevels);
- g = interp1([1 clevels], [1 1], 1:clevels);
- r = interp1([1 clevels], [1 0], 1:clevels);
- elseif zeroInd >= clevels, % Just red
- b = interp1([1 clevels], [0 1], 1:clevels);
- g = interp1([1 clevels], [0 1], 1:clevels);
- r = interp1([1 clevels], [1 1], 1:clevels);
- else
- b = interp1([1 zeroInd clevels], [0 1 0], 1:clevels);
- g = interp1([1 zeroInd clevels], [0 1 1], 1:clevels);
- r = interp1([1 zeroInd clevels], [1 1 0], 1:clevels);
- end
- cmap = [r' g' b'];
- end
- % Generate Red-White color map
- function cmap = red(levels)
- r = ones(levels, 1);
- g = linspace(1, 0, levels)';
- cmap = [r g g];
- end
- %#ok<*INUSD>
- %#ok<*DEFNU>
- %#ok<*INUSL>
heatmap.m, under CC-BY-4.0 · at the source
Overview
- Institute for Cell Biology and Neuroscience, Goethe University, 60438 Frankfurt, Germany
- Biocenter, Department of Zoology II, Emmy-Noether Group Spatial Memory in Insects, University of Würzburg, 97074 Würzburg, Germany
- AG Brain & Behavior, Institute of Biology, Freie Universität, 14195 Berlin, Germany
- Ernst-Strüngmann-Institute for Neuroscience Frankfurt, 60528 Frankfurt am Main, Germany
Abstract
Echolocating bats emit acoustic pulses that get reflected off objects. The spatial information carried by the echoes enables bats to avoid obstacles in darkness. Usually, every pulse is followed by a cascade of echoes arising from multiple objects. By using echolocation sequences where single pulses are followed by echo cascades, we recently demonstrated that cortical neurons predominantly responded to the leading echo. Responses to lagging echoes from a cascade were suppressed, suggesting that spatial information from the most immediate object is processed at the cortex level. In that study, the leading echo was typically the most intense, leaving it unclear whether the echo selectivity was due to echo order or echo level. Here, we recorded from the auditory cortex of anaesthetized Carollia perspicillata, while stimulating the bats with echolocation sequences that contained echo cascades either with echo levels that were equally intense or where the leading echo was less intense than the lagging ones. Our results demonstrate that the echo level has only minor effects on neural processing and that the echo selectivity is mostly caused by the echo order. These results go in line with the neural time window of sensation hypothesis, proposed by Roverud and Grinnell. Whenever the bat hears a pulse, a neural time window opens, and any subsequent high-frequency signal within the spectral range of that pulse is by default classified as an echo, thereby closing the sensation window. This mechanism renders large parts of the cortex less responsive to distant objects, regardless of the echo intensity they produced.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above.
figshare 30883511
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
- 28 September 2026: the link answers (HTTP 200)
4 files
- heatmap.m, MATLAB, 804 lines
- redblue.m, MATLAB, 39 lines
- three_obj_second_MS_2.m, MATLAB, 1,017 lines
- viridis.m, MATLAB, 267 lines
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 4 scripts, each with its path and the digest of its content;
- no match between paragraphs and code yet;
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 2, 28 September 2026
- Publisher: n/a → The Company of Biologists
Version 1, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 3 authors, 5 keywords, 6 MeSH terms, 2 funders, 40 references.
Cite
This paper
Beetz, M. J., Kössl, M., & Hechavarría, J. C. (2026). First come, first served: neuronal processing of multi-echo streams in the auditory cortex of echolocating bats. The Journal of experimental biology, 229(10), jeb252069. https://
BibTeX
@article{beetz2026first,
author = {Beetz, M. Jerome and Kössl, Manfred and Hechavarría, Julio C.},
title = {{First come, first served: neuronal processing of multi-echo streams in the auditory cortex of echolocating bats}},
journal = {The Journal of experimental biology},
year = {2026},
month = may,
volume = {229},
number = {10},
pages = {jeb252069},
publisher = {The Company of Biologists},
issn = {0022-0949},
doi = {10.1242/
url = {https://
pmid = {42023416},
pmcid = {PMC13286348}
}
RIS
TY - JOUR
AU - Beetz, M. Jerome
AU - Kössl, Manfred
AU - Hechavarría, Julio C.
TI - First come, first served: neuronal processing of multi-echo streams in the auditory cortex of echolocating bats
T2 - The Journal of experimental biology
J2 - J Exp Biol
PY - 2026
DA - 2026/
VL - 229
IS - 10
SP - jeb252069
SN - 0022-0949
PB - The Company of Biologists
DO - 10.1242/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1242/
"type": "article-journal",
"title": "First come, first served: neuronal processing of multi-echo streams in the auditory cortex of echolocating bats",
"container-title": "The Journal of experimental biology",
"author": [
{
"family": "Beetz",
"given": "M. Jerome"
},
{
"family": "Kössl",
"given": "Manfred"
},
{
"family": "Hechavarría",
"given": "Julio C."
}
],
"container-title-short":
"volume": "229",
"issue": "10",
"page": "jeb252069",
"DOI": "10.1242/
"PMID": "42023416",
"PMCID": "PMC13286348",
"ISSN": "0022-0949",
"publisher": "The Company of Biologists",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
28
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1038/s42003-026-10319-4 [code]
- Neurons in the bat auditory cortex encode class and complexity of future vocalizations.Journal: Communications biologyIn common: other, systems, 3 references
- [2] doi:10.1016/j.isci.2026.117375 [code]
- Motor priming is associated with widespread recruitment into neural ensembles and more rapid ensemble transitions.Journal: iScienceIn common: Statistics and Machine Learning Toolbox, other, systems
- [3] doi:10.3390/ani16162494 [code]
- Categorical Representation of Numerosity in the Pigeon Entopallium: Coding Format and Temporal Dynamics.Journal: Animals : an open access journal from MDPIIn common: Statistics and Machine Learning Toolbox, other, systems
- [4] doi:10.1016/j.crmeth.2026.101481 [code]
- A hybrid micro-ECoG for functionally targeted multi-site and multi-scale investigation.Journal: Cell reports methodsIn common: Statistics and Machine Learning Toolbox, other, systems
- [5] doi:10.1007/s12021-026-09810-4 [code]
- Meta-Analysis of Face-Validity Indicators in Automatically Detected Dog Sleep Spindles.Journal: NeuroinformaticsIn common: Statistics and Machine Learning Toolbox, other
- [6] doi:10.1016/j.isci.2026.116900 [code]
- Dog brain representations of human facial expressions: Encoding happiness and differentiating specific negative expressions.Journal: iScienceIn common: Statistics and Machine Learning Toolbox, other
- [7] doi:10.1371/journal.pone.0333158 [code]
- Alpha-synuclein overexpression reduces neural activity within a basal ganglia vocal nucleus in a zebra finch model.Journal: PloS oneIn common: Statistics and Machine Learning Toolbox, other
- [8] doi:10.1038/s41598-026-54678-8 [code]
- Neural correlates of appetitive extinction learning: an fMRI study with actively participating pigeons.Journal: Scientific reportsIn common: Statistics and Machine Learning Toolbox, other
- [9] doi:10.1523/jneurosci.1104-25.2026 [code]
- Timbre Encoding in the Inferior Colliculus.Journal: The Journal of neuroscience : the official journal of the Society for NeuroscienceIn common: Statistics and Machine Learning Toolbox, other
- [10] doi:10.1002/mrm.70380 [code]
- The Impact and Reliability of Tissue Segmentation on In Vivo Magnetic Resonance Spectroscopy Metabolite Quantification.Journal: Magnetic resonance in medicineIn common: Statistics and Machine Learning Toolbox, other
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 4 scripts, and 0 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:bc9330d14d36c620…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[.
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
