OSCR

First come, first served: neuronal processing of multi-echo streams in the auditory cortex of echolocating bats.

Code ↔ Paper

The paper beside its authors' code: matches between them have not been computed for this paper yet.

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

  1. function [hImage, hText, hXText] = heatmap(mat, xlab, ylab, textmat, varargin)
  2. % HEATMAP displays a matrix as a heatmap image
  3. %
  4. % USAGE:
  5. % [hImage, hText, hTick] = heatmap(matrix, xlabels, ylabels, textmatrix, 'param', value, ...)
  6. %
  7. % INPUTS:
  8. % * HEATMAP displays "matrix" as an image whose color intensities reflect
  9. % the magnitude of the values in "matrix".
  10. %
  11. % * "xlabels" (and "ylabels") can be either a numeric vector or cell array
  12. % of strings that represent the columns (or rows) of the matrix. If either
  13. % is not specified or empty, no labels will be drawn.
  14. %
  15. % * "textmat" can either be: 1 (or true), in which case the "matrix" values will be
  16. % displayed in each square, a format string, in which case the matrix
  17. % values will be displayed formatted according to the string specified, a numeric
  18. % matrix the size of "matrix", in which case those values will be displayed as
  19. % strings or a cell matrix of strings the size of "matrix", in which case each
  20. % string will be displayed. If not specified or empty, no text will be
  21. % displayed on the image
  22. %
  23. % OTHER PARAMETERS (passed as parameter-value pairs)
  24. % * 'Colormap': Either a matrix of size numLevels-by-3 representing the
  25. % colormap to be used or a string or function handle representing a
  26. % function that returns a colormap, example, 'jet', 'hsv' or @cool.
  27. % Non-standard colormaps available within HEATMAP include 'money' and 'red'.
  28. % By default, the current figure's colormap is used.
  29. %
  30. % * 'ColorLevels': The number of distinct levels in the colormap (default:
  31. % 64). If more levels are specified than are present in the colormap, the
  32. % levels in the colormap are interpolated. If fewer are specified the
  33. % colormap is downsampled.
  34. %
  35. % * 'UseLogColormap': A true/false value which, if true, specifies that the
  36. % intensities displayed should match the log of the "matrix" values. Use
  37. % this if the data is naturally on a logarithmic scale (default: false)
  38. %
  39. % * 'UseFigureColormap': Specifies whether the figure's colormap should be
  40. % used. If false, the color intensities after applying the
  41. % specified/default colormap will be hardcoded, so that the image will be
  42. % independent of the figure's colormap. If this option is true, the figure
  43. % colormap in the end will be replaced by specified/default colormap.
  44. % (default = true)
  45. %
  46. % * 'NaNColor': A 3-element [R G B] vector specifying the color used to display NaN
  47. % or missing value. [0 0 0] corresponds to black and [1 1 1] to white. By
  48. % default MATLAB displays NaN values using the color assigned to the
  49. % lowest value in the colormap. Specifying this option automatically sets
  50. % the 'UseFigureColormap' option to false because the color mapping must
  51. % be computed prior to setting the nan color.
  52. %
  53. % * 'MinColorValue': A scalar number corresponding to the value of the data
  54. % that is mapped to the lowest color of the colormap. By default this is
  55. % the minimum value of the matrix input.
  56. %
  57. % * 'MaxColorValue': A scalar number corresponding to the value of the data
  58. % that is mapped to the highest color of the colormap. By default this is
  59. % the maximum value of the matrix input.
  60. %
  61. % * 'Parent': Handle to an axes object
  62. %
  63. % * 'TextColor': Either a color specification of all the text displayed on
  64. % the image or a string 'xor' which sets the EraseMode property of the text
  65. % objects to 'xor'. This will display all the text labels in a color that
  66. % contrasts its background.
  67. %
  68. % * 'FontSize': The initial fontSize of the text labels on the image. As
  69. % the image size is scaled the fontSize is shrunk appropriately.
  70. %
  71. % * 'ColorBar': Display colorbar. The corresponding value parameter should
  72. % be either logical 1 or 0 or a cell array of any additional parameters
  73. % you wish to pass to the colorbar function (such as location)
  74. %
  75. % * 'GridLines': Draw grid lines separating adjacent sections of the
  76. % heatmap. The value of the parameter is a LineStyle specification, for example,
  77. % :, -, -. or --. By default, no grid lines are drawn.
  78. %
  79. % * 'TickAngle': Angle of rotation of tick labels on x-axis. (Default: 0)
  80. %
  81. % * 'ShowAllTicks': Set to 1 or true to force all ticks and labels to be
  82. % drawn. This can make the axes labels look crowded. (Default: false)
  83. %
  84. % * 'TickFontSize': Font size of the X and Y tick labels. Default value is
  85. % the default axes font size, usually 10. Set to a lower value if many
  86. % tick labels are being displayed
  87. %
  88. % * 'TickTexInterpreter': Set to 1 or true to render tick labels using a TEX
  89. % interpreter. For example, '_b' and '^o' would be rendered as subscript
  90. % b and the degree symbol with the TEX interpreter. This parameter is only
  91. % available in MATLAB R2014b and above (Default: false)
  92. %
  93. % OUTPUTS:
  94. % * hImage: handle to the image object
  95. % * hText : handle to the text objects (empty if no text labels are drawn)
  96. % * hTick : handle to the X-tick label text objects if tick angle is not 0
  97. % (empty otherwise)
  98. %
  99. % Notes:
  100. % * The 'money' colormap displays a colormap where 0 values are mapped to
  101. % white, negative values displayed in varying shades of red and positive
  102. % values in varying shades of green
  103. % * The 'red' colormap maps 0 values to white and higher values to red
  104. %
  105. % EXAMPLES:
  106. % data = reshape(sort(randi(100,10)),10,10)-50;
  107. % heatmap(data, cellstr(('A':'J')'), mean(data,2), '%0.0f%%',...
  108. % 'Colormap', 'money', 'Colorbar', true, 'GridLines', ':',...
  109. % 'TextColor', 'b')
  110. % For detailed examples, see the associated document heatmap_examples.m
  111. % Copyright The MathWorks, Inc. 2009-2014
  112. % Handle missing inputs
  113. if nargin < 1, error('Heatmap requires at least one input argument'); end
  114. if nargin < 2, xlab = []; end
  115. if nargin < 3, ylab = []; end
  116. if nargin < 4, textmat = []; end
  117. % Parse parameter/value inputs
  118. p = parseInputs(mat, varargin{:});
  119. % Get heatmap axes information if it already exists
  120. p.axesInfo = getHeatmapAxesInfo(p.hAxes);
  121. % Calculate the colormap based on inputs
  122. p = calculateColormap(p, mat);
  123. % Create heatmap image
  124. p = plotHeatmap(p, mat); % New properties hImage and cdata added
  125. % Generate grid lines if selected
  126. generateGridLines(p);
  127. % Set axes labels
  128. [p, xlab, ylab, hXText, origPos] = setAxesTickLabels(p, xlab, ylab);
  129. % Set text labels
  130. [p, displayText, fontScaleFactor] = setTextLabels(p, mat, textmat);
  131. % Add colorbar if selected
  132. addColorbar(p, mat, textmat)
  133. % Store heatmap properties in axes for callbacks
  134. axesInfo = struct('Type', 'heatmap', 'Parameters', p, 'FontScaleFactor', ...
  135. fontScaleFactor, 'mat', mat, 'hXText', hXText, ...
  136. 'origAxesPos', origPos);
  137. axesInfo.xlab = xlab;
  138. axesInfo.ylab = ylab;
  139. axesInfo.displayText = displayText;
  140. set(p.hAxes, 'UserData', axesInfo);
  141. % Define callbacks
  142. dObj = datacursormode(p.hFig);
  143. set(dObj, 'Updatefcn', @cursorFun);
  144. zObj = zoom(p.hFig);
  145. set(zObj, 'ActionPostCallback', @(obj,evd)updateLabels(evd.Axes,true));
  146. pObj = pan(p.hFig);
  147. % set(pObj, 'ActionPreCallback', @prePan);
  148. set(pObj, 'ActionPostCallback', @(obj,evd)updateLabels(evd.Axes,true));
  149. set(p.hFig, 'ResizeFcn', @resize)
  150. % Set outputs
  151. hImage = p.hImage;
  152. hText = p.hText;
  153. end
  154. % ---------------------- Heatmap Creation Functions ----------------------
  155. % Parse PV inputs & return structure of parameters
  156. function param = parseInputs(mat, varargin)
  157. p = inputParser;
  158. p.addParamValue('Colormap',[]); %#ok<*NVREPL>
  159. p.addParamValue('ColorLevels',[]);
  160. p.addParamValue('TextColor',[0 0 0]);
  161. p.addParamValue('UseFigureColormap',true);
  162. p.addParamValue('UseLogColormap',false);
  163. p.addParamValue('Parent',NaN);
  164. p.addParamValue('FontSize',[]);
  165. p.addParamValue('Colorbar',[]);
  166. p.addParamValue('GridLines','none');
  167. p.addParamValue('TickAngle',0);
  168. p.addParamValue('ShowAllTicks',false);
  169. p.addParamValue('TickFontSize',[]);
  170. p.addParamValue('TickTexInterpreter',false);
  171. p.addParamValue('NaNColor', [NaN NaN NaN], @(x)isnumeric(x) && length(x)==3 && all(x>=0) && all(x<=1));
  172. p.addParamValue('MinColorValue', nan, @(x)isnumeric(x) && isscalar(x));
  173. p.addParamValue('MaxColorValue', nan, @(x)isnumeric(x) && isscalar(x));
  174. p.parse(varargin{:});
  175. param = p.Results;
  176. if ~ishandle(param.Parent) || ~strcmp(get(param.Parent,'type'), 'axes')
  177. param.Parent = gca;
  178. end
  179. ind = ~isinf(mat(:)) | isnan(mat(:));
  180. if isnan(param.MinColorValue)
  181. param.MinColorValue = min(mat(ind));
  182. end
  183. if isnan(param.MaxColorValue)
  184. param.MaxColorValue = max(mat(ind));
  185. end
  186. % Add a few other parameters
  187. param.hAxes = param.Parent;
  188. param.hFig = ancestor(param.hAxes, 'figure');
  189. param.IsGraphics2 = ~verLessThan('matlab','8.4');
  190. param.ExplicitlyComputeImage = ~all(isnan(param.NaNColor)) ... NaNColor is specified
  191. || ~param.IsGraphics2 && ~param.UseFigureColormap;
  192. % if param.IsGraphics2 && ~param.UseFigureColormap && ~isempty(param.ColorBar) % graphics v2
  193. % 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');
  194. % end
  195. end
  196. % Visualize heatmap image
  197. function p = plotHeatmap(p, mat)
  198. p.cdata = [];
  199. if p.UseLogColormap
  200. p.Colormap = resamplecmap(p.Colormap, p.ColorLevels, ...
  201. logspace(0,log10(p.ColorLevels),p.ColorLevels));
  202. end
  203. if p.ExplicitlyComputeImage
  204. % Calculate the color data explicitly and then display it as an image.
  205. n = p.MinColorValue;
  206. x = p.MaxColorValue;
  207. if x == n, x = n+1; end
  208. p.cdata = round((mat-n)/(x-n)*(p.ColorLevels-1)+1);
  209. %p.cdata = ceil((mat-n)/(x-n)*p.ColorLevels);
  210. p.cdata(p.cdata<1) = 1; % Clipping
  211. p.cdata(p.cdata>p.ColorLevels) = p.ColorLevels; % Clipping
  212. nanInd = find(isnan(p.cdata));
  213. p.cdata(isnan(p.cdata)) = 1;
  214. p.cdata = reshape(p.Colormap(p.cdata(:),:),[size(p.cdata) 3]);
  215. % Handle NaNColor case
  216. if ~all(isnan(p.NaNColor))
  217. p.cdata(nanInd ) = p.NaNColor(1); % Set red color level of nan indices
  218. p.cdata(nanInd + numel(p.cdata)/3) = p.NaNColor(2); % Set green color level of nan indices
  219. p.cdata(nanInd + 2*numel(p.cdata)/3) = p.NaNColor(3); % set blue color level of nan indices
  220. end
  221. % Add a small dummy image so that colorbar subsequently works
  222. [indr, indc] = find(~isnan(mat),1);
  223. imagesc(indr, indc, mat(indr,indc),'Parent',p.hAxes);
  224. nextplot = get(p.hAxes,'nextplot');
  225. set(p.hAxes,'nextplot','add');
  226. p.hImage = image(p.cdata, 'Parent', p.hAxes);
  227. set(p.hAxes,'nextplot',nextplot);
  228. axis(p.hAxes,'tight');
  229. else
  230. % Use a scaled image plot. Axes CLims and colormap will be set later
  231. p.hImage = imagesc(mat, 'Parent', p.hAxes);
  232. end
  233. set(p.hAxes, 'CLim', [p.MinColorValue p.MaxColorValue]); % Ensure proper clipping for colorbar
  234. if p.UseFigureColormap
  235. set(p.hFig,'Colormap',p.Colormap);
  236. elseif p.IsGraphics2
  237. % Set the axes colormap and limits
  238. colormap(p.hAxes, p.Colormap);
  239. %set(p.hAxes, 'CLim', [p.MinColorValue p.MaxColorValue]);
  240. end
  241. end
  242. % Generate grid lines
  243. function generateGridLines(p)
  244. if ~strcmp(p.GridLines,'none')
  245. xlim = get(p.hAxes,'XLim');
  246. ylim = get(p.hAxes,'YLim');
  247. for i = 1:diff(xlim)-1
  248. line('Parent',p.hAxes,'XData',[i i]+.5, 'YData', ylim, 'LineStyle', p.GridLines);
  249. end
  250. for i = 1:diff(ylim)-1
  251. line('Parent',p.hAxes,'XData',xlim, 'YData', [i i]+.5, 'LineStyle', p.GridLines);
  252. end
  253. end
  254. end
  255. % Add color bar
  256. function addColorbar(p, mat, textmat)
  257. if isempty(p.Colorbar)
  258. return;
  259. elseif iscell(p.Colorbar)
  260. c = colorbar(p.Colorbar{:});
  261. else
  262. c = colorbar;
  263. end
  264. if p.IsGraphics2
  265. c.Limits = p.hAxes.CLim;
  266. ticks = get(c,'Ticks');
  267. else
  268. if p.ExplicitlyComputeImage || ~p.UseFigureColormap
  269. d = findobj(get(c,'Children'),'Tag','TMW_COLORBAR'); % Image
  270. set(d,'YData', get(p.hAxes,'CLim'));
  271. set(c,'YLim', get(p.hAxes,'CLim'));
  272. end
  273. ticks = get(c,'YTick');
  274. tickAxis = 'Y';
  275. if isempty(ticks)
  276. ticks = get(c,'XTick');
  277. tickAxis = 'X';
  278. end
  279. end
  280. if ~isempty(ticks)
  281. if ischar(textmat) % If format string, format colorbar ticks in the same way
  282. ticklabels = arrayfun(@(x){sprintf(textmat,x)},ticks);
  283. else
  284. ticklabels = num2str(ticks(:));
  285. end
  286. if p.IsGraphics2
  287. set(c, 'TickLabels', ticklabels);
  288. else
  289. set(c, [tickAxis 'TickLabel'], ticklabels);
  290. end
  291. end
  292. end
  293. % ------------------------- Tick Label Functions -------------------------
  294. % Set axes tick labels
  295. function [p, xlab, ylab, hXText, origPos] = setAxesTickLabels(p, xlab, ylab)
  296. if isempty(p.axesInfo) % Not previously a heatmap axes
  297. origPos = [get(p.hAxes,'Position') get(p.hAxes,'OuterPosition')];
  298. else
  299. origPos = p.axesInfo.origAxesPos;
  300. set(p.hAxes, 'Position', origPos(1:4), 'OuterPosition', origPos(5:8));
  301. end
  302. if isempty(p.TickFontSize)
  303. p.TickFontSize = get(p.hAxes, 'FontSize');
  304. else
  305. set(p.hAxes, 'FontSize', p.TickFontSize);
  306. end
  307. if isempty(ylab) % No ticks or labels
  308. set(p.hAxes,'YTick',[],'YTickLabel','');
  309. else
  310. if isnumeric(ylab) % Numeric tick labels
  311. ylab = arrayfun(@(x){num2str(x)},ylab);
  312. end
  313. if ischar(ylab)
  314. ylab = cellstr(ylab);
  315. end
  316. ytick = get(p.hAxes, 'YTick');
  317. ytick(ytick<1|ytick>length(ylab)) = [];
  318. if p.ShowAllTicks || length(ytick) > length(ylab)
  319. ytick = 1:length(ylab);
  320. end
  321. set(p.hAxes,'YTick',ytick,'YTickLabel',ylab(ytick));
  322. end
  323. if p.IsGraphics2
  324. if p.TickTexInterpreter
  325. set(p.hAxes,'TickLabelInterpreter','tex');
  326. else
  327. set(p.hAxes,'TickLabelInterpreter','none');
  328. end
  329. end
  330. % Xlabels are trickier because they could have a TickAngle
  331. hXText = []; % Default value
  332. if isempty(xlab)
  333. set(p.hAxes,'XTick',[],'XTickLabel','');
  334. else
  335. if isnumeric(xlab)
  336. xlab = arrayfun(@(x){num2str(x)},xlab);
  337. end
  338. if ischar(xlab)
  339. xlab = cellstr(xlab);
  340. end
  341. xtick = get(p.hAxes, 'XTick');
  342. xtick(xtick<1|xtick>length(xlab)) = [];
  343. if p.ShowAllTicks || length(xtick) > length(xlab)
  344. xtick = 1:length(xlab);
  345. end
  346. if p.IsGraphics2
  347. set(p.hAxes,'XTick',xtick,'XTickLabel',xlab(xtick),'XTickLabelRotation', p.TickAngle);
  348. else
  349. if p.TickAngle == 0
  350. set(p.hAxes,'XTick',xtick,'XTickLabel',xlab(xtick));
  351. else
  352. hXText = createXTicks(p.hAxes, p.TickAngle, xtick, xlab(xtick), p.TickTexInterpreter);
  353. adjustAxesToAccommodateTickLabels(p.hAxes, hXText);
  354. end
  355. end
  356. end
  357. end
  358. % Create Rotated X Tick Labels (Graphics v1)
  359. function hXText = createXTicks(hAxes, tickAngle, xticks, xticklabels, texInterpreter)
  360. axXLim = get(hAxes, 'XLim');
  361. [xPos, yPos] = calculateTextTickPositions(hAxes, axXLim, xticks);
  362. if texInterpreter
  363. interpreter = 'tex';
  364. else
  365. interpreter = 'none';
  366. end
  367. hXText = text(xPos, yPos, cellstr(xticklabels), 'Units', 'normalized', ...
  368. 'Parent', hAxes, 'FontSize', get(hAxes,'FontSize'), ...
  369. 'HorizontalAlignment', 'right', 'Rotation', tickAngle,...
  370. 'Interpreter', interpreter);
  371. set(hAxes, 'XTick', xticks, 'XTickLabel', '');
  372. end
  373. % Calculate positions of X tick text objects in normalized units
  374. function [xPos, yPos] = calculateTextTickPositions(hAxes, xlim, ticks)
  375. oldunits = get(hAxes,'Units');
  376. set(hAxes,'units','pixels');
  377. axPos = get(hAxes,'position');
  378. set(hAxes,'units',oldunits);
  379. xPos = (ticks - xlim(1))/diff(xlim);
  380. %yPos = -.08 * ones(size(xPos));
  381. yPos = -7.82/axPos(4) * ones(size(xPos));
  382. end
  383. % Adjust axes and tick positions so that everything fits well on screen
  384. function adjustAxesToAccommodateTickLabels(hAxes, hXText)
  385. % The challenge here is that the axes container, especially in a subplot is
  386. % not well defined. The outer position property does not fully span or
  387. % contain the x tick text objects. So here we just shrink the axes height
  388. % just a little so that the axes and tick labels take the same room as the
  389. % axes would have without the ticks.
  390. [axPosP, axPosN, axOPP, axOPN, coPosP, textPosP] = ...
  391. getGraphicsObjectsPositions(hAxes, hXText); %#ok<ASGLU>
  392. header = axOPP(4) + axOPP(2) - axPosP(4) - axPosP(2); % Distance between top of axes and container in pixels;
  393. delta = 5; % To adjust for overlap between area designated for regular ticks and area occupied by rotated ticks
  394. axHeightP = axOPP(4) - header - delta - textPosP(4);
  395. % Fudge axis position if labels are taking up too much room
  396. if textPosP(4)/(textPosP(4)+axHeightP) > .7 % It's taking up more than 70% of total height
  397. axHeightP = (1/.7-1) * textPosP(4); % Minimum axis
  398. end
  399. axHeightN = max(0.0001, axHeightP / coPosP(4));
  400. axPosN = axPosN + [0 axPosN(4)-axHeightN 0 axHeightN-axPosN(4)];
  401. set(hAxes,'Position', axPosN)
  402. end
  403. % Calculate graphics objects positions in pixels and normalized units
  404. function [axPosP, axPosN, axOPP, axOPN, coPosP, textPosP, textPosN] =...
  405. getGraphicsObjectsPositions(hAxes, hXText)
  406. axPosN = get(hAxes, 'Position'); axOPN = get(hAxes, 'OuterPosition');
  407. set(hAxes,'Units','Pixels');
  408. axPosP = get(hAxes, 'Position'); axOPP = get(hAxes, 'OuterPosition');
  409. set(hAxes,'Units','Normalized');
  410. hContainer = get(hAxes,'Parent');
  411. units = get(hContainer,'Units');
  412. set(hContainer,'Units','Pixels');
  413. coPosP = get(hContainer,'Position');
  414. set(hContainer,'Units',units);
  415. set(hXText,'Units','pixels'); % Measure height in pixels
  416. extents = get(hXText,'Extent'); % Get heights for all text objects
  417. extents = vertcat(extents{:}); % Collect heights in one matrix
  418. textPosP = [min(extents(:,1)) min(extents(:,2)) ...
  419. max(extents(:,3)+extents(:,1))-min(extents(:,1)) ...
  420. max(extents(:,4))]; % Find dimensions of text label block
  421. set(hXText,'Units','normalized'); % Restore previous behavior
  422. extents = get(hXText,'Extent'); % Get heights for all text objects
  423. extents = vertcat(extents{:}); % Collect heights in one matrix
  424. textPosN = [min(extents(:,1)) min(extents(:,2)) ...
  425. max(extents(:,3)+extents(:,1))-min(extents(:,1)) ...
  426. max(extents(:,4))]; % Find dimensions of text label block
  427. end
  428. % -------------------------- Callback Functions --------------------------
  429. % Update x-, y- and text-labels with respect to axes limits
  430. function updateLabels(hAxes, axesLimitsChanged)
  431. axInfo = getHeatmapAxesInfo(hAxes);
  432. if isempty(axInfo), return; end
  433. p = axInfo.Parameters;
  434. % Update text font size to fill the square
  435. if ~isempty(p.hText) && ishandle(p.hText(1))
  436. fs = axInfo.FontScaleFactor * getBestFontSize(hAxes);
  437. if fs > 0
  438. set(p.hText,'fontsize',fs,'visible','on');
  439. else
  440. set(p.hText,'visible','off');
  441. end
  442. end
  443. if axesLimitsChanged && ~isempty(axInfo.displayText) % If limits change & text labels are displayed
  444. % Get positions of text objects
  445. textPos = get(p.hText,'Position');
  446. textPos = vertcat(textPos{:});
  447. % Get axes limits
  448. axXLim = get(hAxes, 'XLim');
  449. axYLim = get(hAxes, 'YLim');
  450. % Find text objects within axes limit
  451. ind = textPos(:,1) > axXLim(1) & textPos(:,1) < axXLim(2) & ...
  452. textPos(:,2) > axYLim(1) & textPos(:,2) < axYLim(2);
  453. set(p.hText(ind), 'Visible', 'on');
  454. set(p.hText(~ind), 'Visible', 'off');
  455. end
  456. % Modify Y Tick Labels
  457. if ~isempty(axInfo.ylab)
  458. axYLim = get(hAxes, 'YLim');
  459. if p.ShowAllTicks
  460. yticks = ceil(axYLim(1)):floor(axYLim(2));
  461. else
  462. set(hAxes, 'YTickMode', 'auto');
  463. yticks = get(hAxes, 'YTick');
  464. yticks = yticks( yticks == floor(yticks) );
  465. yticks(yticks<1|yticks>length(axInfo.ylab)) = [];
  466. end
  467. ylabels = repmat({''},1,max(yticks));
  468. ylabels(1:length(axInfo.ylab)) = axInfo.ylab;
  469. set(hAxes, 'YTick', yticks, 'YTickLabel', ylabels(yticks));
  470. end
  471. if ~isempty(axInfo.xlab)
  472. axXLim = get(hAxes, 'XLim');
  473. if p.ShowAllTicks
  474. xticks = ceil(axXLim(1)):floor(axXLim(2));
  475. else
  476. set(hAxes, 'XTickMode', 'auto');
  477. xticks = get(hAxes, 'XTick');
  478. xticks = xticks( xticks == floor(xticks) );
  479. xticks(xticks<1|xticks>length(axInfo.xlab)) = [];
  480. end
  481. xlabels = repmat({''},1,max(xticks));
  482. xlabels(1:length(axInfo.xlab)) = axInfo.xlab;
  483. if ~isempty(axInfo.hXText) % Rotated X tick labels exist
  484. try delete(axInfo.hXText); end %#ok<TRYNC>
  485. axInfo.hXText = createXTicks(hAxes, p.TickAngle, xticks, xlabels(xticks), p.TickTexInterpreter);
  486. set(hAxes, 'UserData', axInfo);
  487. else
  488. set(hAxes, 'XTick', xticks, 'XTickLabel', xlabels(xticks));
  489. end
  490. %adjustAxesToAccommodateTickLabels(hAxes, axInfo.hXText)
  491. end
  492. end
  493. % Callback for data cursor
  494. function output_txt = cursorFun(obj, eventdata)
  495. hAxes = ancestor(eventdata.Target, 'axes');
  496. axInfo = getHeatmapAxesInfo(hAxes);
  497. pos = eventdata.Position;
  498. if ~isempty(axInfo)
  499. try
  500. val = axInfo.displayText{pos(2), pos(1)};
  501. catch %#ok<CTCH>
  502. val = num2str(axInfo.mat(pos(2), pos(1)));
  503. end
  504. if isempty(axInfo.xlab), i = int2str(pos(1)); else i = axInfo.xlab{pos(1)}; end
  505. if isempty(axInfo.ylab), j = int2str(pos(2)); else j = axInfo.ylab{pos(2)}; end
  506. output_txt = sprintf('X: %s\nY: %s\nVal: %s', i, j, val);
  507. else
  508. if length(pos) == 2
  509. output_txt = sprintf('X: %0.4g\nY: %0.4g', pos(1), pos(2));
  510. else
  511. output_txt = sprintf('X: %0.4g\nY: %0.4g\nZ: %0.4g', pos(1), pos(2), pos(3));
  512. end
  513. end
  514. end
  515. % Callback for resize event
  516. function resize(obj, evd)
  517. hAxes = findobj(obj, 'type', 'axes');
  518. for i = 1:length(hAxes)
  519. updateLabels(hAxes(i), false);
  520. end
  521. end
  522. % Extract heatmap parameters for callback
  523. function axInfo = getHeatmapAxesInfo(axH)
  524. axInfo = get(axH, 'UserData');
  525. try
  526. if ~strcmp(axInfo.Type, 'heatmap')
  527. axInfo = [];
  528. end
  529. catch %#ok<CTCH>
  530. axInfo = [];
  531. end
  532. end
  533. % ------------------------- Text Label Functions -------------------------
  534. % Create text labels
  535. function [p, displaytext, factor] = setTextLabels(p, mat, textmat)
  536. if isempty(textmat)
  537. p.hText = [];
  538. displaytext = {};
  539. factor = 0;
  540. return
  541. end
  542. if isscalar(textmat) && textmat % If true convert mat to text
  543. displaytext = arrayfun(@(x){num2str(x)},mat);
  544. elseif ischar(textmat) % If a format string, convert mat to text with specific format
  545. displaytext = arrayfun(@(x){sprintf(textmat,x)},mat);
  546. elseif isnumeric(textmat) && numel(textmat)==numel(mat) % If numeric, convert to text
  547. displaytext = arrayfun(@(x){num2str(x)},textmat);
  548. elseif iscellstr(textmat) && numel(textmat)==numel(mat) % If cell array of strings, it is already formatted
  549. displaytext = textmat;
  550. else
  551. error('texmat is incorrectly specified');
  552. end
  553. if ischar(p.TextColor) && strcmp(p.TextColor,'xor')
  554. colorprop = 'EraseMode';
  555. else
  556. colorprop = 'Color';
  557. end
  558. autoFontSize = getBestFontSize(p.hAxes);
  559. if isempty(p.FontSize)
  560. p.FontSize = autoFontSize;
  561. end
  562. [xpos,ypos] = meshgrid(1:size(mat,2),1:size(mat,1));
  563. if p.FontSize > 0
  564. p.hText = text(xpos(:),ypos(:),displaytext(:),'FontSize',p.FontSize,...
  565. 'HorizontalAlignment','center', colorprop, p.TextColor,'Parent',p.hAxes);
  566. else
  567. p.hText = text(xpos(:),ypos(:),displaytext(:),'Visible','off',...
  568. 'HorizontalAlignment','center', colorprop, p.TextColor,'Parent',p.hAxes);
  569. end
  570. % Calculate factor to scale font size in future callbacks
  571. factor = p.FontSize/autoFontSize;
  572. if isnan(factor), factor = 1; end
  573. if isinf(factor), factor = p.FontSize/6; end
  574. % % Set up listeners to handle appropriate zooming
  575. % addlistener(p.hAxes,{'XLim','YLim'},'PostSet',@(obj,evdata)resizeText);
  576. % try
  577. % addlistener(p.hFig,'SizeChange',@(obj,evdata)resizeText);
  578. % catch
  579. % addlistener(p.hFig,'Resize',@(obj,evdata)resizeText);
  580. % end
  581. % function resizeText
  582. % if ~isempty(hText) && ishandle(hText(1))
  583. % fs = factor*getBestFontSize(hAxes);
  584. % if fs > 0
  585. % set(hText,'fontsize',fs,'visible','on');
  586. % else
  587. % set(hText,'visible','off');
  588. % end
  589. % end
  590. % end
  591. end
  592. % Guess best font size from axes size using heuristics
  593. function fs = getBestFontSize(imAxes)
  594. hFig = ancestor(imAxes,'figure');
  595. magicNumber = 80;
  596. nrows = diff(get(imAxes,'YLim'));
  597. ncols = diff(get(imAxes,'XLim'));
  598. if ncols < magicNumber && nrows < magicNumber
  599. ratio = max(get(hFig,'Position').*[0 0 0 1])/max(nrows,ncols);
  600. elseif ncols < magicNumber
  601. ratio = max(get(hFig,'Position').*[0 0 0 1])/ncols;
  602. elseif nrows < magicNumber
  603. ratio = max(get(hFig,'Position').*[0 0 0 1])/nrows;
  604. else
  605. ratio = 1;
  606. end
  607. fs = min(9,ceil(ratio/4)); % the gold formula
  608. if fs < 4
  609. fs = 0;
  610. end
  611. end
  612. % -------------------------- Colormap Functions --------------------------
  613. % Determine the colormap to use
  614. function p = calculateColormap(p, mat)
  615. if isempty(p.Colormap)
  616. if p.IsGraphics2 && ~p.UseFigureColormap
  617. p.Colormap = colormap(p.hAxes);
  618. else
  619. p.Colormap = get(p.hFig,'Colormap');
  620. end
  621. if isempty(p.ColorLevels)
  622. p.ColorLevels = size(p.Colormap,1);
  623. else
  624. p.Colormap = resamplecmap(p.Colormap, p.ColorLevels);
  625. end
  626. elseif ischar(p.Colormap) || isa(p.Colormap,'function_handle')
  627. if isempty(p.ColorLevels), p.ColorLevels = 64; end
  628. if strcmp(p.Colormap, 'money')
  629. p.Colormap = money(mat, p.ColorLevels);
  630. else
  631. p.Colormap = feval(p.Colormap,p.ColorLevels);
  632. end
  633. elseif iscell(p.Colormap)
  634. p.Colormap = feval(p.Colormap{1}, p.Colormap{2:end});
  635. p.ColorLevels = size(p.Colormap,1);
  636. elseif isnumeric(p.Colormap) && size(p.Colormap,2) == 3
  637. p.ColorLevels = size(p.Colormap,1);
  638. else
  639. error('Incorrect value for colormap parameter');
  640. end % p.Colormap is now a p.ColorLevels-by-3 rgb vector
  641. assert(p.ColorLevels == size(p.Colormap,1));
  642. end
  643. % Resample a colormap by interpolation or decimation
  644. function cmap = resamplecmap(cmap, clevels, xi)
  645. t = cmap;
  646. if nargin < 3
  647. xi = linspace(1,clevels,size(t,1));
  648. end
  649. xi([1 end]) = [1 clevels]; % These need to be exact for the interpolation to
  650. % work and we don't want machine precision messing it up
  651. cmap = [interp1(xi, t(:,1), 1:clevels);...
  652. interp1(xi, t(:,2), 1:clevels);...
  653. interp1(xi, t(:,3), 1:clevels)]';
  654. end
  655. % Generate Red-White-Green color map
  656. function cmap = money(data, clevels)
  657. % Function to make the heatmap have the green, white and red effect
  658. n = min(data(:));
  659. x = max(data(:));
  660. if x == n, x = n+1; end
  661. zeroInd = round(-n/(x-n)*(clevels-1)+1);
  662. if zeroInd <= 1 % Just green
  663. b = interp1([1 clevels], [1 0], 1:clevels);
  664. g = interp1([1 clevels], [1 1], 1:clevels);
  665. r = interp1([1 clevels], [1 0], 1:clevels);
  666. elseif zeroInd >= clevels, % Just red
  667. b = interp1([1 clevels], [0 1], 1:clevels);
  668. g = interp1([1 clevels], [0 1], 1:clevels);
  669. r = interp1([1 clevels], [1 1], 1:clevels);
  670. else
  671. b = interp1([1 zeroInd clevels], [0 1 0], 1:clevels);
  672. g = interp1([1 zeroInd clevels], [0 1 1], 1:clevels);
  673. r = interp1([1 zeroInd clevels], [1 1 0], 1:clevels);
  674. end
  675. cmap = [r' g' b'];
  676. end
  677. % Generate Red-White color map
  678. function cmap = red(levels)
  679. r = ones(levels, 1);
  680. g = linspace(1, 0, levels)';
  681. cmap = [r g g];
  682. end
  683. %#ok<*INUSD>
  684. %#ok<*DEFNU>
  685. %#ok<*INUSL>

heatmap.m, under CC-BY-4.0 · at the source

Overview

Authors: M. Jerome Beetz1,2, Manfred Kössl1, Julio C. Hechavarría1,3,4
  1. Institute for Cell Biology and Neuroscience, Goethe University, 60438 Frankfurt, Germany
  2. Biocenter, Department of Zoology II, Emmy-Noether Group Spatial Memory in Insects, University of Würzburg, 97074 Würzburg, Germany
  3. AG Brain & Behavior, Institute of Biology, Freie Universität, 14195 Berlin, Germany
  4. Ernst-Strüngmann-Institute for Neuroscience Frankfurt, 60528 Frankfurt am Main, Germany
Journal: The Journal of experimental biology, volume 229, issue 10, article jeb252069
Dates: received 16 December 2025; accepted 17 April 2026; published online 28 May 2026; in print May 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1242/jeb.252069 · PMID 42023416 · PMCID PMC13286348 · OpenAlex W7155378440
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: other (organism), systems (subfield)
Methods: Connectivity, Statistics, Spectral & time-frequency, Single-unit activity, calcium imaging
Keywords: Echolocation, Echo delay, Biosonar, Electrophysiology, Distance coding
MeSH: Auditory Cortex*, Chiroptera*, Echolocation*, Neurons*, Acoustic Stimulation, Animals (* major topic)
Topic: Bat Biology and Ecology Studies (Ecology, Evolution, Behavior and Systematics, Agricultural and Biological Sciences), according to OpenAlex
Funding: Deutsche Forschungsgemeinschaft (KO 987/12-2, BE 8388/1-1, 520223571, 525004430); University of Würzburg
Citations: cited by 1 paper (Europe PMC); 40 references in the paper

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

License: CC-BY-4.0
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Languages: MATLAB (4)
Size: 55 files, 4 scripts
Software Heritage: not checked
Found in: the end of the paper
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
  • 28 September 2026: the link answers (HTTP 200)
4 files

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://doi.org/10.1242/jeb.252069

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/jeb.252069},
url = {https://doi.org/10.1242/jeb.252069},
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/05/28
VL - 229
IS - 10
SP - jeb252069
SN - 0022-0949
PB - The Company of Biologists
DO - 10.1242/jeb.252069
UR - https://doi.org/10.1242/jeb.252069
LA - en
ER -

CSL-JSON

{
"id": "10.1242/jeb.252069",
"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": "J Exp Biol",
"volume": "229",
"issue": "10",
"page": "jeb252069",
"DOI": "10.1242/jeb.252069",
"PMID": "42023416",
"PMCID": "PMC13286348",
"ISSN": "0022-0949",
"publisher": "The Company of Biologists",
"URL": "https://doi.org/10.1242/jeb.252069",
"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 biology
In 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: iScience
In 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 MDPI
In 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 methods
In 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: Neuroinformatics
In 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: iScience
In 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 one
In 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 reports
In 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 Neuroscience
In 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 medicine
In 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.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

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.