{"id":415,"date":"2019-10-21T09:51:29","date_gmt":"2019-10-21T07:51:29","guid":{"rendered":"http:\/\/benediktehinger.de\/blog\/science\/?p=415"},"modified":"2019-10-22T09:12:04","modified_gmt":"2019-10-22T07:12:04","slug":"eeg-erp-rounding-event-latencies","status":"publish","type":"post","link":"https:\/\/benediktehinger.de\/blog\/science\/eeg-erp-rounding-event-latencies\/","title":{"rendered":"EEG\/ERP rounding event latencies"},"content":{"rendered":"\n<p><strong>22.10.2019 Edit:<\/strong> Thanks <a href=\"https:\/\/twitter.com\/Matt_Craddock\">Matt Craddock<\/a>, I understand the source of the problem better. He mentioned that this should not occur if the amplifiers record the triggers as trigger channels (before converting it to events). And mentions that this could happen through downsampling. Indeed after checking in the dataset I used it was downsampled from 1024 to 512Hz. This made many eventlatencies ~ X.50001, which will be uprounded with round and floored with floor. This gives some context to the problem.<br><a href=\"https:\/\/twitter.com\/Matt_Craddock\/status\/1186304549801385985\">Full discussion on twitter<\/a><\/p>\n\n\n\n<p>TLDR; EEGlab allows for non-integer event latencies (in units of samples).  Eeglab chose floor(latency), while others e.g. unfold &amp; fieldtrip choose round(latency) to round the latency to samples. This leads to differences between toolboxes, in my example of up to 1.5\u00b5V (or ~25% ERP magnitude). Importantly, this probably <strong>does not introduce bias<\/strong> between conditions<\/p>\n\n\n\n<p>This is an ERP, actually its two ERPs. One is calculated using the &#8220;unfold&#8221; toolbox and one using eeglab&#8217;s pop_epoch function<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/erp-1024x828.png\" alt=\"\" class=\"wp-image-417\" width=\"768\" height=\"621\" srcset=\"https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/erp-1024x828.png 1024w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/erp-300x243.png 300w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/erp-768x621.png 768w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/erp.png 1427w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><figcaption>Elec: PO8, average reference, 1280 trials, 512Hz, very typical experiment with single stimulus presentation, no task<\/figcaption><\/figure><\/div>\n\n\n\n<p>If you look very closely, you can see that they are not identical, even though they should be. So &#8211; whats the difference?<\/p>\n\n\n\n<p>It turns out that EEGlab saves event latencies in samples (e.g. stimulus is starting at sample 213), but also allows non-integer latencies (e.g. stimulus is starting between 212 and 213, to be exact: at sample 212.7). This makes sense, i.e. if your EEG sampling resolution is 100Hz you might know your stimulus onset with higher precision and not in 10ms bins. But in order to get ERPs we have to &#8220;cut&#8221; the signal at the event onset. EEGlab uses &#8220;floor&#8221; for this and rounds the stimulus onset from 212.7 to sample 212. Other toolboxes (unfold \/ fieldtrip) use &#8220;round&#8221;, thus the event would start at sample 213.<\/p>\n\n\n\n<p>It turns out that in the example you see above, this introduces a difference between the two ERPs of 0.5\u00b5V (!) thats around 8% of the magnitude.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff-1024x811.png\" alt=\"\" class=\"wp-image-418\" width=\"768\" height=\"608\" srcset=\"https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff-1024x811.png 1024w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff-300x238.png 300w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff-768x608.png 768w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff.png 1457w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><figcaption>difference &#8220;floor&#8221; vs &#8220;round&#8221; for 512Hz data<\/figcaption><\/figure><\/div>\n\n\n\n<p>This is just a random example I stumbled upon. With lower sampling rates, this effect should increase. Indeed, downsampling to 128 Hz gives us a whopping difference of 1.5\u00b5V.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff128-1024x811.png\" alt=\"\" class=\"wp-image-419\" width=\"768\" height=\"608\" srcset=\"https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff128-1024x811.png 1024w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff128-300x238.png 300w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff128-768x608.png 768w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2019\/10\/diff128.png 1457w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><figcaption>Difference &#8220;floor&#8221;-&#8220;round&#8221; for 128Hz sampling rate<\/figcaption><\/figure><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Floor vs. round (vs. others?)<\/h4>\n\n\n\n<p>The benefit of <strong>floor<\/strong>, at least the one I can think of, is that it would never shift the onset of a stimulus in the future. That is, it is causal. Possibly there are other benefits I am not aware of.<\/p>\n\n\n\n<p>The benefit of <strong>round <\/strong>is that it more accurately reflects the actual stimulus onset. Possibly there are other benefits I am not aware of<\/p>\n\n\n\n<p>Given that we mostly use acausal filtering anyway, I think the causal benefit is not very strong. <\/p>\n\n\n\n<p>There is yet another alternative: a weighted average between samples. We could &#8220;split&#8221; the event onset to two samples, i.e. if we want the instantaneous stim-onset response, and stim onset is at sample 12.3, then sample 12 should be weighted 30% and sample 13 by 70%. I have to explore this idea a bit more, but I think its very easy to implement in unfold and test. But this for a new blogpost.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The big picture<\/h2>\n\n\n\n<p>In the fMRI community there are papers from time to time reporting that different analysis tools (or versions) lead to different results. I am not aware of any such paper in the EEG community (if you know one, let me know please!) but I think it would be nice if somebody would do such comparisons.<\/p>\n\n\n\n<p>I currently do not forsee if such an event-latency-rounding difference could possibly introduce bias in condition differences. But I forsee that changing it will be difficult for the EEGlab developers, as &#8220;floor&#8221; has been around for a very long time in eeglab.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Code<\/h2>\n\n\n\n<p>Note that I did not use simulation here (but could have, it should be straight) but I cannot publicly share the data at this point in time.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">load('EEG_subj8_inkl_ufresult.mat')\n%%\n%EEG = pop_resample(EEG,128);\ntimelimits = [-.3 .5];\nEEG = uf_designmat(EEG,'eventtypes','stimulus','formula','y~1');\n%deconv based \"round\" analysis\nEEG = uf_timeexpandDesignmat(EEG,'timelimits',timelimits);\nEEG = uf_glmfit(EEG,'channel',63);\n\n%eeglab based \"floor\" analysis\nEEGe = pop_epoch(EEG,{'stimulus'},timelimits);\n\nufresult = uf_condense(EEGe);\n%% ERP plot\nfigure\nplot(ufresult.times,mean(EEGe.data(63,:,:),3)) %floor\nhold on\nplot(ufresult.times,ufresult.beta(63,:,1)) %round\nxlim(timelimits)\nxlabel('time [s]')\nylabel('ERP [\u00b5V]')\nbox off\nexport_fig erp.png -m3 -transparent\n%% difference plot\nfigure\ntitle('Diff \"floor\"-\"round\"')\nplot(ufresult.times,mean(EEGe.data(63,:,:),3)-ufresult.beta(63,:,1)) %floor-round\nxlim(timelimits)\nxlabel('time [s]')\nylabel('ERP [\u00b5V]')\nbox off\nexport_fig diff.png -m3 -transparent\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>22.10.2019 Edit: Thanks Matt Craddock, I understand the source of the problem better. He mentioned that this should not occur if the amplifiers record the triggers as trigger channels (before converting it to events). And mentions that this could happen through downsampling. Indeed after checking in the dataset I used it was downsampled from 1024 to 512Hz. This made many eventlatencies ~ X.50001, which will be uprounded with round and floored with floor. This gives some context to the problem.Full discussion on twitter TLDR; EEGlab allows for non-integer event latencies (in units of samples). Eeglab chose floor(latency), while others e.g&#8230;.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-415","post","type-post","status-publish","format-standard","hentry","category-blog"],"_links":{"self":[{"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/posts\/415","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/comments?post=415"}],"version-history":[{"count":0,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/posts\/415\/revisions"}],"wp:attachment":[{"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/media?parent=415"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/categories?post=415"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/tags?post=415"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}