def cuingEffectPupil(dm, traceParams=trialParams, epoch=(500,1000)): """ Determines the pupil cuing effect for a given dataset. This is the mean difference between bright and dark trials within a specific epoch. Arguments: dm -- A DataMatrix. Keyword arguments: traceParams -- The trace parameters. (default=trialParams) epoch -- The time interval for which to estimate the pupil effect. (default=(500,1000)) Returns: A value reflecting the pupil cuing effect. """ x1, y1, err1 = tk.getTraceAvg(dm.select('cueLum == "bright"', verbose= \ False), **traceParams) x2, y2, err2 = tk.getTraceAvg(dm.select('cueLum == "dark"', verbose= \ False), **traceParams) d = y2-y1 d = d[epoch[0]:epoch[1]] return d.mean()
def traceDiffPeaks(dm): """ desc: | Determines the peaks where the effect is largest, and smallest, in real units. This analysis is based on non-baselined pupil size. arguments: dm: A DataMatrix. """ traceParams = trialParams.copy() del traceParams['baseline'] x1, y1, err1 = tk.getTraceAvg(dm.select('cueLum == "bright"'), \ **traceParams) x2, y2, err2 = tk.getTraceAvg(dm.select('cueLum == "dark"'), \ **traceParams) xGrand, yGrand, errGrand = tk.getTraceAvg(dm, **traceParams) d = 100. * (y2-y1) / yGrand iMax = np.where(d == d.max())[0][0] iMin = np.where(d == d.min())[0][0] print 'Max effect: %.2f (sample %d)' % (d[iMax], iMax) print 'Min effect: %.2f (sample %d)' % (d[iMin], iMin) plt.subplot(211) plt.plot(y1, color=green[1]) plt.plot(y2, color=blue[1]) plt.plot(yGrand, color=gray[1]) plt.subplot(212) plt.plot(d, color='black') plt.axvline(iMax, color='black') plt.axvline(iMin, color='black') Plot.save('traceDiffPeaks')
def checkMissing(dm, checkCue=True, checkPostSacc=True): """ Checks whether there is missing data in the pupil traces, and filters trials with missing data out. Arguments: dm -- DataMatrix Keyword arguments: checkCue -- Indicates whether the cue phase should be checked. (default=True) checkPostSacc -- Indicates whether the postSacc phase should be checked. (default=True) Returns: The filtered DataMatrix. """ dm = dm.addField("missing", dtype=int, default=0) count = 0 for i in dm.range(): missing = False if checkCue: a = TraceKit.getTrace( dm[i], signal="pupil", phase="cue", lock="end", baseline=baseline, baselineLock=baselineLock, traceLen=preTraceLen, nanPad=False, ) if np.isnan(a.sum()): missing = True if checkPostSacc: a = TraceKit.getTrace( dm[i], signal="pupil", phase="postSacc", lock="start", baseline=baseline, baselineLock=baselineLock, traceLen=postTraceLen, nanPad=False, ) if np.isnan(a.sum()): missing = True if missing: dm["missing"][i] = 1 count += 1 print "Found %d (of %d) trials with missing data" % (count, len(dm)) dm = dm.select("missing == 0") return dm
def tracePlot(dm, traceParams=trialParams, suffix='', err=True, lumVar='cueLum', minSmp=200, diff=True): """ A pupil-trace plot for a single epoch. Arguments: dm -- A DataMatrix. Keyword arguments: traceParams -- The trace parameters. (default=trialParams) suffix -- A suffix to identify the trace. (default='') err -- Indicates whether error bars should be drawn. (default=True) lumVar -- The variable that contains the luminance information. (default='cueLum') diff -- Indicates whether the difference trace should be plotted as well. (default=True) """ # At the moment we can only determine error bars for cueLum assert(not err or lumVar == 'cueLum') assert(lumVar in ['cueLum', 'targetLum']) dmBright = dm.select('%s == "bright"' % lumVar) dmDark = dm.select('%s == "dark"' % lumVar) x1, y1, err1 = tk.getTraceAvg(dmBright, **traceParams) x2, y2, err2 = tk.getTraceAvg(dmDark, **traceParams) if err: d = y2-y1 aErr = lmeTrace(dm, traceParams=traceParams, suffix=suffix, \ cacheId='lmeTrace%s' % suffix) aT = aErr[:,0] aLo = aErr[:,1] aHi = aErr[:,2] minErr = (d-aLo)/2 maxErr = (aHi-d)/2 y1min = y1 - minErr y1max = y1 + maxErr y2min = y2 - minErr y2max = y2 + maxErr plt.fill_between(x1, y1min, y1max, color=green[1], alpha=.25) plt.fill_between(x2, y2min, y2max, color=blue[1], alpha=.25) tk.markStats(plt.gca(), np.abs(aT), below=False, thr=2, minSmp=minSmp) if diff: plt.plot(x1, diffY+y2-y1, color=orange[1], label='Pupillary cuing effect') if lumVar == 'cueLum': plt.plot(x1, y1, color=green[1], label='Cue on bright (N=%d)' \ % len(dmBright)) plt.plot(x2, y2, color=blue[1], label='Cue on dark (N=%d)' \ % len(dmDark)) elif lumVar == 'targetLum': plt.plot(x1, y1, color=green[1], label='Target on bright') plt.plot(x2, y2, color=blue[1], label='Target on dark')
def getDiffTrace(dm): """desc: | The difference pupil trace, i.e. the difference between Land-on-Dark and Land-on-Bright trials. arguments: dm: A DataMatrix. returns: | A difference trace. """ dmWhite = dm.select('saccCol == "white"', verbose=False) xAvg, yAvg, errAvg= TraceKit.getTraceAvg(dm, signal='pupil', phase='postSacc', traceLen=postTraceLen, baseline=baseline, baselineLock=baselineLock) xWhite, yWhite, errWhite = TraceKit.getTraceAvg(dmWhite, signal='pupil', phase='postSacc', traceLen=postTraceLen, baseline=baseline, baselineLock=baselineLock) yWhite -= yAvg return yWhite
def traceDiffPlot(dm, traceParams=trialParams, suffix='', err=True, \ color=blue[1], label=None): """ A pupil-trace plot for a single epoch. Arguments: dm -- A DataMatrix. Keyword arguments: traceParams -- The trace parameters. (default=trialParams) suffix -- A suffix to identify the trace. (default='') err -- Indicates whether error bars should be drawn. (default=True) Note: UNUSED color -- The line color. (default=blue[1]) label -- The line label. (default=None) """ x1, y1, err1 = tk.getTraceAvg(dm.select('cueLum == "bright"'), \ **traceParams) x2, y2, err2 = tk.getTraceAvg(dm.select('cueLum == "dark"'), **traceParams) d = y2-y1 plt.plot(d, color=color, label=label)
def tracePlot(dm, traceParams, suffix="", err=True): """ desc: | A pupil-trace plot for a single epoch. arguments: dm: A DataMatrix. traceParams: The trace parameters. keywords: suffix: A suffix to identify the trace. err: Indicates whether error bars should be drawn. """ x1, y1, err1 = TraceKit.getTraceAvg(dm.select('saccCol == "white"'), **traceParams) x2, y2, err2 = TraceKit.getTraceAvg(dm.select('saccCol == "black"'), **traceParams) if err: d = y1 - y2 aErr = lmeTrace(dm, traceParams=traceParams, suffix=suffix, cacheId="lmeTrace.%s%s" % (exp, suffix)) aT = aErr[:, 0] aLo = aErr[:, 2] aHi = aErr[:, 1] minErr = (d - aLo) / 2 maxErr = (aHi - d) / 2 y1min = y1 - minErr y1max = y1 + maxErr y2min = y2 - minErr y2max = y2 + maxErr plt.fill_between(x1, y1min, y1max, color=brightColor, label="Bright") plt.fill_between(x2, y2min, y2max, color=darkColor, label="Dark") TraceKit.markStats(plt.gca(), np.abs(aT), below=False, thr=2, minSmp=200, loExt=True, hiExt=True) plt.plot(x1, d, color="green") else: plt.plot(x1, y1, color=brightColor, label="Bright") plt.plot(x2, y2, color=darkColor, label="Dark")
def addFixStabInfo(dm, offset): print "Determining fixation stability ..." dm = dm.addField("fixStabX", dtype=float) for i in dm.range(): a = TraceKit.getTrace( dm[i], signal="x", phase="postSacc", lock="start", offset=offset, traceLen=postTraceLen - offset, nanPad=False, ) print "%.4d\tM = %.2f, SD = %.2f" % (i, a.mean(), np.std(a)) dm["fixStabX"][i] = np.std(a) print "Done" return dm
def matchBias(dm): """ Matches trials based on a bias towards the saccade target side. Arguments: dm -- DataMatrix Returns: A matched DataMatrix. """ dm = dm.addField("gazeBias", dtype=float) print "Adding gaze-bias information ..." for i in dm.range(): a = TraceKit.getTrace(dm[i], signal="x", phase="cue", traceLen=preTraceLen, lock="end", nanPad=False) saccSide = dm["saccSide"][i] _a = a[:-50] bias = (_a - 512).mean() if saccSide == "left": bias *= -1 dm["gazeBias"][i] = bias print "All: M = %.4f, SD = %.4f" % (dm["gazeBias"].mean(), dm["gazeBias"].std()) newFig() plt.subplot(2, 1, 1) plt.title("Unbalanced: M = %.4f, SD = %.4f" % (dm["gazeBias"].mean(), dm["gazeBias"].std())) plt.hist(dm["gazeBias"], bins=50, color=blue[1]) plt.axvline(dm["gazeBias"].mean()) # dm = dm.select('gazeBias < 0') dm = dm.balance("gazeBias", 0.1, verbose=True) # newDm = None # for _dm in dm.group('subject_nr'): # print 'Balancing subject_nr %d' % _dm['subject_nr'][0] # if newDm == None: # newDm = _dm.balance('gazeBias', 1) # else: # newDm += _dm.balance('gazeBias', 1) # dm = newDm dm = dm.select("__unbalanced__ == 0") plt.subplot(2, 1, 2) plt.title("Balanced: M = %.4f, SD = %.4f" % (dm["gazeBias"].mean(), dm["gazeBias"].std())) plt.hist(dm["gazeBias"], bins=50, color=blue[1]) plt.axvline(dm["gazeBias"].mean()) print "Antibias: M = %.4f, SD = %.4f" % (dm["gazeBias"].mean(), dm["gazeBias"].std()) saveFig("gazeBias") return dm
def toRadialShape(im, s=1024, smooth=501): """ desc: Extracts the radial outline from an image. argumens: im: desc: An image with 0 for background and 1 for shape. type: ndarray keywords: s: desc: The length of the output arrays. type: int smooth: desc: Smoothing window for the radius or None for no smoothing. type: [int, NoneType] returns: desc: A tuple with an array of angles and an array of radii. type: tuple """ im = edgeDetect(im) cx = im.shape[0]/2 cy = im.shape[1]/2 x, y = np.where(im != 0) a = np.arctan2(y-cy, x-cx) r = np.sqrt((x-cx)**2+(y-cy)**2) a = np.concatenate( (a-2*np.pi, a, a+2*np.pi) ) r = np.concatenate( (r, r, r ) ) i = np.argsort(a) a = a[i] r = r[i] if smooth != None: r = tk.smooth(r, windowLen=smooth) f = interpolate.interp1d(a, r) ia = np.linspace(-np.pi, np.pi, s) ir = f(ia) return ia, ir
def lmeTrace(dm, traceParams, suffix=""): """ desc: | Performs the lme analysis for the cueLum Bright vs Dark contrast. arguments: dm: A DataMatrix. traceParams: The trace parameters. keywords: suffix: A suffix to identify the trace. returns: | A 2D array where the columns are [p, ciHi, ciLo] and the rows are the samples. """ _dm = dm.selectColumns(["saccCol", "subject_nr", "__trace_postSacc__", "__trace_cue__", "__trace_baseline__"]) a = TraceKit.mixedModelTrace(_dm, traceModel, winSize=winSize, **traceParams) return a
def lmeTrace(dm, traceParams=trialParams, suffix=''): """ Performs the lme analysis for the cueLum Bright vs Dark contrast. To speed up the analysis, the results are cached. Arguments: dm -- A DataMatrix. Keyword arguments: traceParams -- The trace parameters. (default=trialParams) suffix -- A suffix to identify the trace. (default='') Returns: A 2D array where the columns are [p, ciHi, ciLo] and the rows are the samples. """ _dm = dm.selectColumns(['cueLum', 'subject_nr', '__trace_trial__', \ '__trace_baseline__']) a = tk.mixedModelTrace(_dm, traceModel, winSize=winSize, **traceParams) return a
def corrPlot(dm, soaBehav, soaPupil, suffix=''): """ Plots and analyzes the correlation between the cuing effect in behavior and pupil size. Arguments: dm -- A DataMatrix. soaBehav -- The SOA to analyze for the behavioral effect. soaPupil -- The SOA to analyze for the pupil effect. Keyword arguments: suffix -- A suffix for the plot filename. (default='') """ if stripCorr: suffix += '.stripCorr' Plot.new(size=Plot.ws) plt.title('SOA: %d ms (behavior), %d ms (pupil)' % (soaBehav+55, \ soaPupil+55)) plt.ylim(-.2, 1) plt.xlabel('Time since cue onset (ms)') plt.ylabel('Behavior - pupil correlation (r)') plt.axhline(0, linestyle='--', color='black') # Cue shading plt.axvspan(0, cueDur, color=blue[1], alpha=.2) # Target shading targetOnset = soaPupil+55 plt.axvspan(targetOnset, targetOnset+targetDur, color=blue[1], alpha=.2) plt.xlim(0, 2550) # Accuracy a = corrTrace(dm, soaBehav, soaPupil, dv='correct', suffix='acc', \ cacheId='corrTrace.correct.%d.%d%s' % (soaBehav, soaPupil, suffix)) tk.markStats(plt.gca(), a[:,1]) plt.plot(a[:,0], label='Accuracy', color=blue[1]) # RTs a = corrTrace(dm, soaBehav, soaPupil, dv='response_time', suffix='rt', \ cacheId='corrTrace.rt.%d.%d%s' % (soaBehav, soaPupil, suffix)) tk.markStats(plt.gca(), a[:,1]) tk.markStats(plt.gca(), a[:,1], color='red') plt.plot(a[:,0], label='Response times', color=orange[1]) plt.legend(frameon=False, loc='upper left') Plot.save('corrAnalysis.%d.%d%s' % (soaBehav, soaPupil, suffix), 'corrAnalysis', show=show)
def prepFit(dm, suffix=''): """ Perform a linear-regression fit on only the first 220 ms. Arguments: dm -- DataMatrix Keyword arguments: suffix -- A suffix for the output files. (default='') """ fig = newFig(size=bigWide) plt.subplots_adjust(wspace=0, hspace=0) i = 0 l = [['subjectNr', 'sConst', 'iConst', 'sOnset', 'iOnset', 'sSwap', 'iSwap']] for dm in [dm] + dm.group('subject_nr'): print 'Subject %s' % i row = [i] # We use a 6 x 2 plot grid # XX X X X X # XX X X X X if i == 0: # Overall plot ax = plt.subplot2grid((2,6),(0,0), colspan=2, rowspan=2) linewidth = 1 title = 'Full data (N=%d)' % len(dm.select('cond != "swap"')) else: # Individual plots ax = plt.subplot2grid((2,6),((i-1)/4, 2+(i-1)%4)) linewidth = 1 title = '%d (N=%d)' % (i, len(dm.select('cond != "swap"'))) plt.text(0.9, 0.1, title, horizontalalignment='right', \ verticalalignment='bottom', transform=ax.transAxes) if i == 0: plt.xticks([0, 50, 100, 150, 200]) elif i > 0 and i < 5: plt.xticks([]) else: plt.xticks([0, 100]) if i > 0: plt.yticks([]) else: plt.yticks([0, -.01, -.02]) plt.ylim(-.025, .01) plt.axhline(0, color='black', linestyle=':') for cond in ('constant', 'onset', 'swap'): _dm = dm.select("cond == '%s'" % cond, verbose=False) dmWhite = _dm.select('saccCol == "white"', verbose=False) xAvg, yAvg, errAvg= TraceKit.getTraceAvg(_dm, signal='pupil', \ phase='postSacc', traceLen=postTraceLen, baseline=baseline, \ baselineLock=baselineLock) xWhite, yWhite, errWhite = TraceKit.getTraceAvg(dmWhite, \ signal='pupil', phase='postSacc', traceLen=postTraceLen, \ baseline=baseline, baselineLock=baselineLock) yWhite -= yAvg xWhite = xWhite[:prepWin] yWhite = yWhite[:prepWin] if cond == 'constant': col = constColor lineStyle = '-' elif cond == 'onset': col = onsetColor lineStyle = '--' else: col = swapColor lineStyle = '-.' yWhite *= -1 opts = Plot.regress(xWhite, yWhite, lineColor=col, symbolColor=col, label=cond, annotate=False, symbol=lineStyle, linestyle=':') s = 1000.*opts[0] _i = 1000.*opts[1] print 's = %.4f, i = %.4f' % (s, _i) if i > 0: row += [s, _i] else: plt.legend(frameon=False, loc='upper right') if i > 0: l.append(row) i += 1 saveFig('linFitPrepWin%s' % suffix, show=False) dm = DataMatrix(l) dm.save('output/%s/linFitPrepWin%s.csv' % (exp, suffix)) # Summarize the data and perform ttests on the model parameters for dv in ['s', 'i']: print'\nAnalyzing %s' % dv aConst = dm['%sConst' % dv] aOnset = dm['%sOnset' % dv] aSwap = dm['%sSwap' % dv] print 'Constant: M = %f, SE = %f' % (aConst.mean(), \ aConst.std() / np.sqrt(N)) print 'Onset: M = %f, SE = %f' % (aOnset.mean(), \ aOnset.std() / np.sqrt(N)) print 'Swap: M = %f, SE = %f' % (aSwap.mean(), \ aSwap.std() / np.sqrt(N)) # Standard t-tests t, p = ttest_rel(aConst, aOnset) print 'Const vs onset, t = %.4f, p = %.4f' % (t, p) t, p = ttest_rel(aSwap, aOnset) print 'Swap vs onset, t = %.4f, p = %.4f' % (t, p) t, p = ttest_rel(aConst, aSwap) print 'Const vs swap, t = %.4f, p = %.4f' % (t, p)
def latencyCorr(dm): """ Determines the correlation between saccade latency and the latency of the PLR. Arguments: dm -- DataMatrix """ dm = dm.addField("saccLat2Bin", dtype=float) dm = dm.calcPerc("saccLat2", "saccLat2Bin", keys=["subject_nr"], nBin=10) # dm = dm.select('cond == "onset"') l = [["subject_nr", "cond", "bin", "saccLat", "t0"]] colors = TangoPalette.brightColors[:] * 10 for _dm in dm.group(["subject_nr", "saccLat2Bin"]): for cond in ("constant", "onset"): __dm = _dm.select("cond == '%s'" % cond, verbose=False) dmWhite = __dm.select('saccCol == "white"', verbose=False) xAvg, yAvg, errAvg = TraceKit.getTraceAvg( __dm, signal="pupil", phase="postSacc", traceLen=postTraceLen, baseline=baseline, baselineLock=baselineLock, ) xWhite, yWhite, errWhite = TraceKit.getTraceAvg( dmWhite, signal="pupil", phase="postSacc", traceLen=postTraceLen, baseline=baseline, baselineLock=baselineLock, ) yWhite -= yAvg opts = fitCurve(xWhite, yWhite, plot=False) t0 = opts[0] subject = __dm["subject_nr"][0] col = colors[int(subject)] saccLat = __dm["saccLat2"].mean() _bin = __dm["saccLat2Bin"][0] plt.plot(saccLat, t0, "o", color=col) l.append([subject, cond, _bin, saccLat, t0]) print subject, t0, saccLat dm = DataMatrix(l) dm.save("output/%s/latencyCorr.csv" % exp) print dm # plt.plot(dm['saccLat'], dm['t0'], '.') s, i, r, p, se = linregress(dm["saccLat"], dm["t0"]) print "r = %.4f, p = %.4f" % (r, p) plt.plot(dm["saccLat"], i + s * dm["saccLat"]) plt.show() newFig(size=(4.8, 2.4)) plt.subplots_adjust(bottom=0.2) pmX = PivotMatrix(dm, ["cond", "bin"], ["subject_nr"], "saccLat", colsWithin=True, err="se") print pmX pmY = PivotMatrix(dm, ["cond", "bin"], ["subject_nr"], "t0", colsWithin=True, err="se") print pmY xM = np.array(pmX.m[-2, 2:-2], dtype=float) xErr = np.array(pmX.m[-1, 2:-2], dtype=float) xMConst = xM[: len(xM) / 2] xMOnset = xM[len(xM) / 2 :] xErrConst = xErr[: len(xErr) / 2] xErrOnset = xErr[len(xErr) / 2 :] yM = np.array(pmY.m[-2, 2:-2], dtype=float) yErr = np.array(pmY.m[-1, 2:-2], dtype=float) yMConst = yM[: len(yM) / 2] yMOnset = yM[len(yM) / 2 :] yErrConst = yErr[: len(yErr) / 2] yErrOnset = yErr[len(yErr) / 2 :] plt.errorbar( xMConst, yMConst, xerr=xErrConst, yerr=yErrConst, label="Constant", capsize=0, color=constColor, fmt="o-" ) plt.errorbar(xMOnset, yMOnset, xerr=xErrOnset, yerr=yErrOnset, label="Onset", capsize=0, color=onsetColor, fmt="o-") plt.legend(frameon=False) plt.xlabel("Saccade latency (ms)") plt.ylabel("PLR latency (ms)") saveFig("latencyCorr") aov = AnovaMatrix(dm, ["cond", "bin"], "t0", "subject_nr") print aov aov.save("output/%s/aov.latencyCorr.csv" % exp)
def fit(dm, suffix='', maxSmp=None): """ Fits the data based on an exponential pupil model. Arguments: dm -- DataMatrixpass Keyword arguments: suffix -- A suffix for the output files. (default='') maxSmp -- The maximum number of samples to base the fit on, or None for no limit. (default=None) """ fig = newFig(size=bigWide) plt.subplots_adjust(wspace=0, hspace=0) i = 0 l = [['subjectNr', 't0Const', 'speedConst', 'lim1Const', 'lim2Const', \ 't0Onset', 'speedOnset', 'lim1Onset', 'lim2Onset']] for dm in [dm] + dm.group('subject_nr'): print 'Subject %s' % i row = [i] # We use a 6 x 2 plot grid # XX X X X X # XX X X X X if i == 0: # Overall plot ax = plt.subplot2grid((2,6),(0,0), colspan=2, rowspan=2) # Draw 'window of preparation' plt.axvspan(0, prepWin, color=green[1], alpha=.2) linewidth = 1 title = 'Full data (N=%d)' % len(dm.select('cond != "swap"')) else: # Individual plots ax = plt.subplot2grid((2,6),((i-1)/4, 2+(i-1)%4)) linewidth = 1 title = '%d (N=%d)' % (i, len(dm.select('cond != "swap"'))) plt.text(0.9, 0.1, title, horizontalalignment='right', \ verticalalignment='bottom', transform=ax.transAxes) if i == 0: plt.xticks([0, 500, 1000, 1500]) elif i > 0 and i < 5: plt.xticks([]) else: plt.xticks([0, 1000]) if i > 0: plt.yticks([]) else: plt.yticks([0, -.1, -.2, -.3]) for cond in ('constant', 'onset'): _dm = dm.select("cond == '%s'" % cond, verbose=False) dmWhite = _dm.select('saccCol == "white"', verbose=False) xAvg, yAvg, errAvg= TraceKit.getTraceAvg(_dm, signal='pupil', \ phase='postSacc', traceLen=postTraceLen, baseline=baseline, \ baselineLock=baselineLock) xWhite, yWhite, errWhite = TraceKit.getTraceAvg(dmWhite, \ signal='pupil', phase='postSacc', traceLen=postTraceLen, \ baseline=baseline, baselineLock=baselineLock) yWhite -= yAvg if cond == 'constant': col = constColor lineStyle = '-' elif cond == 'onset': col = onsetColor lineStyle = '--' else: col = swapColor lineStyle = '-' if maxSmp != None: xWhite = xWhite[:maxSmp] yWhite = yWhite[:maxSmp] opts = fitCurve(xWhite, yWhite, col=col, label=cond, \ linewidth=linewidth, lineStyle=lineStyle) print 't0 = %.4f, s = %.4f, l1 = %.4f, l2 = %.4f' % tuple(opts) t0 = opts[0] if i > 0: row += list(opts) else: plt.legend(frameon=False, loc='upper right') print '%s : %f' % (cond, t0) if i > 0: l.append(row) i += 1 saveFig('fit%s' % suffix, show=False) dm = DataMatrix(l) dm.save('output/%s/fit%s.csv' % (exp, suffix)) # Summarize the data and perform ttests on the model parameters for dv in ['t0', 'speed', 'lim1', 'lim2']: print'\nAnalyzing %s' % dv aConst = dm['%sConst' % dv] aOnset = dm['%sOnset' % dv] print 'Constant: M = %f, SE = %f' % (aConst.mean(), \ aConst.std() / np.sqrt(N)) print 'Onset: M = %f, SE = %f' % (aOnset.mean(), \ aOnset.std() / np.sqrt(N)) t, p = ttest_rel(aConst, aOnset) print 'Const vs onset, t = %.4f, p = %.4f' % (t, p)
def parseLine(self, trialDict, l): """ desc: Parses a single line from the EyeLink .asc file. arguments: trialDict: desc: Trial information. type: dict l: desc: A white-space-splitted line. type: list """ trialDict['subject_nr'] = int(trialDict['file'][2:4]) # The active period is the moment from the first round until the winner # is announced. This way we don't count the extra time taken by the # preview and end animation. if 'start_round' in l and l[3] == 0: self.active = True self.startTime = l[1] if 'status=winner' in l: self.active = False self.endTime = l[1] # Check for check fixation during the active period. if self.active: fix = self.toFixation(l) if fix != None: fixErr = np.sqrt( (fix['x']-xc)**2 + (fix['y']-yc)**2 ) trialDict['maxFixErr'] = max(trialDict['maxFixErr'], fixErr) self.fixList.append( (fix['x'], fix['y']) ) if 'fixation_lost' in l: trialDict['nFixLost'] += 1 # Get target coordinates # MSG 18895724 item id="B" status=init likelihood=1 ecc=375 # angle=0.785398163397 size=99 brightness=-1 color=green opacity=0.5 # x=265.165042945 y=265.16504 if 'target' in trialDict and ('id="%s"' % trialDict['target']) in l: if 'targetX' not in trialDict: # The x and y coordinates are sometimes chopped off, because the # logging string is too long. Therefore, we recalculate them # from the radius and eccentricity. for i in l: if type(i) != str: continue if 'ecc' in i: r = float(i[4:]) if 'angle' in i: a = float(i[6:]) x = np.cos(a) * r y = np.sin(a) * r # If possible, doubcheck the coordinates for strings that have # not been chopped off. try: _x = float(l[-2][2:]) _y = float(l[-1][2:]) assert(int(_x) == int(x)) assert(int(_y) == int(y)) except: print 'Failed to verify' trialDict['targetX'] = x trialDict['targetY'] = y # For Exp 1: # MSG 7093294 var correct 1 # For Exp 2+: # MSG 7093294 var correct_selection 1 if ('correct' in l or 'correct_selection' in l) and l[4] in (0, 1): self.correct = l[4] # Collect pupil trace # MSG 18625682 start_round 0 if 'start_round' in l: self.tracePhase = 'dummy' self.startRoundTime = l[1] # Determine whether the target is bright or dark if self.tracePhase != None and 'item' in l: if 'id="%s"' % trialDict['target'] in l: if 'brightness=1.0' in l: self.phaseType = 'bright' elif 'brightness=-1.0' in l: self.phaseType = 'dark' else: raise Exception('Incorrect brightness!') if 'end_invert' in l: self.endInvertTime.append(l[1] - self.startRoundTime) if 'end_adaptation' in l: self.endAdaptationTime.append(l[1] - self.startRoundTime) if 'end_collection' in l: self.endCollectionTime.append(l[1] - self.startRoundTime) if 'end_round' in l: if 'dummy' in self.traceDict: a = np.array(self.traceDict['dummy'], dtype=float) a[:,2] = tk.blinkReconstruct(a[:,2]) global rnd path = 'traces/subject%0d-%04d-%s.npy' % \ (trialDict['subject_nr'], rnd, self.phaseType) rnd += 1 print(path) np.save(path, a) del self.traceDict['dummy'] else: print('Warning: No trace collected!') self.tracePhase = None self.endRoundTime.append(l[1] - self.startRoundTime) self.roundId += 1 # Collect item information and likelihood ratios # MSG 1806804 item id="B" status=init likelihood=1 ecc=310 # angle=-0.785398163397 size=64 brightness=1 color=green opacity=0.5 # x=219.203102168 if self.tracePhase != None: # Process item if len(l) > 2 and l[2] == 'item': _dict = {} for s in l[3:]: _l = s.split('=') if len(_l) != 2: continue _dict[_l[0]] = _l[1].strip('"') if _dict['id'] == trialDict['target']: _id = 'target' else: _id = _dict['id'] if _id not in self.likelihoodTraces: self.likelihoodTraces[_id] = [] self.likelihoodTraces[_id].append(_dict['likelihood'])
def posTracePlots(dm, signal="x"): """ Analyzes the position traces. Most useful to see whether the eyes gravitate towards the saccade side before the actual saccade. Arguments: dm -- DataMatrix Keyword arguments: signal -- Indicates the trace signal. (default='x') """ for subjectNr in ["all"] + dm.unique("subject_nr"): print baseline, subjectNr if subjectNr == "all": _dm = dm else: _dm = dm.select("subject_nr == %d" % subjectNr, verbose=False) fig = newFig(size=(12, 4)) plt.subplots_adjust(wspace=0) # Select DataMatrices _dmLeft = _dm.select('saccSide == "left"', verbose=False) _dmRight = _dm.select('saccSide == "right"', verbose=False) # Get stats if subjectNr == "all": aStatsPre = np.load("stats/%s/x.pre.npy" % exp) print "First significant: %s" % np.where(aStatsPre[:, 0] < 0.05)[0] aErrLeft, aErrRight, aDiff = errorTrace(aStatsPre, _dmLeft, _dmRight, phase="cue", signal="x") else: aErrLeft, aErrRight, aDiff = None, None, None # Pre-saccade ax = plt.subplot2grid((1, 7), (0, 0), colspan=1) plt.axhline(384, linestyle="--", color="black") plt.ylim(0, 1024) plt.xlim(0, preTraceLen) plt.xticks(range(0, preTraceLen, 50), range(-preTraceLen, 1, 50)) if subjectNr == "all": TraceKit.markStats(ax, aStatsPre[:, 0], minSmp=1) TraceKit.plotTraceAvg( ax, _dmLeft, signal=signal, aErr=aErrLeft, phase="cue", lock="end", traceLen=preTraceLen, lineColor=brightColor, errColor=brightColor, ) TraceKit.plotTraceAvg( ax, _dmRight, signal=signal, aErr=aErrRight, phase="cue", lock="end", traceLen=preTraceLen, lineColor=darkColor, errColor=darkColor, ) # Post-saccade ax = plt.subplot2grid((1, 7), (0, 1), colspan=6) plt.axhline(384, linestyle="--", color="black") plt.ylim(0, 1024) plt.xlim(0, postTraceLen) plt.xticks(range(0, postTraceLen, 50)) plt.yticks([]) TraceKit.plotTraceAvg( ax, _dmLeft, signal=signal, phase="postSacc", traceLen=postTraceLen, lineColor=brightColor, errColor=brightColor, ) TraceKit.plotTraceAvg( ax, _dmRight, signal=signal, phase="postSacc", traceLen=postTraceLen, lineColor=darkColor, errColor=darkColor, ) saveFig("%s.posTrace.%s" % (signal, subjectNr), show=True)
def mainPlots(dm, perSubject=False, suffix="", zoom=False): """ desc: | Generates the main plots, consisting of four panes (difference, Constant, Swap, Onset) arguments: dm: DataMatrix keywords: perSubject: desc: | Indicates whether a single grand mean plot should be generated (False) or individual plots per subject (True). type: bool suffix: desc: A suffix for the plot filename. type: [str, unicode] zoom: desc: Indicates whether we should generate a zoom plot. type: bool """ if perSubject: subjectList = dm.unique("subject_nr") else: subjectList = ["all"] for subjectNr in subjectList: print ("Generating main plots for subject: %s" % subjectNr) if subjectNr == "all": _dm = dm else: _dm = dm.select("subject_nr == %d" % subjectNr, verbose=False) if zoom: fig = newFig(size=(2, 5)) else: fig = newFig(size=big) plt.subplots_adjust(wspace=0, hspace=0) # Pre difference plot if zoom: ax = plt.subplot2grid((4, 2), (0, 0), colspan=1) else: ax = plt.subplot2grid((4, 7), (0, 0), colspan=1) plt.ylabel("Pupil-size diff (norm.)") if zoom: plt.ylim(-0.03, 0.01) plt.yticks([-0.02, -0.01, 0]) plt.xticks([preTraceLen - 25], [-25]) plt.xlim(preTraceLen - 50, preTraceLen) else: plt.ylim(-0.5, 0.2) plt.yticks([0.0, -0.2, -0.4]) plt.xticks([25], [-150]) plt.xlim(0, preTraceLen) ax.spines["right"].set_visible(False) ax.get_yaxis().tick_left() ax.axhline(0, linestyle="--", color="black") # Draw saccade onsets sMin = _dm["flipSDelay"].min() sMax = _dm["flipSDelay"].max() sMean = _dm["flipSDelay"].mean() plt.axvspan(preTraceLen - sMin, preTraceLen - sMax, color=flipSColor, alpha=0.1) plt.axvline(preTraceLen - sMean, color=flipSColor, linestyle=flipEStyle) for cond in ("constant", "swap", "onset"): print "\t%s" % cond __dm = _dm.select('cond == "%s"' % cond, verbose=False) _dmWhite = __dm.select('saccCol == "white"', verbose=False) _dmBlack = __dm.select('saccCol == "black"', verbose=False) xPre, blackPre, err = TraceKit.getTraceAvg( _dmBlack, signal="pupil", phase="cue", lock="end", traceLen=preTraceLen, baseline=baseline ) xPre, whitePre, err = TraceKit.getTraceAvg( _dmWhite, signal="pupil", phase="cue", lock="end", traceLen=preTraceLen, baseline=baseline ) if cond == "constant": col = constColor style = constStyle elif cond == "swap": col = swapColor style = swapStyle else: col = onsetColor style = onsetStyle plt.plot(xPre, whitePre - blackPre, color=col, label=cond, linestyle=style) if cond == "swap": plt.plot(xPre, blackPre - whitePre, ":", color=col, label="inverse swap") # Post difference plot if zoom: ax = plt.subplot2grid((4, 2), (0, 1), colspan=6) else: ax = plt.subplot2grid((4, 7), (0, 1), colspan=6) ax.spines["left"].set_visible(False) plt.axvline(0, linestyle="--", color="black") if zoom: plt.ylim(-0.03, 0.01) plt.yticks([-0.02, -0.01, 0]) plt.xticks([0, 25]) plt.xlim(0, 50) else: plt.ylim(-0.5, 0.2) plt.yticks([0.0, -0.2, -0.4]) plt.xlim(0, postTraceLen) plt.xticks(range(0, postTraceLen, 150)) ax.axhline(0, linestyle="--", color="black") ax.get_yaxis().set_ticklabels([]) # Title plt.text( 0.1, 0.9, "Difference", horizontalalignment="center", verticalalignment="center", transform=ax.transAxes ) # Draw saccade offsets eMin = -_dm["flipEDelay"].min() eMax = -_dm["flipEDelay"].max() eMean = -_dm["flipEDelay"].mean() plt.axvspan(eMin, eMax, color=flipEColor, alpha=0.1) plt.axvline(eMean, color=flipEColor, linestyle=flipEStyle) # Draw 'window of preparation' plt.axvspan(0, prepWin, color=green[1], alpha=0.2) for cond in ("constant", "swap", "onset"): print "\t%s" % cond __dm = _dm.select('cond == "%s"' % cond, verbose=False) _dmWhite = __dm.select('saccCol == "white"', verbose=False) _dmBlack = __dm.select('saccCol == "black"', verbose=False) xPost, blackPost, err = TraceKit.getTraceAvg( _dmBlack, signal="pupil", phase="postSacc", lock="start", traceLen=postTraceLen, baseline=baseline ) xPost, whitePost, err = TraceKit.getTraceAvg( _dmWhite, signal="pupil", phase="postSacc", lock="start", traceLen=postTraceLen, baseline=baseline ) if cond == "constant": col = constColor style = constStyle elif cond == "swap": col = swapColor style = swapStyle else: col = onsetColor style = onsetStyle plt.plot(xPost, whitePost - blackPost, color=col, label=cond, linestyle=style) if cond == "swap": plt.plot(xPost, blackPost - whitePost, ":", color=col, label="inverse swap") plt.legend(frameon=False) # Main plots i = 1 for cond in ("constant", "swap", "onset"): print "\t%s" % cond __dm = _dm.select('cond == "%s"' % cond, verbose=False) _dmWhite = __dm.select('saccCol == "white"', verbose=False) _dmBlack = __dm.select('saccCol == "black"', verbose=False) if zoom: ax = plt.subplot2grid((4, 2), (i, 0), colspan=1) else: ax = plt.subplot2grid((4, 7), (i, 0), colspan=1) ax.spines["right"].set_visible(False) ax.get_yaxis().tick_left() ax.axhline(1.0, linestyle="--", color="black") # Draw saccade onsets sMin = __dm["flipSDelay"].min() sMax = __dm["flipSDelay"].max() sMean = __dm["flipSDelay"].mean() plt.axvspan(preTraceLen - sMin, preTraceLen - sMax, color=flipSColor, alpha=0.1) plt.axvline(preTraceLen - sMean, color=flipSColor, linestyle=flipSStyle) tracePlot(__dm, traceParamsPre, suffix=".%s.%s.pre" % (cond, subjectNr), err=True) plt.xticks([25], [-150]) if i < 3: ax.get_xaxis().set_ticklabels([]) if cond == "swap": plt.ylabel("Pupil size (norm.)") if zoom: plt.xlim(preTraceLen - 50, preTraceLen) plt.ylim(0.99, 1.05) plt.xticks([preTraceLen - 25], [-25]) else: plt.xlim(0, preTraceLen) plt.ylim(yMin, yMax) plt.xticks([25], [-150]) # Post-saccade if zoom: ax = plt.subplot2grid((4, 2), (i, 1), colspan=6) else: ax = plt.subplot2grid((4, 7), (i, 1), colspan=6) ax.spines["left"].set_visible(False) plt.axvline(0, linestyle="--", color="black") ax.axhline(1.0, linestyle="--", color="black") # Title plt.text(0.1, 0.9, cond, horizontalalignment="center", verticalalignment="center", transform=ax.transAxes) # Draw saccade offsets eMin = -__dm["flipEDelay"].min() eMax = -__dm["flipEDelay"].max() eMean = -__dm["flipEDelay"].mean() plt.axvspan(eMin, eMax, color=flipEColor, alpha=0.1) plt.axvline(eMean, color=flipEColor, linestyle=flipEStyle) tracePlot(__dm, traceParamsPost, suffix=".%s.%s.post" % (cond, subjectNr), err=True) if zoom: plt.xlim(0, 50) plt.ylim(0.99, 1.05) plt.xticks([0, 25]) else: plt.xlim(0, postTraceLen) plt.ylim(yMin, yMax) plt.xticks(range(0, postTraceLen, 150)) if i == 3: plt.xlabel("Time after saccade (ms)") if i < 3: ax.get_xaxis().set_ticklabels([]) ax.get_yaxis().tick_right() ax.get_yaxis().set_ticklabels([]) if cond == "constant": plt.legend(frameon=False, loc="lower left") i += 1 saveFig("main.subject.%s%s" % (subjectNr, suffix), show=True)
def fitSwap(dm, suffix='all'): """ desc: | Performs a mixture-model of the swap condition. arguments: dm: A DataMatrix, keywords: suffix: desc: A suffix. type: [str, unicode] returns: desc: | - None if suffix is 'all' - Otherwise An (i1, i2) tuple indicating the end of the preparation period and the start of the non-preparation period. type: [tuple, None] """ dmSwap = dm.select('cond == "swap"', verbose=False) ySwap = getDiffTrace(dmSwap, cacheId='swapDiff%s.%s' % (exp, suffix)) dmOnset = dm.select('cond == "onset"', verbose=False) yOnset = getDiffTrace(dmOnset, cacheId='onsetDiff%s.%s' % (exp, suffix)) dmConst= dm.select('cond == "constant"', verbose=False) yConst = getDiffTrace(dmConst, cacheId='constDiff%s.%s' % (exp, suffix)) lP = [] lErr = [] for i in np.arange(1000): y1 = yConst[i] y2 = yOnset[i] y3 = ySwap[i] pBest = None errBest = None for p in np.linspace(0, 1, 1000): y3est = p*-y1 + (1-p)*y2 err = abs(y3est-y3) if errBest == None or err < errBest: errBest = err pBest = p lP.append(pBest) lErr.append(errBest) aP = np.array(lP) fig = newFig(size=flat) plt.ylim(-.1, 1.1) plt.axhline(0, linestyle=':', color='black') plt.axhline(1, linestyle=':', color='black') plt.yticks(np.linspace(0,1,6)) plt.ylabel('Preparation index') plt.xlabel('Time relative to display change (ms)') if suffix == 'all': # Values hard-coded based on fitSwapAll() output plt.axvspan(352.50-1.96*13.22, 352.50+1.96*13.22, color=brown[1], alpha=.1) plt.axvline(352.50, color=brown[1], linestyle='--') else: lRoi = TraceKit.markStats(plt.gca(), aP, thr=.5, below=False, color=green[0]) if len(lRoi) != 1: i1 = np.nan else: i1 = lRoi[0][1] lRoi = TraceKit.markStats(plt.gca(), aP, thr=.5, below=True, color=red[0]) if len(lRoi) != 1: i2 = np.nan else: i2 = lRoi[0][0] plt.plot(aP, color=blue[1]) saveFig('fitSwap/%s' % suffix) if suffix != 'all': return i1, i2
def __finishTrial__(self, trialDict): """ Perform some finalization after we we have parsed a trial Arguments: trialDict -- a trial dictionary """ nPhase = len(self.traceDict) i = 1 if self.traceImg or self.tracePlot: plt.clf() plt.close() plt.figure(figsize=(12,12)) plt.subplots_adjust(hspace=.5, wspace=.5) for phase, trace in self.traceDict.iteritems(): a = np.array(trace, dtype=float) if len(a) == 0: continue origA = a.copy() if self.blinkReconstruct: a[:,2] = TraceKit.blinkReconstruct(a[:,2]) if self.traceSmoothParams != None: a[:,0] = TraceKit.smooth(a[:,0], **self.traceSmoothParams) a[:,1] = TraceKit.smooth(a[:,1], **self.traceSmoothParams) a[:,2] = TraceKit.smooth(a[:,2], **self.traceSmoothParams) self.traceDict[phase] = a path = os.path.join(self.traceFolder, '%s-%.5d-%s.npy' \ % (trialDict['file'], trialDict['trialId'], phase)) if not os.path.exists(self.traceFolder): print('Creating traceFolder: %s' % self.traceFolder) os.makedirs(self.traceFolder) np.save(path, a) trialDict['__trace_%s__' % phase] = path if self.traceImg or self.tracePlot: plt.subplot(nPhase, 3, i) i += 1 plt.title('X(%s)' % phase) plt.plot(a[:,0]) if self.traceSmoothParams != None: plt.plot(origA[:,0]) plt.subplot(nPhase, 3, i) i += 1 plt.title('Y(%s)' % phase) plt.plot(a[:,1]) if self.traceSmoothParams != None: plt.plot(origA[:,1]) plt.subplot(nPhase, 3, i) i += 1 plt.title('Pupil(%s)' % phase) plt.plot(a[:,2]) if self.traceSmoothParams != None or self.blinkReconstruct: plt.plot(origA[:,2]) if self.traceImg or self.tracePlot: plt.suptitle(path) if self.traceImg: path = os.path.join(self.traceFolder, 'png', '%s-%.5d-%s.png' \ % (trialDict['file'], trialDict['trialId'], phase)) if not os.path.exists(os.path.join(self.traceFolder, 'png')): print('Creating traceImgFolder: %s' % os.path.join( \ self.traceFolder, 'png')) if not os.path.exists(os.path.join(self.traceFolder, 'png')): os.makedirs(os.path.join(self.traceFolder, 'png')) plt.savefig(path) if self.tracePlot: plt.show()
def corrTrace(dm, soaBehav, soaPupil, dv='correct', suffix='', \ traceParams=trialParams): """ Calculates the correlation between the behavioral cuing effect and the pupil-size cuing effect. Arguments: dm -- A DataMatrix. soaBehav -- The SOA to analyze for the behavioral effect. soaPupil -- The SOA to analyze for the pupil effect. Keyword arguments: dv -- The dependent variable to use for the behavioral cuing effect. (default='correct') suffix -- A suffix to identify the trace for caching. (default='') Returns: A 2D numpy array r- and p-values for each sample. """ assert(soaPupil in dm.unique('soa')) assert(soaBehav in dm.unique('soa')) dmBehav = dm.select('soa == %d' % soaBehav) dmPupil = dm.select('soa == %d' % soaPupil) nSubject = dmPupil.count('subject_nr') # Determine the trace length and create the trace plot traceLen = soaPupil + 105 traceParams = trialParams.copy() traceParams['traceLen'] = traceLen # First determine the behavioral cuing effect for each participant print 'Determining behavioral cuing effect ...' aCuingEffect = np.empty(nSubject) for _dm in dmBehav.group('subject_nr'): i = _dm['subject_nr'][0] aCuingEffect[i] = cuingEffectBehav(_dm, dv=dv) print aCuingEffect print 'M = %.4f (%.4f)' % (aCuingEffect.mean(), aCuingEffect.std()) print 'Done' # Next create 2 dimensional array with pupil-effect traces for each # participant over time print 'Creating pupil-effect traces ...' aPupilEffect = np.empty( (nSubject, traceLen) ) for _dm in dmPupil.group('subject_nr'): i = _dm['subject_nr'][0] print 'Subject %d' % i x1, y1, err1 = tk.getTraceAvg(_dm.select('cueLum == "bright"', \ verbose=False), **traceParams) x2, y2, err2 = tk.getTraceAvg(_dm.select('cueLum == "dark"', \ verbose=False), **traceParams) d = y2-y1 aPupilEffect[i] = d print 'Done' # Now walk through the pupil-effect array sample by sample and determine # the correlation with the behavioral cuing effect. print 'Determine correlations ...' aStats = np.empty( (traceLen, 2) ) for i in range(traceLen): if stripCorr: index, a, b = stripStd(aCuingEffect, aPupilEffect[:,i]) else: a, b = aCuingEffect, aPupilEffect[:,i] s, _i, r, p, se = linregress(a, b) print 'soaBehav: %d, soaPupil: %d' % (soaBehav, soaPupil) print '%d: r = %.4f, p = %.4f (N = %d)' % (i, r, p, len(a)) aStats[i,0] = r aStats[i,1] = p print 'Done' return aStats