fig5S2.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. """Figure 5S2 pupil area plots, use run -i fig5S2.py"""
  2. ## collect PSTH trial matrices with same timebase as pupil area signal, masked by runspeed
  3. ## to include only sitting periods, then also masked by pupil constriction/dilation upper and
  4. ## lower quartiles. Store in arearate_mviresp DataFrame indexed by [mseu, opto]
  5. allmseustrs = mviresp.index.levels[0].values # get all mice, series, experiments, units
  6. levels = [allmseustrs, OPTOS]
  7. names = ['mseu', 'opto']
  8. mi = pd.MultiIndex.from_product(levels, names=names)
  9. columns = ['trialmat', 'bins', 'binw', 'tq_trialmat', 'bq_trialmat',
  10. 'psth', 'tq_psth', 'bq_psth', 'pct_changes']
  11. arearate_mviresp = pd.DataFrame(index=mi, columns=columns)
  12. arearates = {} # mse to (arearate, arearate_ts) mapping
  13. # for each row (mse) in ipos_opto df, find 1st derivative of pupil area trial matrix:
  14. for index, row in ipos_opto.iterrows():
  15. msestr, stimtype, opto = index
  16. if not stimtype == 'mvi':
  17. continue
  18. print(msestr, stimtype, opto)
  19. DX = 1 # decimation factor, e.g. 5 increases sampling period by 5X, from ~20 ms to ~100 ms
  20. area_trialmat = row['area_trialmat'][:, ::DX]
  21. area_trialts = row['area_trialts'][::DX]
  22. ntrials, nt = area_trialmat.shape
  23. arearate = np.diff(area_trialmat, axis=1) # 2D: ntrials, nt-1
  24. arearate_ts = area_trialts[:-1] # drop the last timepoint due to diff
  25. arearates[msestr] = arearate.copy(), arearate_ts.copy()
  26. # get locomotion info and mask out all instances of running:
  27. runrow = runspeed.loc[msestr, 'nat', opto]
  28. assert (row['trialis'] == runrow['trialis']).all() # sanity check
  29. run_ts, speed = runrow['t'], runrow['speed']
  30. # each runspeed trial can have slightly different number of timepoints, so have to
  31. # resample each runspeed trial separately:
  32. assert len(speed) == ntrials
  33. for i in range(ntrials): # iterate over trials
  34. f = scipy.interpolate.interp1d(run_ts[i], speed[i], axis=-1, kind='linear',
  35. fill_value='extrapolate')
  36. rs_speed = f(arearate_ts) # runspeed signal resampled to arearate_ts
  37. # mask out arearate during running periods:
  38. runmask = rs_speed > 0.25 # cm/s
  39. arearate[i, runmask] = np.nan # these points shouldn't influence the percentile calc
  40. # get top and bottom quartiles for each timepoint, averaged across all trials:
  41. tq, bq = np.nanpercentile(arearate, [75, 25], axis=0) # each is nt-1
  42. assert len(tq) == nt - 1
  43. assert len(bq) == nt - 1
  44. # mask out timepoints per trial in which arearate falls in top or bottom quartile:
  45. tqmask = np.int8(arearate >= tq) # 2D binary: ntrials, nt-1
  46. bqmask = np.int8(arearate <= bq)
  47. # create top and bottom quartile arearate arrays:
  48. arearatetq = np.full(arearate.shape, np.nan)
  49. arearatetq[tqmask] = arearate[tqmask]
  50. arearatebq = np.full(arearate.shape, np.nan)
  51. arearatebq[bqmask] = arearate[bqmask]
  52. # get all mseu for this mse:
  53. hits = findmse(allmseustrs, msestr)
  54. mseustrs = allmseustrs[hits] # all mseu that belong to this mse
  55. # get bins for calculating PSTHs using arearate timepoints:
  56. binw = np.median(np.diff(arearate_ts))
  57. ledges = arearate_ts # left edges
  58. redges = np.hstack([arearate_ts[1:], arearate_ts[-1]+binw]) # right edges
  59. bins = np.column_stack((ledges, redges))
  60. # calculate PSTHs and binned % rate change for each rate percentile range for each mseu:
  61. for mseustr in mseustrs:
  62. trialmat = np.full(arearate.shape, np.nan)
  63. mviresprow = mviresp.loc[mseustr, 'nat', 'none', opto]
  64. if np.isnan(mviresprow['dt']) or mviresprow['snr'] < SNRTHRESH:
  65. continue
  66. #raster, braster = mviresprow['raster'], mviresprow['braster']
  67. raster = mviresprow['raster']
  68. # generate trial-based psth matrix, binned at bins:
  69. for triali, trspkts in enumerate(raster):
  70. # tres is ignored when kernel == None
  71. trpsth = raster2psth([trspkts], bins, binw, None, kernel=None)
  72. trialmat[triali] = trpsth
  73. # create top quartile trialmat by masking out all entries in trialmat which are not
  74. # in top quartile:
  75. tq_trialmat = trialmat.copy()
  76. tq_trialmat[tqmask == 0] = np.nan
  77. # create bottom quartile trialmat by masking out all entries in trialmat which are not
  78. # in bottom quartile:
  79. bq_trialmat = trialmat.copy()
  80. bq_trialmat[bqmask == 0] = np.nan
  81. # compute mean across trials, only timepoints remain:
  82. psth = np.nanmean(trialmat, axis=0)
  83. tq_psth = np.nanmean(tq_trialmat, axis=0)
  84. bq_psth = np.nanmean(bq_trialmat, axis=0)
  85. # get firing rate percentile threshold ranges of the psth:
  86. lo, bq, mid, tq, hi = np.percentile(psth, [0, 25, 50, 75, 100])
  87. rpctileranges = [[lo, bq], [bq, mid], [mid, tq], [tq, hi]]
  88. # calculate binned percent change of PSTH of top quartile vs bottom quartile pupil
  89. # arearate, for each firing rate percentile range:
  90. pct_changes = []
  91. for rpctilerange in rpctileranges:
  92. # find PSTH timepoints that fall in this rate percentile range:
  93. tis = (rpctilerange[0] < psth) & (psth <= rpctilerange[1]) # bool array
  94. if (tis == False).all(): # no timepoints in this rpctilerange
  95. pct_change = np.nan
  96. else:
  97. tqmean = tq_psth[tis].mean()
  98. bqmean = bq_psth[tis].mean()
  99. if bqmean > 0:
  100. pct_change = (tqmean - bqmean) / bqmean * 100
  101. else:
  102. #print('bqmean', bqmean)
  103. pct_change = np.nan
  104. pct_changes.append(pct_change)
  105. pct_changes = np.array(pct_changes)
  106. arearate_mviresprow = arearate_mviresp.loc[mseustr, opto]
  107. arearate_mviresprow['trialmat'] = trialmat
  108. arearate_mviresprow['tq_trialmat'] = tq_trialmat
  109. arearate_mviresprow['bq_trialmat'] = bq_trialmat
  110. arearate_mviresprow['bins'] = bins
  111. arearate_mviresprow['binw'] = binw
  112. arearate_mviresprow['psth'] = psth
  113. arearate_mviresprow['tq_psth'] = tq_psth
  114. arearate_mviresprow['bq_psth'] = bq_psth
  115. arearate_mviresprow['pct_changes'] = pct_changes # length 4, 1 value per FR quartile
  116. ## collect % change values for both opto conditions, and resample them to allow
  117. ## confidence interval calculations; keep only rows that have non-nan entries in pct_changes:
  118. arearate_mviresp_notna = arearate_mviresp.dropna(subset=['pct_changes'])
  119. pcoffs = np.vstack(arearate_mviresp_notna.loc[(slice(None), False), 'pct_changes'].values)
  120. pcons = np.vstack(arearate_mviresp_notna.loc[(slice(None), True), 'pct_changes'].values)
  121. noffs, nons = len(pcoffs), len(pcons) # not necessarily the same, some can be unpaired
  122. NSAMPLES = 5000
  123. rspcoffmeds = np.full((NSAMPLES, 4), np.nan) # resampled % change opto off medians
  124. rspconmeds = np.full((NSAMPLES, 4), np.nan)
  125. rng = np.random.default_rng(0) # init random number generator with seed 0
  126. for samplei in range(NSAMPLES):
  127. rspcoffs = rng.choice(pcoffs, noffs, replace=True, axis=0)
  128. rspcons = rng.choice(pcons, nons, replace=True, axis=0)
  129. rspcoffmeds[samplei] = np.nanmedian(rspcoffs, axis=0)
  130. rspconmeds[samplei] = np.nanmedian(rspcons, axis=0)
  131. # calculate medians and percentiles for CIs:
  132. med_pcoffs = np.nanmedian(pcoffs, axis=0)
  133. med_rspcoffmeds = np.nanmedian(rspcoffmeds, axis=0)
  134. lci_rspcoffmeds = np.nanpercentile(rspcoffmeds, 2.5, axis=0)
  135. uci_rspcoffmeds = np.nanpercentile(rspcoffmeds, 97.5, axis=0)
  136. med_pcons = np.nanmedian(pcons, axis=0)
  137. med_rspconmeds = np.nanmedian(rspconmeds, axis=0)
  138. lci_rspconmeds = np.nanpercentile(rspconmeds, 2.5, axis=0)
  139. uci_rspconmeds = np.nanpercentile(rspconmeds, 97.5, axis=0)
  140. print('median pcoffs: ', med_pcoffs)
  141. print('median rspcoffmeds:', med_rspcoffmeds)
  142. print(' 2.5% rspcoffmeds:', lci_rspcoffmeds)
  143. print(' 97.5% rspcoffmeds:', uci_rspcoffmeds)
  144. print('median pcons: ', med_pcons)
  145. print('median rspconmeds: ', med_rspconmeds)
  146. print(' 2.5% rspconmeds: ', lci_rspconmeds)
  147. print(' 97.5% rspconmeds: ', uci_rspconmeds)
  148. ## plot arearate trial matrix for example experiment:
  149. exmplmsestrs = ['PVCre_2017_0006_s03_e03',
  150. 'PVCre_2017_0015_s07_e04',
  151. 'PVCre_2019_0002_s08_e04']
  152. for msestr in exmplmsestrs:#list(arearates):
  153. arearate, arearate_ts = arearates[msestr]
  154. binw = np.median(np.diff(arearate_ts)) # s
  155. arearate = arearate / binw # normalized pupil area per sec
  156. ntrials = len(arearate)
  157. tmin, tmax = arearate_ts[0], np.ceil(arearate_ts[-1])
  158. extent = tmin, tmax, ntrials, 1
  159. f, a = plt.subplots(figsize=(6, 3))
  160. wintitle('arearate trialmat %s' % msestr)
  161. #std = np.nanstd(arearate)
  162. #vlim = 3*std
  163. vlim = 0.3 # might result in a bit of clipping of outlier values
  164. im = a.imshow(arearate, extent=extent, aspect='auto', origin='upper', cmap='seismic',
  165. interpolation='nearest', vmin=-vlim, vmax=vlim)
  166. plt.colorbar(im, ticks=[-vlim, 0, vlim])
  167. #a.set_xticks(np.arange(5))
  168. a.set_xlabel('Time (s)')
  169. a.set_ylabel('Trial')
  170. #a.set_xticks(tmin, tmax, 50)
  171. yticks = np.arange(0, ntrials+1, 50)
  172. yticks[0] = 1
  173. a.set_yticks(yticks)
  174. ## plot example raster plot:
  175. exmplmseustr = 'PVCre_2019_0002_s08_e04_u32'
  176. mviresprow = mviresp.loc[exmplmseustr, 'nat', 'none', False]
  177. raster = mviresprow['raster']
  178. dt = mviresprow['dt'] # stimulus duration (s)
  179. SCATTER = False # True: low quality, but fast; False: high quality, but slow
  180. title = 'movie raster %s %s' % (mseustr, False)
  181. a = simpletraster(raster=raster, alpha=0.5, s=1, dt=dt, offsets=[0, 0],
  182. scatter=SCATTER,
  183. title=title, inchespersec=1.5*RASTERSCALEX,
  184. inchespertrial=1/50*RASTERSCALEX,
  185. xaxis=True)
  186. a.spines['left'].set_position(('outward', 4))
  187. a.spines['bottom'].set_position(('outward', 4))
  188. yticks = np.arange(0, ntrials+1, 50)
  189. yticks[0] = 1
  190. a.set_yticks(yticks)
  191. ## plot example PSTHs:
  192. exmplmsestr = 'PVCre_2019_0002_s08_e04'
  193. hits = findmse(allmseustrs, exmplmsestr)
  194. exmplmseustrs = allmseustrs[hits]
  195. alpha = 0.5
  196. for mseustr in [exmplmseustr]:#exmplmseustrs:
  197. psthoptos = arearate_mviresp.loc[mseustr][['tq_psth', 'bq_psth']]
  198. #maxpsth = np.vstack(psthoptos.values).max() # max of mean PSTH across both opto conditions
  199. maxpsth = np.hstack(psthoptos.values.flatten()).max()
  200. for opto in OPTOS:
  201. row = arearate_mviresp.loc[(mseustr, opto)]
  202. bins, psth, tq_psth, bq_psth = row[['bins', 'psth', 'tq_psth', 'bq_psth']]
  203. if np.any(np.isnan(bins)):
  204. continue
  205. t = bins.mean(axis=1)
  206. f, a = plt.subplots(figsize=(5, 3))
  207. wintitle('top bottom qrt PSTH %s opto=%s' % (mseustr, opto))
  208. #maxpsth = psth.max()
  209. #a.plot(t, psth/maxpsth, '-', color='black', alpha=alpha, label='Mean')
  210. a.plot(t, tq_psth, '-', color='red', alpha=alpha, label='Top quartile')
  211. a.plot(t, bq_psth, '-', color='blue', alpha=alpha, label='Bottom quartile')
  212. a.spines['left'].set_position(('outward', 4))
  213. a.spines['bottom'].set_position(('outward', 4))
  214. a.set_xlabel('Time (s)')
  215. a.set_ylabel('Normalized firing rate')
  216. a.set_xlim(0, 5)
  217. a.set_ylim(-1, maxpsth)
  218. a.set_xticks([0, 1, 2, 3, 4, 5])
  219. #a.set_yticks([0, 1])
  220. #a.legend()
  221. ## bar plot % change of firing for top vs bottom quartile pupil arearate, median
  222. ## across population, one value per firing rate quartile. Separate plots for
  223. ## each opto condition. See Reimer2014 fig5e:
  224. opto2height = {False:med_pcoffs, True:med_pcons}
  225. opto2yerr = {False:np.array([med_pcoffs-lci_rspcoffmeds, uci_rspcoffmeds-med_pcoffs]),
  226. True:np.array([med_pcons-lci_rspconmeds, uci_rspconmeds-med_pcons])}
  227. for opto in OPTOS:
  228. f, a = plt.subplots(figsize=(3, 4))
  229. wintitle('top bottom qrt pct FR change bar opto=%s' % opto)
  230. x = np.arange(4)
  231. height = opto2height[opto]
  232. yerr = opto2yerr[opto]
  233. color = opto2clr[opto]
  234. a.bar(x, height, yerr=yerr, color=color, ecolor='gray')
  235. #a.hlines(0, 0, 3)
  236. #a.axhline(y=0, c='k', ls='--', alpha=0.5)
  237. a.set_xlabel('Firing rate quartile')
  238. a.set_ylabel('% Change')
  239. a.spines['left'].set_position(('outward', 4))
  240. a.spines['bottom'].set_position(('outward', 4))
  241. #a.spines['bottom'].set_visible(False)
  242. a.set_xticks([0, 1, 2, 3])
  243. a.set_xticklabels(['0-25%', '25-50%', '50-75%', '75-100%'], rotation=45, ha='center')
  244. ## bar plot % change of firing for top vs bottom quartile pupil arearate, median
  245. ## across population, one value per firing rate quartile. One plot for both opto conditions.
  246. ## See Reimer2014 fig5e:
  247. w = 0.4 # bar width
  248. opto2x = {False:np.arange(-w/2, 4-w/2, 1), True:np.arange(w/2, 4+w/2, 1)}
  249. opto2height = {False:med_pcoffs, True:med_pcons}
  250. opto2yerr = {False:np.array([med_pcoffs-lci_rspcoffmeds, uci_rspcoffmeds-med_pcoffs]),
  251. True:np.array([med_pcons-lci_rspconmeds, uci_rspconmeds-med_pcons])}
  252. opto2label = {False:'V1 Control', True:'V1 Suppressed'}
  253. f, a = plt.subplots(figsize=(3, 3))
  254. wintitle('top bottom qrt pct FR change bar opto=both')
  255. for opto in OPTOS:
  256. x = opto2x[opto]
  257. height = opto2height[opto]
  258. yerr = opto2yerr[opto]
  259. color = opto2clr[opto]
  260. label = opto2label[opto]
  261. a.bar(x, height, width=w, yerr=yerr, color=color, label=label, ecolor='gray')
  262. #a.hlines(0, 0, 3)
  263. #a.axhline(y=0, c='k', ls='--', alpha=0.5)
  264. a.set_xlabel('Firing rate quartile')
  265. a.set_ylabel('% Firing rate change')
  266. a.spines['left'].set_position(('outward', 4))
  267. a.spines['bottom'].set_position(('outward', 4))
  268. #a.spines['bottom'].set_visible(False)
  269. a.set_xticks([0, 1, 2, 3])
  270. a.set_xticklabels(['0-25%', '25-50%', '50-75%', '75-100%'], rotation=45, ha='center')
  271. #ymin = np.floor(min(lci_rspcoffmeds.min(), lci_rspconmeds.min()))
  272. ymin = -1.5
  273. ymax = np.ceil(max(uci_rspcoffmeds.max(), uci_rspconmeds.max()))
  274. a.set_ylim(ymin=ymin, ymax=ymax)
  275. #a.legend()
  276. ## calculate mean FR, reliability, and SNR for top and bottom pupil arearate quartiles:
  277. columns = ['mseu', 'opto', 'tqr', 'bqr', 'tqrel', 'bqrel', 'tqsnr', 'bqsnr']
  278. fig5S2 = []
  279. for mseustr in allmseustrs:
  280. arearate_mviresprow = arearate_mviresp.loc[mseustr]
  281. tq_trialmats = arearate_mviresprow['tq_trialmat']
  282. bq_trialmats = arearate_mviresprow['bq_trialmat']
  283. if np.any(pd.isna(tq_trialmats)) or np.any(pd.isna(bq_trialmats)):
  284. continue
  285. for opto in OPTOS:
  286. tq_trialmat = tq_trialmats[opto]
  287. bq_trialmat = bq_trialmats[opto]
  288. tqr = np.nanmean(tq_trialmat) # top quartile, mean rate
  289. bqr = np.nanmean(bq_trialmat) # bottom quartile, mean rate
  290. tqrel, _ = reliability(tq_trialmat, average='mean', ignore_nan=True)
  291. bqrel, _ = reliability(bq_trialmat, average='mean', ignore_nan=True)
  292. tqsnr = snr_baden2016(tq_trialmat)
  293. bqsnr = snr_baden2016(bq_trialmat)
  294. # skip mseu that have mean rates that fall below thresh due to being limited
  295. # by periods of pupil area dynamics:
  296. if tqr < RATETHRESH and bqr < RATETHRESH:
  297. continue
  298. row = {}
  299. row['mseu'] = mseustr
  300. row['opto'] = opto
  301. row['tqr'] = tqr # top quartile mean firing rate
  302. row['bqr'] = bqr
  303. row['tqrel'] = tqrel # top quartile reliability
  304. row['bqrel'] = bqrel
  305. row['tqsnr'] = tqsnr # top quartile SNR
  306. row['bqsnr'] = bqsnr
  307. fig5S2.append(row)
  308. fig5S2 = pd.DataFrame(fig5S2).set_index(['mseu', 'opto'])
  309. ## scatter plot top vs bottom pupil arearate quartiles for meanrate, reliability, and SNR.
  310. ## See Reimer2014 fig5fghi:
  311. figsize = DEFAULTFIGURESIZE[0]*1.02, DEFAULTFIGURESIZE[1] # tweak to make space for log units
  312. for opto in OPTOS:
  313. rows = fig5S2.loc[(slice(None), opto), :]
  314. normlis, = np.where(rows.reset_index()['mseu'] != exmplmseustr)
  315. exmpli, = np.where(rows.reset_index()['mseu'] == exmplmseustr)
  316. ## scatter plot mean firing rates during top vs. bottom quartile pupil area
  317. ## dilation/constriction rate:
  318. f, a = plt.subplots(figsize=figsize)
  319. wintitle('top bottom qrt scatter meanrate opto=%s' % opto)
  320. # replace any FRs < MINFRVAL with MINFRVAL, and any > MAXFRVAL with MAXFRVAL, for plotting:
  321. logmin, logmax = -0.9, 1.9
  322. logticks = np.array([0, 1])
  323. MINFRVAL, MAXFRVAL = 10**logmin, 10**logmax
  324. tqr, bqr = rows['tqr'].values, rows['bqr'].values
  325. tqr[tqr < MINFRVAL] = MINFRVAL
  326. bqr[bqr < MINFRVAL] = MINFRVAL
  327. tqr[tqr > MAXFRVAL] = MAXFRVAL
  328. bqr[bqr > MAXFRVAL] = MAXFRVAL
  329. # plot y=x line:
  330. linmin, linmax = MINFRVAL, MAXFRVAL
  331. xyline = [linmin, linmax], [linmin, linmax]
  332. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  333. # plot normal points:
  334. a.scatter(bqr[normlis], tqr[normlis], clip_on=False, marker='.',
  335. c='None', edgecolor='black', s=DEFSZ)
  336. # plot example point:
  337. a.scatter(bqr[exmpli], tqr[exmpli], clip_on=False, marker='x',
  338. c=green, s=60)
  339. a.set_title('FR (Hz), opto=%s' % opto)
  340. a.set_ylabel("Top 1/4 arearate")
  341. a.set_xlabel("Bottom 1/4 arearate")
  342. a.set_xscale('log')
  343. a.set_yscale('log')
  344. a.set_xlim(MINFRVAL, MAXFRVAL)
  345. a.set_ylim(MINFRVAL, MAXFRVAL)
  346. a.set_xticks(10.0**logticks)
  347. a.set_yticks(a.get_xticks()) # make log scale y ticks the same as x ticks
  348. a.minorticks_off()
  349. axes_disable_scientific(a)
  350. a.set_aspect('equal')
  351. a.spines['left'].set_position(('outward', 4))
  352. a.spines['bottom'].set_position(('outward', 4))
  353. t, p = ttest_rel(bqr, tqr) # paired t-test
  354. a.add_artist(AnchoredText('p$=$%.2g' % p, loc='lower right', frameon=False))
  355. ## scatter plot reliability during top vs. bottom quartile pupil area
  356. ## dilation/constriction rate:
  357. f, a = plt.subplots(figsize=figsize)
  358. wintitle('top bottom qrt scatter rel opto=%s' % opto)
  359. tqrel, bqrel = rows['tqrel'].values, rows['bqrel'].values
  360. # plot y=x line:
  361. linmin, linmax = -0.002, 0.06
  362. xyline = [linmin, linmax], [linmin, linmax]
  363. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  364. # plot normal points:
  365. a.scatter(bqrel[normlis], tqrel[normlis], clip_on=False, marker='.',
  366. c='None', edgecolor='black', s=DEFSZ)
  367. # plot example point:
  368. a.scatter(bqrel[exmpli], tqrel[exmpli], clip_on=False, marker='x',
  369. c=green, s=60)
  370. a.set_title('Reliability, opto=%s' % opto)
  371. a.set_ylabel("Top 1/4 arearate")
  372. a.set_xlabel("Bottom 1/4 arearate")
  373. #a.set_xscale('log')
  374. #a.set_yscale('log')
  375. a.set_xlim(linmin, linmax)
  376. a.set_ylim(linmin, linmax)
  377. #a.set_xticks(10.0**logticks)
  378. a.set_xticks([0, linmax])
  379. a.set_yticks(a.get_xticks()) # make log scale y ticks the same as x ticks
  380. a.minorticks_off()
  381. axes_disable_scientific(a)
  382. a.set_aspect('equal')
  383. a.spines['left'].set_position(('outward', 4))
  384. a.spines['bottom'].set_position(('outward', 4))
  385. # filter out nans for t-test:
  386. t, p = ttest_rel(bqrel, tqrel) # paired t-test
  387. a.add_artist(AnchoredText('p$=$%.2g' % p, loc='lower right', frameon=False))
  388. ## scatter plot SNR during top vs. bottom quartile pupil area
  389. ## dilation/constriction rate:
  390. f, a = plt.subplots(figsize=figsize)
  391. wintitle('top bottom qrt scatter snr opto=%s' % opto)
  392. tqsnr, bqsnr = rows['tqsnr'].values, rows['bqsnr'].values
  393. # plot y=x line:
  394. linmin, linmax = 0, 0.7
  395. xyline = [linmin, linmax], [linmin, linmax]
  396. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  397. # plot normal points:
  398. a.scatter(bqsnr[normlis], tqsnr[normlis], clip_on=False, marker='.',
  399. c='None', edgecolor='black', s=DEFSZ)
  400. # plot example point:
  401. a.scatter(bqsnr[exmpli], tqsnr[exmpli], clip_on=False, marker='x',
  402. c=green, s=60)
  403. a.set_title('SNR, opto=%s' % opto)
  404. a.set_ylabel("Top 1/4 arearate")
  405. a.set_xlabel("Bottom 1/4 arearate")
  406. #a.set_xscale('log')
  407. #a.set_yscale('log')
  408. a.set_xlim(linmin, linmax)
  409. a.set_ylim(linmin, linmax)
  410. #a.set_xticks(10.0**logticks)
  411. a.set_xticks([0, linmax])
  412. a.set_yticks(a.get_xticks()) # make log scale y ticks the same as x ticks
  413. a.minorticks_off()
  414. axes_disable_scientific(a)
  415. a.set_aspect('equal')
  416. a.spines['left'].set_position(('outward', 4))
  417. a.spines['bottom'].set_position(('outward', 4))
  418. # filter out nans for t-test:
  419. t, p = ttest_rel(bqsnr, tqsnr) # paired t-test
  420. a.add_artist(AnchoredText('p$=$%.2g' % p, loc='lower right', frameon=False))