Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

fig3.py 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890
  1. """Figure 3 plots, use run -i fig3.py"""
  2. mi = pd.MultiIndex.from_product([grtmseustrs, OPTOS], names=['mseu', 'opto'])
  3. columns = ['trialis', 'rates', 'blankrates', 'blankcondrates', # single trials
  4. 'burstratios', 'blankburstratios', 'blankcondburstratios', # single trials
  5. 'meanrate', 'blankmeanrate', 'blankcondmeanrate',
  6. 'meanburstratio', 'blankmeanburstratio', 'blankcondmeanburstratio',
  7. 'osi', 'oripref', 'f1f0',
  8. 'phi', 'bphi', 'nbphi', 'dphi', 'dbphi', 'dnbphi']
  9. fig3 = pd.DataFrame(index=mi, columns=columns) # excludes kind and st8
  10. # plot grating rasters for each unit by state and opto condition:
  11. showbursts = True
  12. if EXPTYPE == 'negntsrmvis':
  13. NTRIALSPERCONDITION = 8 # per stimi and opto combination
  14. inchespertrial = 1/20*RASTERSCALEX
  15. else:
  16. NTRIALSPERCONDITION = 10 # per stimi and opto combination
  17. inchespertrial = 1/25*RASTERSCALEX
  18. #outliers = ['PVCre_2018_0001_s02_e02_u36', 'PVCre_2018_0001_s02_e06_u36',
  19. # 'PVCre_2018_0001_s02_e02_u64', 'PVCre_2018_0001_s02_e06_u64']
  20. for mseustr in []:#grtmseu2exmpli: #grtmseustrs: #outliers:
  21. for st8 in ['none']:#ALLST8S:
  22. for opto in OPTOS:
  23. dt = grtresp.loc[mseustr, st8, opto]['dt']
  24. wdt = -OFFSETS[0] + dt + OFFSETS[1] # wide raster duration, sec
  25. tmin, tmax = 0 + OFFSETS[0], dt + OFFSETS[1]
  26. optotrange = grtresp.loc[mseustr, st8, opto]['optotrange']
  27. wraster = grtresp.loc[mseustr, st8, opto]['wraster']
  28. if np.any(pd.isna(wraster)): # no raster for this condition
  29. continue
  30. ntrials = len(wraster)
  31. title = '%s grating %s %s raster' % (mseustr, st8, opto)
  32. burstis = None
  33. if showbursts:
  34. wburstis = grtresp.loc[mseustr, st8, opto]['wburstis']
  35. a = simpletraster(raster=wraster, alpha=0.5, s=1, dt=dt, offsets=OFFSETS,
  36. scatter=True, # False: low quality, but fast
  37. title=title, inchespersec=1.5*RASTERSCALEX,
  38. inchespertrial=inchespertrial,
  39. xaxis=True, burstis=wburstis)
  40. if opto:
  41. # plot horizontal line signifying opto ON period, just above axes:
  42. ton, toff = optotrange
  43. a.hlines(-5, ton, toff, colors=optoblue, lw=4, clip_on=False,
  44. in_layout=False)
  45. # plot horizontal line signifying stimulus period, just above axes:
  46. a.hlines(-2, 0, dt, colors='k', lw=4, clip_on=False, in_layout=False)
  47. # plot vertical coloured line just right of axes
  48. #vlinexpos = dt + OFFSETS[1] + wdt*0.02
  49. #y0, y1 = a.get_ylim()
  50. #clr = st82clr[st8]
  51. #alpha = opto2alpha[opto]
  52. #a.vlines(vlinexpos, y0, y1, colors=clr, lw=4, alpha=alpha, clip_on=False,
  53. # in_layout=False)
  54. a.set_xlim(tmin, tmax)
  55. #a.set_xticks(range(0, intround(dt)+1)) # force integer 1 sec tick marks
  56. # force ticks on trial axis separating ori conditions:
  57. a.set_yticks(range(0, ntrials+1, NTRIALSPERCONDITION))
  58. # Plot grating tuning curves for example units. Requires a database connection.
  59. # Uses effectively same grey as 0.4 alpha for opto True condition:
  60. try:
  61. tun
  62. except:
  63. print("WARNING: Can't plot tuning curves without database connection")
  64. tun = None
  65. if tun is not None:
  66. for mseustr in grtmseu2exmpli: #grtmseustrs:#['Ntsr1Cre_2020_0001_s04_e04_u55']
  67. tunrow = (tun & {**mseustr2key(mseustr), 'st8_crit':'none'})
  68. if tunrow:
  69. tunrow.plot(cs=['k', optoblue])
  70. ## calculate cycle averages (i.e. grating PSTHs), F1/F0, and phi for all units:
  71. PLOTCYCPSTH = False
  72. CYCPSTHTHRESH = 10 # minimum cycle average peak, Hz
  73. F1F0THRESH = 0.25 # minimum grating modulation index
  74. # minimum ratio of suppression burst cycle average peak to suppression of all spikes
  75. # cycle average peak, at maxori:
  76. BURSTRATIOTHRESH = 0.1
  77. TESTPEAKS = 'both' # test peak amplitude of 'either' or 'both' cycle PSTH opto conditions
  78. ncyclesoffset = 0.25 # fraction of previous and next cycle to plot
  79. figsize = DEFAULTFIGURESIZE
  80. for st8 in ['none']:#ALLST8S:
  81. color = st82clr[st8]
  82. f1f0offs, f1f0ons = [], []
  83. phioffs, phions, bphioffs, bphions, nbphioffs, nbphions = [], [], [], [], [], []
  84. dphis, dbphis, dnbphis = [], [], []
  85. exmplis, exmplmseustrs, normlis = [], [], []
  86. keptmseui = 0 # manually init and increment instead of using enumerate()
  87. for mseustr in grtmseustrs: #['Ntsr1Cre_2020_0001_s04_e04_u12']
  88. cycpsths, cycbpsths, cycts = {}, {}, {} # indexed by ori and opto
  89. f1f0s, phis = {}, {} # indexed by ori and opto
  90. rasters = grtresp.loc[mseustr, st8]['raster']
  91. brasters = grtresp.loc[mseustr, st8]['braster']
  92. if rasters.isna().any():
  93. print('%s: missing one or both raster opto conditions, skipping' % mseustr)
  94. continue
  95. if brasters.isna().any():
  96. print('%s: missing one or both braster opto conditions, skipping' % mseustr)
  97. continue
  98. orisopto = grtresp.loc[mseustr, st8]['oris'] # sorted, correspond to rows in raster
  99. assert np.equal(*orisopto).all() # make sure oris are identical across opto
  100. # get oris from arbitrary condition, convert to int for use as dict keys:
  101. uoris = np.int64(np.unique(orisopto[False]))
  102. for ori in uoris:
  103. cycpsths[ori], cycbpsths[ori], cycts[ori] = {}, {}, {}
  104. f1f0s[ori], phis[ori] = {}, {}
  105. for opto in OPTOS:
  106. raster = rasters[opto]
  107. braster = brasters[opto]
  108. oris = orisopto[opto]
  109. dt = grtresp.loc[mseustr, st8, opto]['dt']
  110. tfreq = grtresp.loc[mseustr, st8, opto]['tfreq']
  111. cycdt = 1 / tfreq # cycle duration, s
  112. t0 = 0 + cycdt # exclude first cycle...
  113. t1 = dt # ...include up to end of last cycle
  114. offsets = np.array([-cycdt, cycdt]) * ncyclesoffset
  115. # to avoid edge effects of Guassian smoothing in raster2psth(), give it a wide
  116. # set of bins, and then slice out a slightly narrower (one bin width less at
  117. # each end) set of bins:
  118. wtrange = 0+offsets[0], cycdt+offsets[1] # wide (full) trange
  119. ntrange = wtrange[0]+binw, wtrange[1]-binw # narrow trange
  120. wcycbins = split_tranges([wtrange], binw, tres)
  121. ncycbins = split_tranges([ntrange], binw, tres)
  122. # find which rows in wcycbins correpond to those in ncycbins, based on the
  123. # start time of each bin:
  124. nbinis = wcycbins[:, 0].searchsorted(ncycbins[:, 0])
  125. orirowis = oris == ori
  126. rast = raster[orirowis] # raster restricted to rows corrsponding to ori
  127. brast = braster[orirowis]
  128. cycraster = wrap_raster(rast, t0, t1, cycdt, offsets=offsets)
  129. cycbraster = wrap_raster(brast, t0, t1, cycdt, offsets=offsets)
  130. f0, _ = raster2freqcomp(rast, dt, 0, mean='vector')
  131. f1, phi = raster2freqcomp(rast, dt, tfreq, mean='vector')
  132. if f0 == 0.0: # no spikes at all for this ori and opto combination
  133. f1f0s[ori][opto] = np.nan
  134. phis[ori][opto] = np.nan
  135. else:
  136. f1f0s[ori][opto] = f1 / f0
  137. phis[ori][opto] = np.angle(np.exp(1j*phi)) + pi # wrapped +ve angle, rad
  138. cycts[ori][opto] = ncycbins.mean(axis=1) # save narrow raster timepoints
  139. wrast = raster2psth(cycraster, wcycbins, binw, tres, kernel) # wide raster
  140. wbrast = raster2psth(cycbraster, wcycbins, binw, tres, kernel) # wide burst raster
  141. cycpsths[ori][opto] = wrast[nbinis] # slice down to narrow raster
  142. cycbpsths[ori][opto] = wbrast[nbinis]
  143. # find ori with max cycle average peak, across opto conditions:
  144. maxcycpsthvals = []
  145. for ori in uoris:
  146. maxcycpsthvals.append(max(cycpsths[ori][False].max(), cycpsths[ori][True].max()))
  147. maxcycpsthvali = np.argmax(maxcycpsthvals)
  148. maxori = uoris[maxcycpsthvali]
  149. if TESTPEAKS == 'either':
  150. # test for minimum PSTH peak height in at least one opto condition:
  151. maxcycpsthval = maxcycpsthvals[maxcycpsthvali]
  152. if maxcycpsthval < CYCPSTHTHRESH:
  153. continue # skip this mseu
  154. elif TESTPEAKS == 'both':
  155. # test for minimum PSTH peak height in both opto conditions:
  156. maxcycpsthoff = cycpsths[maxori][False].max()
  157. maxcycpsthon = cycpsths[maxori][True].max()
  158. if (np.array([maxcycpsthoff, maxcycpsthon]) < CYCPSTHTHRESH).any():
  159. continue # skip this mseu
  160. # test for minimum f1/f0 in at least one opto condition:
  161. maxf1f0atmaxori = max(f1f0s[maxori][False], f1f0s[maxori][True])
  162. if maxf1f0atmaxori < F1F0THRESH:
  163. continue # skip this mseu
  164. # collect f1f0 and phi values at maxori:
  165. f1f0off, f1f0on = f1f0s[maxori][False], f1f0s[maxori][True]
  166. f1f0offs.append(f1f0off)
  167. f1f0ons.append(f1f0on)
  168. phioff, phion = phis[maxori][False], phis[maxori][True]
  169. if EXPTYPE == 'pvmvis':
  170. # wrap some points:
  171. if (phioff < 60*pi/180) & (phion > 240*pi/180):
  172. phioff += 360*pi/180 # wrap point up over the y=x line
  173. elif (phioff > 340*pi/180) & (phion < 15*pi/180):
  174. phioff -= 360*pi/180 # wrap point down over the y=x line
  175. elif EXPTYPE == 'ntsrmvis':
  176. # wrap one point:
  177. if (phioff < 30*pi/180) & (phion > 340*pi/180):
  178. phioff += 360*pi/180 # wrap point up over the y=x line
  179. phioffs.append(phioff)
  180. phions.append(phion)
  181. fig3.loc[mseustr, 'phi'] = phioff*180/pi, phion*180/pi # save
  182. dphi = np.angle(np.exp(1j*phioff) / np.exp(1j*phion)) # phioff - phion
  183. dphis.append(dphi)
  184. if dphi < (-75*pi/180):
  185. print('OUTLIER: %s dphi=%g' % (mseustr, dphi))
  186. fig3.loc[mseustr, 'dphi'] = dphi*180/pi, dphi*180/pi # save, ignore opto field
  187. # collect phi of mseus that meet minimum ratio of suppression burst cycle average peak
  188. # to suppression all spikes cycle average peak, at maxori:
  189. cycbpsthmax, cycpsthmax = cycbpsths[maxori][True].max(), cycpsths[maxori][True].max()
  190. if cycpsthmax == 0.0:
  191. if cycbpsthmax == 0.0: # numerator and denominator are both 0
  192. suppburstratioatmaxori = 0.0
  193. else:
  194. suppburstratioatmaxori = np.inf
  195. else:
  196. suppburstratioatmaxori = cycbpsthmax / cycpsthmax
  197. # if suppression burst cycle average peak is big enough, treat is as a burst mseu,
  198. # otherwise treat it as a nonburst mseu:
  199. if suppburstratioatmaxori >= BURSTRATIOTHRESH:
  200. bphioffs.append(phioff)
  201. bphions.append(phion)
  202. fig3.loc[mseustr, 'bphi'] = phioff*180/pi, phion*180/pi # save
  203. dbphi = np.angle(np.exp(1j*phioff) / np.exp(1j*phion)) # bphioff - bphion
  204. dbphis.append(dbphi)
  205. fig3.loc[mseustr, 'dbphi'] = dbphi*180/pi, dbphi*180/pi # save, ignore opto field
  206. else:
  207. nbphioffs.append(phioff)
  208. nbphions.append(phion)
  209. fig3.loc[mseustr, 'nbphi'] = phioff*180/pi, phion*180/pi # save
  210. dnbphi = np.angle(np.exp(1j*phioff) / np.exp(1j*phion)) # nbphioff - nbphion
  211. dnbphis.append(dnbphi)
  212. fig3.loc[mseustr, 'dnbphi'] = dnbphi*180/pi, dnbphi*180/pi # save, ignore opto field
  213. ## plot maxori cycle average:
  214. if PLOTCYCPSTH:# and mseustr in grtmseu2exmpli:#['PVCre_2018_0003_s03_e02_u51']
  215. f, a = plt.subplots(figsize=(2, figsize[1]))
  216. title = ('%s %s PSTH maxori=%d offset=%g'
  217. % (mseustr, st8, maxori, ncyclesoffset))
  218. wintitle(title)
  219. for opto in OPTOS:
  220. c = desat(color, opto2alpha[opto]) # do manual alpha mixing
  221. bc = desat(burstclr, opto2alpha[opto]) # do manual alpha mixing
  222. fb = opto2fb[opto].title()
  223. if st8 == 'none':
  224. label = fb
  225. else:
  226. label = ', '.join([fb, st8.title()])
  227. cyct = cycts[maxori][opto]
  228. cycpsth = cycpsths[maxori][opto]
  229. cycbpsth = cycbpsths[maxori][opto]
  230. a.plot(cyct, cycpsth, '-', color=c, label=label)
  231. a.plot(cyct, cycbpsth, '-', color=bc, label=label+', Burst')
  232. a.axvline(x=0, ls='--', marker='', color='lightgray', zorder=-np.inf)
  233. a.axvline(x=cycdt, ls='--', marker='', color='lightgray', zorder=-np.inf)
  234. #l = a.legend(frameon=False)
  235. #l.set_draggable(True)
  236. a.set_xlabel('Time (s)')
  237. a.set_ylabel('Firing rate (spk/s)')
  238. a.set_xlim(cyct[0], cyct[-1])
  239. if mseustr in grtmseu2exmpli:
  240. exmplis.append(keptmseui)
  241. exmplmseustrs.append(mseustr)
  242. else:
  243. normlis.append(keptmseui)
  244. keptmseui += 1 # manually increment
  245. f1f0offs, f1f0ons = np.asarray(f1f0offs), np.asarray(f1f0ons)
  246. phioffs, phions = np.asarray(phioffs), np.asarray(phions)
  247. bphioffs, bphions = np.asarray(bphioffs), np.asarray(bphions)
  248. nbphioffs, nbphions = np.asarray(nbphioffs), np.asarray(nbphions)
  249. dphis, dbphis, dnbphis = np.asarray(dphis), np.asarray(dbphis), np.asarray(dnbphis)
  250. ## scatter plot collected maxori f1f0s:
  251. figsize = DEFAULTFIGURESIZE
  252. f, a = plt.subplots(figsize=figsize)
  253. wintitle('opto f1f0 maxori grating %s CYCPSTHTHRESH=%g F1F0THRESH=%g '
  254. 'TESTPEAKS=%s' % (st8, CYCPSTHTHRESH, F1F0THRESH, TESTPEAKS))
  255. # plot y=x line:
  256. linmin, linmax = 0, 2
  257. xyline = [linmin, linmax], [linmin, linmax]
  258. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  259. # plot normal (non-example) points:
  260. a.scatter(f1f0ons[normlis], f1f0offs[normlis], clip_on=False,
  261. marker='.', c='None', edgecolor=st82clr[st8], s=DEFSZ)
  262. # plot example points, one at a time:
  263. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  264. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  265. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  266. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  267. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  268. a.scatter(f1f0ons[exmpli], f1f0offs[exmpli], clip_on=False,
  269. marker=marker, c=c, s=sz, lw=lw)
  270. a.set_xlabel('Suppression F1/F0')
  271. a.set_ylabel('Feedback F1/F0')
  272. #a.set_xscale('log', basex=2)
  273. #a.set_yscale('log', basey=2)
  274. #a.set_xlim(0.125, 2)
  275. #a.set_ylim(0.125, 2)
  276. #a.set_xticks([0.125, 0.25, 0.5, 1, 2])
  277. #a.set_yticks(a.get_xticks()) # make scale y ticks the same as x ticks
  278. #axes_disable_scientific(a)
  279. a.set_xlim(linmin, linmax)
  280. a.set_ylim(linmin, linmax)
  281. a.set_xticks(np.arange(linmin, linmax+1))
  282. a.set_yticks(a.get_xticks()) # make scale y ticks the same as x ticks
  283. a.minorticks_off()
  284. a.set_aspect('equal')
  285. a.spines['left'].set_position(('outward', 4))
  286. a.spines['bottom'].set_position(('outward', 4))
  287. t, p = ttest_rel(f1f0ons, f1f0offs) # paired t-test
  288. a.add_artist(AnchoredText('p$=$%.1g' % p, loc='upper left', frameon=False))
  289. ## scatter plot all collected maxori phis, regardless of burst classification:
  290. figsize = DEFAULTFIGURESIZE[0]*1.02, DEFAULTFIGURESIZE[1] # tweak to make space for labels
  291. f, a = plt.subplots(figsize=figsize)
  292. wintitle('opto phi maxori grating %s CYCPSTHTHRESH=%g F1F0THRESH=%g '
  293. 'TESTPEAKS=%s' % (st8, CYCPSTHTHRESH, F1F0THRESH, TESTPEAKS))
  294. # plot y=x line:
  295. #linmin, linmax, linstep = -180, 180, 180
  296. linmin, linmax, linstep = 0, 360, 180
  297. xyline = [linmin, linmax], [linmin, linmax]
  298. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  299. # plot normal (non-example) points:
  300. a.scatter(phions[normlis]*180/pi, phioffs[normlis]*180/pi, clip_on=False,
  301. marker='.', c='None', edgecolor=st82clr[st8], s=DEFSZ)
  302. # plot example points, one at a time:
  303. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  304. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  305. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  306. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  307. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  308. a.scatter(phions[exmpli]*180/pi, phioffs[exmpli]*180/pi, clip_on=False,
  309. marker=marker, c=c, s=sz, lw=lw)
  310. a.set_xlabel('Suppression')# $\phi$ ($\degree$)')
  311. a.set_ylabel('Feedback')# $\phi$ ($\degree$)')
  312. a.set_xlim(linmin, linmax)
  313. a.set_ylim(linmin-10.5, linmax+52)
  314. a.set_xticks(np.arange(linmin, linmax+linstep, linstep))
  315. a.set_yticks(a.get_xticks()) # make y ticks the same as x ticks
  316. a.minorticks_off()
  317. a.set_aspect('equal')
  318. a.spines['left'].set_position(('outward', 4))
  319. a.spines['bottom'].set_position(('outward', 4))
  320. ## plot maxori delta phi PDF:
  321. figsize = DEFAULTFIGURESIZE
  322. f, a = plt.subplots(figsize=figsize)
  323. wintitle('opto delta phi grating pdf %s BURSTRATIOTHRESH=%g' % (st8, BURSTRATIOTHRESH))
  324. phibins = np.arange(-180, 180+15, 15)
  325. a.hist(dnbphis*180/pi, bins=phibins, color='black', histtype='step') # non burst mseus
  326. a.hist(dbphis*180/pi, bins=phibins, color='red', histtype='step') # burst mseus
  327. #a.axvline(x=0, ls='--', marker='', color='lightgray', zorder=-np.inf)
  328. a.set_xlabel('$\Delta\phi=\phi_{\mathregular{fb}}-\phi_{\mathregular{sup}}$ ($\degree$)')
  329. a.set_ylabel('Unit count')
  330. a.set_xlim(-180, 180)
  331. a.set_xticks([-180, 0, 180])
  332. #p, k = kuiper(dnbphis, dbphis, axis=0) # test if dnbphis and dbphis are different
  333. #a.add_artist(AnchoredText('p$=$%.2g' % p, loc='upper left', frameon=False))
  334. ## plot maxori delta phi CDF:
  335. figsize = DEFAULTFIGURESIZE
  336. f, a = plt.subplots(figsize=figsize)
  337. wintitle('opto delta phi grating cdf %s BURSTRATIOTHRESH=%g' % (st8, BURSTRATIOTHRESH))
  338. # add extra max bin to prevent vertical line:
  339. dnbphibins = list(np.unique(dnbphis*180/pi)) + [180]
  340. a.hist(dnbphis*180/pi, bins=dnbphibins, density=True, cumulative=True, histtype='step',
  341. color='black') # non burst mseus
  342. dbphibins = list(np.unique(dbphis*180/pi)) + [180]
  343. a.hist(dbphis*180/pi, bins=dbphibins, density=True, cumulative=True, histtype='step',
  344. color='red') # non burst mseus
  345. a.axvline(x=0, ls='--', marker='', color='lightgray', zorder=-np.inf)
  346. a.set_xlabel('$\Delta\phi=\phi_{\mathregular{fb}}-\phi_{\mathregular{sup}}$ ($\degree$)')
  347. a.set_ylabel('Cumulative probability')
  348. a.set_xlim(-110, 110)
  349. #a.set_xticks([-180, 0, 180])
  350. a.set_yticks([0, 0.5, 1])
  351. nnbadv = (dnbphis > 0).sum() # non-bursting units whose phase advanced
  352. nnbtotal = len(dnbphis) # total number of non-bursting units
  353. pnb = scipy.stats.binom_test(nnbadv, nnbtotal) # test if nnbadv and nnbtotal are equal
  354. print('non-burst units that advanced: %d/%d, p_binom=%g' % (nnbadv, nnbtotal, pnb))
  355. nbadv = (dbphis > 0).sum() # bursting units whose phase advanced
  356. nbtotal = len(dbphis) # total number of bursting units
  357. pb = scipy.stats.binom_test(nbadv, nbtotal) # test if nbadv and nbtotal are equal
  358. print('burst units that advanced: %d/%d, p_binom=%g' % (nbadv, nbtotal, pb))
  359. # scatter plot grating meanrates:
  360. figsize = DEFAULTFIGURESIZE[0]*1.02, DEFAULTFIGURESIZE[1] # tweak to make space for log units
  361. logmin, logmax = -1.1, 2
  362. if EXPTYPE == 'ntsrmvis':
  363. logmin, logmax = -1.5, 2
  364. logticks = np.array([-1, 0, 1, 2])
  365. #log0min = logmin + 0.05
  366. for st8 in ['none']:#ALLST8S:
  367. f, a = plt.subplots(figsize=figsize)
  368. wintitle('opto meanrate grating %s' % st8)
  369. rons, roffs, sgnfs, exmplis, exmplmseustrs, normlis = [], [], [], [], [], []
  370. keptmseui = 0 # manually init and increment instead of using enumerate()
  371. for mseustr in grtmseustrs:
  372. trialis = grtresp.loc[mseustr, st8]['trialis'] # non-blank & blank trialis
  373. if trialis.isna().any(): # missing for at least one opto condition
  374. continue
  375. ntrials = { opto:len(trialis[opto]) for opto in OPTOS } # should be equal
  376. ratesfull = pd.Series({ opto:np.full(ntrials[opto], np.nan) # pad
  377. for opto in [False, True] })
  378. meanrate = grtresp.loc[mseustr, st8]['meanrate']
  379. if meanrate.isna().any(): # missing for at least one opto condition, can't plot
  380. fig3.loc[mseustr][blnksname] = ratesfull # save nans for all trials
  381. continue
  382. rons.append(meanrate[True])
  383. roffs.append(meanrate[False])
  384. rates = grtresp.loc[mseustr, st8]['rates'] # trial-wise non-blank rates
  385. nnblnktrials = { opto:len(rates[opto]) for opto in OPTOS } # num non-blank trials
  386. for opto in OPTOS:
  387. ratesfull[opto][:nnblnktrials[opto]] = rates[opto]
  388. _, pval = ttest_ind(rates[False], rates[True], equal_var=False)
  389. sgnfs.append(pval < SCATTERPTHRESH) # bool
  390. fig3.loc[mseustr, 'meanrate'] = meanrate[False], meanrate[True] # save
  391. fig3.loc[mseustr]['trialis'] = trialis # save, for joining DataFrames trial-wise
  392. fig3.loc[mseustr]['rates'] = ratesfull # save padded trial-wise values
  393. if mseustr in grtmseu2exmpli:
  394. exmplis.append(keptmseui)
  395. exmplmseustrs.append(mseustr)
  396. else:
  397. normlis.append(keptmseui)
  398. keptmseui += 1 # manually increment
  399. rons = np.asarray(rons)
  400. roffs = np.asarray(roffs)
  401. sgnfs = np.asarray(sgnfs)
  402. # plot y=x line:
  403. xyline = [10**logmin, 10**logmax], [10**logmin, 10**logmax]
  404. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  405. insgnfis, = np.where(sgnfs == False)
  406. sgnfis, = np.where(sgnfs == True)
  407. normlinsgnfis = np.intersect1d(normlis, insgnfis)
  408. normlsgnfis = np.intersect1d(normlis, sgnfis)
  409. nsgnabovediag = (roffs[sgnfis] > rons[sgnfis]).sum()
  410. nsgnbelowdiag = (roffs[sgnfis] < rons[sgnfis]).sum()
  411. print('npoints=%d, nsgnf=%d, ninsgnf=%d, nsgnabovediag=%d, nsgnbelowdiag=%d'
  412. % (len(sgnfs), len(sgnfis), len(insgnfis), nsgnabovediag, nsgnbelowdiag))
  413. if len(normlinsgnfis) > 0:
  414. # plot normal (non-example) insignificant points:
  415. c = desat(st82clr[st8], SGNF2ALPHA[False]) # do manual alpha mixing
  416. a.scatter(rons[normlinsgnfis], roffs[normlinsgnfis], clip_on=False,
  417. marker='.', c='None', edgecolor=c, s=DEFSZ)
  418. if len(normlsgnfis) > 0:
  419. # plot normal (non-example) significant points:
  420. c = desat(st82clr[st8], SGNF2ALPHA[True]) # do manual alpha mixing
  421. a.scatter(rons[normlsgnfis], roffs[normlsgnfis], clip_on=False,
  422. marker='.', c='None', edgecolor=c, s=DEFSZ)
  423. # plot example points, one at a time:
  424. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  425. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  426. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  427. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  428. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  429. a.scatter(rons[exmpli], roffs[exmpli], clip_on=False,
  430. marker=marker, c=c, s=sz, lw=lw)
  431. a.set_xlabel('Suppression')# FR (spk/s)')
  432. a.set_ylabel('Feedback')# FR (spk/s)')
  433. a.set_xscale('log')
  434. a.set_yscale('log')
  435. a.set_xlim(10**logmin, 10**logmax)
  436. a.set_ylim(10**logmin, 10**logmax)
  437. a.set_xticks(10.0**logticks)
  438. a.set_yticks(a.get_xticks()) # make log scale y ticks the same as x ticks
  439. a.minorticks_off()
  440. axes_disable_scientific(a)
  441. a.set_aspect('equal')
  442. a.spines['left'].set_position(('outward', 4))
  443. a.spines['bottom'].set_position(('outward', 4))
  444. #t, p = ttest_rel(rons, roffs) # paired t-test
  445. #a.add_artist(AnchoredText('p$=$%.2g' % p, loc='lower right', frameon=False))
  446. #mu = rons.mean(), roffs.mean()
  447. #txt = '$\mathregular{\mu=%.1f, %.1f}$' % mu
  448. #a.add_artist(AnchoredText(txt, loc='upper left', frameon=False))
  449. # scatter plot grating burst ratio:
  450. figsize = DEFAULTFIGURESIZE[0]*1.04, DEFAULTFIGURESIZE[1] # tweak to make space for log units
  451. logmin, logmax = -3.55, 0
  452. if EXPTYPE == 'ntsrmvis':
  453. logmin, logmax = -3.83, 0
  454. logticks = np.array([-3, -2, -1, 0])
  455. for st8 in ['none']:#ALLST8S:
  456. f, a = plt.subplots(figsize=figsize)
  457. wintitle('opto burst ratio grating %s' % st8)
  458. brons, broffs, sgnfs, exmplis, exmplmseustrs, normlis = [], [], [], [], [], []
  459. keptmseui = 0 # manually init and increment instead of using enumerate()
  460. for mseustr in grtmseustrs:
  461. trialis = grtresp.loc[mseustr, st8]['trialis'] # non-blank & blank trialis
  462. if trialis.isna().any(): # missing for at least one opto condition
  463. continue
  464. ntrials = { opto:len(trialis[opto]) for opto in OPTOS } # should be equal
  465. burstratiosfull = pd.Series({ opto:np.full(ntrials[opto], np.nan) # pad
  466. for opto in [False, True] })
  467. br = grtresp.loc[mseustr, st8]['meanburstratio']
  468. if br.isna().any(): # missing for at least one opto condition, can't plot
  469. fig3.loc[mseustr]['burstratios'] = burstratiosfull # save nans for all trials
  470. continue
  471. brons.append(br[True])
  472. broffs.append(br[False])
  473. burstratios = grtresp.loc[mseustr, st8]['burstratios'] # non-blank burst ratios
  474. nnblnktrials = { opto:len(burstratios[opto]) for opto in OPTOS } # num non-blank trials
  475. for opto in OPTOS:
  476. burstratiosfull[opto][:nnblnktrials[opto]] = burstratios[opto]
  477. _, pval = ttest_ind(burstratios[False], burstratios[True], equal_var=False)
  478. sgnfs.append(pval < SCATTERPTHRESH) # bool
  479. fig3.loc[mseustr, 'meanburstratio'] = br[False], br[True] # save
  480. fig3.loc[mseustr]['burstratios'] = burstratiosfull # save padded trial-wise values
  481. if mseustr in grtmseu2exmpli:
  482. exmplis.append(keptmseui)
  483. exmplmseustrs.append(mseustr)
  484. else:
  485. normlis.append(keptmseui)
  486. keptmseui += 1 # manually increment
  487. brons, broffs = np.asarray(brons), np.asarray(broffs)
  488. sgnfs = np.asarray(sgnfs)
  489. # plot y=x line:
  490. xyline = [10**logmin, 10**logmax], [10**logmin, 10**logmax]
  491. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  492. insgnfis, = np.where(sgnfs == False)
  493. sgnfis, = np.where(sgnfs == True)
  494. normlinsgnfis = np.intersect1d(normlis, insgnfis)
  495. normlsgnfis = np.intersect1d(normlis, sgnfis)
  496. if len(normlinsgnfis) > 0:
  497. # plot normal (non-example) insignificant points:
  498. c = desat(st82clr[st8], SGNF2ALPHA[False]) # do manual alpha mixing
  499. a.scatter(brons[normlinsgnfis], broffs[normlinsgnfis], clip_on=True,
  500. marker='.', c='None', edgecolor=c, s=DEFSZ) # clip_on=False fails
  501. if len(normlsgnfis) > 0:
  502. # plot normal (non-example) significant points:
  503. c = desat(st82clr[st8], SGNF2ALPHA[True]) # do manual alpha mixing
  504. a.scatter(brons[normlsgnfis], broffs[normlsgnfis], clip_on=True,
  505. marker='.', c='None', edgecolor=c, s=DEFSZ) # clip_on=False fails
  506. # plot example points, one at a time:
  507. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  508. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  509. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  510. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  511. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  512. a.scatter(brons[exmpli], broffs[exmpli], clip_on=False,
  513. marker=marker, c=c, s=sz, lw=lw)
  514. a.set_xlabel('Suppression')# BR') # keep it short to maximize space for axes
  515. a.set_ylabel('Feedback')# BR')
  516. a.set_xscale('log')
  517. a.set_yscale('log')
  518. a.set_xlim(10**logmin, 10**logmax)
  519. a.set_ylim(10**logmin, 10**logmax)
  520. a.set_xticks(10.0**logticks)
  521. a.set_yticks(a.get_xticks()) # make log scale y ticks the same as x ticks
  522. a.minorticks_off()
  523. axes_disable_scientific(a)
  524. a.set_aspect('equal')
  525. a.spines['left'].set_position(('outward', 4))
  526. a.spines['bottom'].set_position(('outward', 4))
  527. #t, p = ttest_rel(brons, broffs) # paired t-test
  528. #a.add_artist(AnchoredText('p$=$%.2g' % p, loc='upper left', frameon=False))
  529. # scatter plot grating opto OSI, R2, and orientation preference:
  530. oris = np.arange(360) # get nice smooth model output in 1 deg steps
  531. OSITHRESH = 0.02 # minimum OSI required in both conditions to include in oripref scatter
  532. R2THRESH = 0.5 # minimum R2 tuning curve fit threshold to include in oripref scatter
  533. for st8 in ['none']:#ALLST8S:
  534. color = st82clr[st8]
  535. osioffs, osions, oproffs, oprons, R2offs, R2ons = [], [], [], [], [], []
  536. exmplis, exmplmseustrs, normlis, mseustrs = [], [], [], []
  537. keptmseui = 0 # manually init and increment instead of using enumerate()
  538. for mseustr in grtmseustrs:
  539. tunparams = grtresp.loc[mseustr, st8]['tun']
  540. tunrsqs = grtresp.loc[mseustr, st8]['tunrsq']
  541. if tunparams.isna().any(): # missing one or both tunparams
  542. print('%s: missing one or both opto conditions, skipping' % mseustr)
  543. continue
  544. offparams, onparams = tunparams[False], tunparams[True]
  545. # don't subtract spont (params[5]) - negative rates lead to OSI exceeding 1 a lot more:
  546. offrates = sum_of_gaussians(oris, *offparams[:5])# - offparams[5] # model output
  547. onrates = sum_of_gaussians(oris, *onparams[:5])# - onparams[5] # model output
  548. osioff, osion = vector_OSI(oris, offrates), vector_OSI(oris, onrates)
  549. osioffs.append(osioff)
  550. osions.append(osion)
  551. fig3.loc[mseustr, 'osi'] = osioff, osion # save
  552. if mseustr in grtmseu2exmpli: # print out OSI values for example units
  553. print('Example OSI %s, opto_on: %.3f, opto_off: %.3f' % (mseustr, osion, osioff))
  554. oproff, opron = offparams[0] % 180, onparams[0] % 180 # direction pref mod 180
  555. oproffs.append(oproff)
  556. oprons.append(opron)
  557. R2offs.append(tunrsqs[False])
  558. R2ons.append(tunrsqs[True])
  559. if mseustr in grtmseu2exmpli:
  560. exmplis.append(keptmseui)
  561. exmplmseustrs.append(mseustr)
  562. else:
  563. normlis.append(keptmseui)
  564. mseustrs.append(mseustr)
  565. keptmseui += 1 # manually increment
  566. osioffs, osions = np.asarray(osioffs), np.asarray(osions)
  567. oproffs, oprons = np.asarray(oproffs), np.asarray(oprons)
  568. R2offs, R2ons = np.asarray(R2offs), np.asarray(R2ons)
  569. mseustrs = np.asarray(mseustrs)
  570. ## scatter plot OSI:
  571. figsize = DEFAULTFIGURESIZE[0]*1.02, DEFAULTFIGURESIZE[1] # tweak for log units
  572. f, a = plt.subplots(figsize=figsize)
  573. wintitle('opto osi grating %s' % st8)
  574. logmin, logmax, logstep = -2.2, 0, 1
  575. logticks = np.array([-2, -1, 0])
  576. # plot y=x line:
  577. xyline = [10**logmin, 10**logmax], [10**logmin, 10**logmax]
  578. #xyline = [linmin, linmax], [linmin, linmax]
  579. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  580. # plot normal (non-example) points:
  581. a.scatter(osions[normlis], osioffs[normlis], clip_on=False,
  582. marker='.', c='None', edgecolor=st82clr[st8], s=DEFSZ)
  583. # plot example points, one at a time:
  584. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  585. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  586. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  587. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  588. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  589. a.scatter(osions[exmpli], osioffs[exmpli], clip_on=False,
  590. marker=marker, c=c, s=sz, lw=lw)
  591. a.set_xlabel('Suppression')# OSI')
  592. a.set_ylabel('Feedback')# OSI')
  593. a.set_xscale('log')
  594. a.set_yscale('log')
  595. a.set_xlim(10**logmin, 10**logmax)
  596. a.set_ylim(10**logmin, 10**logmax)
  597. a.set_xticks(10.0**logticks)
  598. a.set_yticks(a.get_xticks()) # make scale y ticks the same as x ticks
  599. a.minorticks_off()
  600. axes_disable_scientific(a)
  601. a.set_aspect('equal')
  602. a.spines['left'].set_position(('outward', 4))
  603. a.spines['bottom'].set_position(('outward', 4))
  604. #t, p = ttest_rel(osions, osioffs) # paired t-test
  605. #a.add_artist(AnchoredText('p$=$%.2g' % p, loc='upper left', frameon=False))
  606. ## scatter plot R2:
  607. figsize = DEFAULTFIGURESIZE
  608. f, a = plt.subplots(figsize=figsize)
  609. wintitle('opto R2 grating %s' % st8)
  610. linmin, linmax, linstep = 0, 1, 0.5
  611. xyline = [linmin, linmax], [linmin, linmax]
  612. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  613. # plot normal (non-example) points, coloring by state:
  614. a.scatter(R2ons[normlis], R2offs[normlis], clip_on=False,
  615. marker='.', c='None', edgecolor=st82clr[st8], s=DEFSZ)
  616. # plot example points, one at a time:
  617. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  618. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  619. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  620. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  621. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  622. a.scatter(R2ons[exmpli], R2offs[exmpli], clip_on=False,
  623. marker=marker, c=c, s=sz, lw=lw)
  624. a.set_xlabel(r'Suppression $\mathregular{R^2}$')
  625. a.set_ylabel(r'Feedback $\mathregular{R^2}$')
  626. a.set_xlim(linmin, linmax)
  627. a.set_ylim(linmin, linmax)
  628. a.set_xticks(np.arange(linmin, linmax+linstep, linstep))
  629. a.set_yticks(a.get_xticks())
  630. a.minorticks_off()
  631. a.set_aspect('equal')
  632. a.spines['left'].set_position(('outward', 4))
  633. a.spines['bottom'].set_position(('outward', 4))
  634. t, p = ttest_rel(R2ons, R2offs) # paired t-test
  635. a.add_artist(AnchoredText('p$=$%.2g' % p, loc='lower right', frameon=False))
  636. ## scatter plot oripref:
  637. figsize = DEFAULTFIGURESIZE[0]*1.02, DEFAULTFIGURESIZE[1] # tweak tick labels
  638. f, a = plt.subplots(figsize=figsize)
  639. wintitle('opto oripref grating %s OSITHRESH=%g R2THRESH=%g' % (st8, OSITHRESH, R2THRESH))
  640. linmin, linmax, linstep = 0, 180, 90
  641. xyline = [linmin, linmax], [linmin, linmax]
  642. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  643. if EXPTYPE == 'pvmvis':
  644. # wrap some points:
  645. wrapis = (oproffs < 15) & (oprons > 175)
  646. assert wrapis.sum() == 2
  647. oproffs[wrapis] = oproffs[wrapis] + 180 # wrap 2 points up over the y=x line
  648. # plot only units whose OSI satisfies OSITHRESH and R2 satisfies R2THRESH,
  649. # get indices of units that are well-tuned and well-fit in both conditions:
  650. tunis, = np.where((osioffs >= OSITHRESH) & (osions >= OSITHRESH) &
  651. (R2offs >= R2THRESH) & (R2ons >= R2THRESH))
  652. normlis = np.intersect1d(tunis, normlis)
  653. exmplis = np.intersect1d(tunis, exmplis)
  654. for mseustr, oproff, opron in zip(mseustrs[tunis], oproffs[tunis], oprons[tunis]):
  655. fig3.loc[mseustr, 'oripref'] = oproff, opron # save
  656. # plot normal (non-example) points, coloring by state:
  657. a.scatter(oprons[normlis], oproffs[normlis], clip_on=False,
  658. marker='.', c='None', edgecolor=st82clr[st8], s=DEFSZ)
  659. # plot example points, one at a time:
  660. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  661. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  662. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  663. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  664. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  665. a.scatter(oprons[exmpli], oproffs[exmpli], clip_on=False,
  666. marker=marker, c=c, s=sz, lw=lw)
  667. a.set_xlabel(r'Suppression')# $\theta$ ($\degree$)')
  668. a.set_ylabel(r'Feedback')# $\theta$ ($\degree$)')
  669. a.set_xlim(linmin, linmax)
  670. a.set_ylim(ymin=linmin)
  671. a.set_xticks(np.arange(linmin, linmax+linstep, linstep))
  672. a.set_yticks(a.get_xticks())
  673. a.minorticks_off()
  674. a.set_aspect('equal')
  675. a.spines['left'].set_position(('outward', 4))
  676. a.spines['bottom'].set_position(('outward', 4))
  677. #t, p = ttest_rel(oprons[tunis], oproffs[tunis]) # paired t-test
  678. #oriprefm, oriprefb, oriprefr, _, _ = linregress(oproffs[tunis], oprons[tunis])
  679. #oriprefrsq = oriprefr * oriprefr
  680. #txt = ('$\mathregular{R^2=}$%.2g\n'
  681. # '$\mathregular{p=}$%.2g' % (oriprefrsq, p))
  682. #a.add_artist(AnchoredText(txt, loc='upper left', frameon=False))
  683. ## plot OSI distributions, for choosing OSITHRESH:
  684. f, a = plt.subplots(figsize=figsize)
  685. wintitle('opto osi grating pdf %s' % st8)
  686. osibins = np.logspace(-2, 0, 15)
  687. offclr, onclr = desat(color, opto2alpha[False]), desat(color, opto2alpha[True])
  688. a.hist(osioffs, bins=osibins, color=offclr, histtype='step')
  689. a.hist(osions, bins=osibins, color=onclr, histtype='step')
  690. a.axvline(x=OSITHRESH, ls='--', marker='', color='lightgray', zorder=-np.inf)
  691. a.set_xlabel('OSI')
  692. a.set_ylabel('Unit count')
  693. a.set_xscale('log')
  694. ## plot R2 distributions, for choosing R2THRESH:
  695. f, a = plt.subplots(figsize=figsize)
  696. wintitle('opto R2 grating pdf %s' % st8)
  697. R2bins = np.linspace(0, 1, 11)
  698. a.hist(R2offs, bins=R2bins, color=offclr, histtype='step')
  699. a.hist(R2ons, bins=R2bins, color=onclr, histtype='step')
  700. a.axvline(x=R2THRESH, ls='--', marker='', color='lightgray', zorder=-np.inf)
  701. a.set_xlabel('Fit $\mathregular{R^2}$')
  702. a.set_ylabel('Unit count')
  703. # scatter plot grating opto mean f1 and f1/f0:
  704. logmin, logmax = -1, 2
  705. logticks = np.array([-1, 0, 1, 2])
  706. for st8 in ['none']:#ALLST8S:
  707. f1offs, f1ons, f1f0offs, f1f0ons, exmplis, exmplmseustrs, normlis = [], [], [], [], [], [], []
  708. keptmseui = 0 # manually init and increment instead of using enumerate()
  709. for mseustr in grtmseustrs:
  710. rasters = grtresp.loc[mseustr, st8]['raster']
  711. dt = grtresp.loc[mseustr, st8, False]['dt']
  712. tfreq = grtresp.loc[mseustr, st8, False]['tfreq']
  713. if rasters.isna().any(): # missing one or both rasters
  714. print('%s: missing one or both opto conditions, skipping' % mseustr)
  715. continue
  716. offraster, onraster = rasters[False], rasters[True]
  717. ## maybe consider std as well, to gauge significance of each f1f0 measure...
  718. f0off, _ = raster2freqcomp(offraster, dt, 0, mean='scalar')
  719. f1off, _ = raster2freqcomp(offraster, dt, tfreq, mean='scalar')
  720. f0on, _ = raster2freqcomp(onraster, dt, 0, mean='scalar')
  721. f1on, _ = raster2freqcomp(onraster, dt, tfreq, mean='scalar')
  722. f1f0off = f1off / f0off
  723. f1f0on = f1on / f0on
  724. f1offs.append(f1off)
  725. f1ons.append(f1on)
  726. f1f0offs.append(f1f0off)
  727. f1f0ons.append(f1f0on)
  728. fig3.loc[mseustr, 'f1f0'] = f1f0off, f1f0on # save
  729. if mseustr in grtmseu2exmpli:
  730. exmplis.append(keptmseui)
  731. exmplmseustrs.append(mseustr)
  732. else:
  733. normlis.append(keptmseui)
  734. keptmseui += 1 # manually increment
  735. f1offs = np.asarray(f1offs)
  736. f1ons = np.asarray(f1ons)
  737. f1f0offs = np.asarray(f1f0offs)
  738. f1f0ons = np.asarray(f1f0ons)
  739. ## scatter plot mean f1:
  740. figsize = DEFAULTFIGURESIZE
  741. f, a = plt.subplots(figsize=figsize)
  742. wintitle('opto mean f1 grating %s' % st8)
  743. # plot y=x line:
  744. xyline = [10**logmin, 10**logmax], [10**logmin, 10**logmax]
  745. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  746. # plot normal (non-example) points:
  747. a.scatter(f1ons[normlis], f1offs[normlis], clip_on=False,
  748. marker='.', c='None', edgecolor=st82clr[st8], s=DEFSZ)
  749. # plot example points, one at a time:
  750. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  751. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  752. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  753. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  754. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  755. a.scatter(f1ons[exmpli], f1offs[exmpli], marker=marker, clip_on=False,
  756. c=c, s=sz, lw=lw)
  757. a.set_xlabel('Suppression F1')
  758. a.set_ylabel('Feedback F1')
  759. a.set_xscale('log')
  760. a.set_yscale('log')
  761. a.set_xlim(10**logmin, 10**logmax)
  762. a.set_ylim(10**logmin, 10**logmax)
  763. a.set_xticks(10.0**logticks)
  764. a.set_yticks(a.get_xticks()) # make scale y ticks the same as x ticks
  765. a.minorticks_off()
  766. axes_disable_scientific(a)
  767. a.set_aspect('equal')
  768. a.spines['left'].set_position(('outward', 4))
  769. a.spines['bottom'].set_position(('outward', 4))
  770. #t, p = ttest_rel(f1ons, f1offs) # paired t-test
  771. #a.add_artist(AnchoredText('p$=$%.2g' % p, loc='lower right', frameon=False))
  772. ## scatter plot mean f1/f0
  773. figsize = DEFAULTFIGURESIZE
  774. f, a = plt.subplots(figsize=figsize)
  775. wintitle('opto mean f1f0 grating %s' % st8)
  776. # plot y=x line:
  777. linmin, linmax = 0, 2
  778. xyline = [linmin, linmax], [linmin, linmax]
  779. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  780. # plot normal (non-example) points:
  781. a.scatter(f1f0ons[normlis], f1f0offs[normlis], clip_on=False,
  782. marker='.', c='None', edgecolor=st82clr[st8], s=DEFSZ)
  783. # plot example points, one at a time:
  784. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  785. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  786. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  787. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  788. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  789. a.scatter(f1f0ons[exmpli], f1f0offs[exmpli], clip_on=False,
  790. marker=marker, c=c, s=sz, lw=lw)
  791. a.set_xlabel('Suppression')# F1/F0')
  792. a.set_ylabel('Feedback')# F1/F0')
  793. #a.set_xscale('log', basex=2)
  794. #a.set_yscale('log', basey=2)
  795. #a.set_xlim(0.125, 2)
  796. #a.set_ylim(0.125, 2)
  797. #a.set_xticks([0.125, 0.25, 0.5, 1, 2])
  798. #a.set_yticks(a.get_xticks()) # make scale y ticks the same as x ticks
  799. #axes_disable_scientific(a)
  800. a.set_xlim(linmin, linmax)
  801. a.set_ylim(linmin, linmax)
  802. a.set_xticks(np.arange(linmin, linmax+1))
  803. a.set_yticks(a.get_xticks()) # make scale y ticks the same as x ticks
  804. a.minorticks_off()
  805. a.set_aspect('equal')
  806. a.spines['left'].set_position(('outward', 4))
  807. a.spines['bottom'].set_position(('outward', 4))
  808. #t, p = ttest_rel(f1f0ons, f1f0offs) # paired t-test
  809. #a.add_artist(AnchoredText('p$=$%.2g' % p, loc='upper left', frameon=False))
  810. # fig4S1a: scatter plot grating meanburstratio FMI vs meanrate FMI:
  811. figsize = DEFAULTFIGURESIZE[0]*0.95, DEFAULTFIGURESIZE[1] # tweak to match others in 4S1
  812. linmin, linmax, linstep = -1, 1, 1
  813. ticks = np.arange(linmin, linmax+linstep, linstep)
  814. for st8 in ['none']:#ALLST8S:
  815. for measure in ['meanburstratio']:
  816. if measure.startswith('meanrate'):
  817. continue # don't bother plotting meanrate* against meanrate
  818. axislabel = measure2axislabel[measure]
  819. rfmis, msrfmis = [], []
  820. exmplis, exmplmseustrs, normlis = [], [], []
  821. keptmseui = 0 # manually init and increment instead of using enumerate()
  822. for mseustr in grtmseustrs:
  823. rfmi = grtFMI.loc[mseustr, st8]['meanrate']
  824. msrfmi = grtFMI.loc[mseustr, st8][measure]
  825. if pd.isna(rfmi) or pd.isna(msrfmi): # missing at least one FMI value
  826. continue
  827. rfmis.append(rfmi)
  828. msrfmis.append(msrfmi)
  829. if mseustr in grtmseu2exmpli:
  830. exmplis.append(keptmseui)
  831. exmplmseustrs.append(mseustr)
  832. else:
  833. normlis.append(keptmseui)
  834. keptmseui += 1 # manually increment
  835. rfmis, msrfmis = np.asarray(rfmis), np.asarray(msrfmis)
  836. ## scatter plot FMIs:
  837. f, a = plt.subplots(figsize=figsize)
  838. wintitle('opto FMI %s vs FMI rate %s' % (measure, st8))
  839. # plot x=0 and y=0 lines:
  840. a.axhline(y=0, ls='--', marker='', color='lightgray', zorder=-np.inf)
  841. a.axvline(x=0, ls='--', marker='', color='lightgray', zorder=-np.inf)
  842. # plot y=x line:
  843. xyline = [linmin, linmax], [linmin, linmax]
  844. a.plot(xyline[0], xyline[1], '--', color='gray', zorder=-1)
  845. # plot normal (non-example) points:
  846. a.scatter(rfmis[normlis], msrfmis[normlis], clip_on=False,
  847. marker='.', c='None', edgecolor=st82clr[st8], s=DEFSZ)
  848. # plot example points, one at a time:
  849. for exmpli, mseustr in zip(exmplis, exmplmseustrs):
  850. marker = exmpli2mrk[grtmseu2exmpli[mseustr]]
  851. c = exmpli2clr[grtmseu2exmpli[mseustr]]
  852. sz = exmpli2sz[grtmseu2exmpli[mseustr]]
  853. lw = exmpli2lw[grtmseu2exmpli[mseustr]]
  854. a.scatter(rfmis[exmpli], msrfmis[exmpli], marker=marker, c=c, s=sz, lw=lw)
  855. #mm, b, rr, p, stderr = linregress(rfmis, msrfmis)
  856. #rsq = rr * rr
  857. #x = np.array([rfmis.min(), rfmis.max()])
  858. #y = mm * x + b
  859. #txt = ('$\mathregular{R^{2}=%.2g}$' % rsq)
  860. #a.add_artist(AnchoredText(txt, loc='upper left', frameon=False))
  861. #a.plot(x, y, '-', color='red') # plot linregress fit
  862. a.set_xlabel('Firing rate FMI')
  863. if axislabel.islower():
  864. axislabel = axislabel.capitalize()
  865. ylabel = '%s FMI' % axislabel
  866. a.set_ylabel(ylabel)
  867. a.set_xlim(linmin, linmax)
  868. a.set_ylim(linmin, linmax)
  869. a.set_xticks(ticks)
  870. a.set_yticks(ticks)
  871. a.set_aspect('equal')
  872. a.spines['left'].set_position(('outward', 4))
  873. a.spines['bottom'].set_position(('outward', 4))