fig3.py 52 KB

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