from IPython.display import display, Image display(Image(filename='images/surrounded.png')) display(Image(filename='images/comparison.png')) ortho = mlab.csv2rec('data/ortho.csv') para = mlab.csv2rec('data/para.csv') fig, ax = plt.subplots(1) # We apply a small vertical jitter to each point, just to show that there are multiple points at each location: ax.plot(ortho['contrast1'], ortho['answer'] + np.random.randn(len(ortho)) * 0.02 , '*') ax.plot(para['contrast1'], para['answer'] + np.random.randn(len(para)) * 0.02 , '+') ax.set_ylim([0.9, 2.1]) ax.set_xlabel('Contrast 1') ax.set_ylabel('Which stimulus had higher contrast? (1 or 2)') fig.set_size_inches([8,8]) def transform_data(data): """ Function that takes experimental data and gives us the dependent/independent variables for analysis Parameters ---------- data : rec array The data with records: `contrast1`, `contrast2` and `answer` Returns ------- x : The unique contrast differences. y : The proportion of '2' answers in each contrast difference n : The number of trials in each x,y condition """ contrast1 = data['contrast1'] answers = data['answer'] x = np.unique(contrast1) y = [] n = [] for c in x: idx = np.where(contrast1 == c) n.append(float(len(idx[0]))) answer1 = len(np.where(answers[idx] == 1)[0]) y.append(answer1 / n[-1]) return x,y,n x_ortho, y_ortho, n_ortho = transform_data(ortho) x_para, y_para, n_para = transform_data(para) fig, ax = plt.subplots(1) # To plot each point with size proportional to the number of trials in that condition: for x,y,n in zip(x_ortho, y_ortho, n_ortho): ax.plot(x, y, 'bo', markersize=n) for x,y,n in zip(x_para, y_para, n_para): ax.plot(x, y, 'go', markersize=n) ax.set_xlabel('Contrast in interval 1') ax.set_ylabel('Proportion answers `1`') ax.set_ylim([-0.1, 1.1]) ax.set_xlim([-0.1, 1.1]) ax.grid('on') fig.set_size_inches([8,8]) # Note that the coefficients go in the other direction than my equation above (the constant comes last): beta1_ortho, beta0_ortho = np.polyfit(x_ortho, y_ortho, 1) beta1_para, beta0_para = np.polyfit(x_para, y_para, 1) # Let's show the data and the model fit, # polyval evaluates the model at an arbitrary set of x values: x = np.linspace(np.min([x_ortho, x_para]), np.max([x_ortho, x_para]), 100) fig, ax = plt.subplots(1) ax.plot(x, np.polyval([beta1_ortho, beta0_ortho], x)) ax.plot(x_ortho, y_ortho, 'bo') ax.plot(x, np.polyval([beta1_para, beta0_para], x)) ax.plot(x_para, y_para, 'go') ax.set_xlabel('Contrast 1') ax.set_ylabel('Proportion responses `1`') ax.set_ylim([-0.1, 1.1]) ax.set_xlim([-0.1, 1.1]) ax.grid('on') fig.set_size_inches([8,8]) #Let's quantify the fit by calculating the sum of squared errors relative to the actual data: SSE_ortho = np.sum((y_ortho - np.polyval([beta1_ortho, beta0_ortho], x_ortho))**2) SSE_para = np.sum((y_para - np.polyval([beta1_para, beta0_para], x_para))**2) print(SSE_ortho + SSE_para) print('PSE for the orthogonal condition is:%s'%((0.5 - beta0_ortho)/beta1_ortho)) print('PSE for the parallel condition is:%s'%((0.5 - beta0_para)/beta1_para)) from scipy.special import erf def cumgauss(x, mu, sigma): """ The cumulative Gaussian at x, for the distribution with mean mu and standard deviation sigma. Parameters ---------- x : float or array The values of x over which to evaluate the cumulative Gaussian function mu : float The mean parameter. Determines the x value at which the y value is 0.5 sigma : float The variance parameter. Determines the slope of the curve at the point of Deflection Returns ------- Notes ----- Based on: http://en.wikipedia.org/wiki/Normal_distribution#Cumulative_distribution_function """ return 0.5 * (1 + erf((x-mu)/(np.sqrt(2)*sigma))) fig, ax = plt.subplots(1) ax.plot(x, cumgauss(x, 0.5, 0.25), label=r'$\mu=0, \sigma=0.25$') ax.plot(x, cumgauss(x, 0.5, 0.5), label=r'$\mu=0, \sigma=0.5$') ax.plot(x, cumgauss(x, 0.5, 0.75), label=r'$\mu=0, \sigma=0.75$') ax.plot(x, cumgauss(x, 0.3, 0.25), label=r'$\mu=0.3, \sigma=0.25$') ax.plot(x, cumgauss(x, 0.7, 0.25), label=r'$\mu=0.3, \sigma=0.25$') ax.set_ylim([-0.1, 1.1]) ax.set_xlim([-0.1, 1.1]) ax.grid('on') fig.set_size_inches([8,8]) plt.legend(loc='lower right') def err_func(params, x, y, func): """ Error function for fitting a function Parameters ---------- params : tuple A tuple with the parameters of `func` according to their order of input x : float array An independent variable. y : float array The dependent variable. func : function A function with inputs: `(x, *params)` Returns ------- The marginals of the fit to x/y given the params """ return y - func(x, *params) import scipy.optimize as opt # Let's guess the inital conditions: initial = 0,0.5 # We get the params, and throw away the second output of leastsq: params_ortho, _ = opt.leastsq(err_func, initial, args=(x_ortho, y_ortho, cumgauss)) params_para, _ = opt.leastsq(err_func, initial, args=(x_para, y_para, cumgauss)) plot(x, cumgauss(x, params_ortho[0], params_ortho[1])) plot(x, cumgauss(x, params_para[0], params_para[1])) plot(x_ortho, y_ortho, 'bo') plot(x_para, y_para, 'go') ax.set_ylim([-0.1, 1.1]) ax.set_xlim([-0.1, 1.1]) ax.grid('on') fig.set_size_inches([8,8]) SSE_ortho = np.sum((y_ortho - cumgauss(x_ortho, *params_ortho))**2) SSE_para = np.sum((y_para - cumgauss(x_para, *params_para))**2) print(SSE_ortho + SSE_para) print('PSE for the orthogonal condition is:%s'%params_ortho[0]) print('PSE for the parallel condition is:%s'%params_para[0]) def weibull(x,threshx,slope,guess,flake): """ The Weibull cumulative distribution function Parameters ---------- x : float or array The values of x over which to evaluate the cumulative Weibull function threshx : float The value of x at the deflection point of the function. For a guess set to 0.5, this is at approximately 0.81 slope : float The slope of the function at the deflection point. guess : float The guess rate. Between 0 and 1. For example, for a two-alternative forced-choice experiment, this would be 0.5 flake : float The upper asymptote of the function. Often thought of as indicative of lapses in performance ('flake rate') """ threshy = 1 - (1 - guess) * np.exp(-1) flake = 1 - flake k = (-np.log((1 - threshy) / (1 - guess))) ** (1 / slope) return (flake - (flake - guess) * np.exp(-(k * x / threshx) ** slope)) fig, ax = plt.subplots(1) ax.plot(x, weibull(x, 0.5, 3.5, 0.5, 0.05), label='threshx=0.5, slope=3.5, guess=0.5, flake=0.05') ax.plot(x, weibull(x, 0.5, 3.5, 0, 0.05), label='threshx=0.5, slope=3.5, guess=0, flake=0.05') ax.plot(x, weibull(x, 0.5, 3.5, 0, 0.15), label='threshx=0.5, slope=3.5, guess=0, flake=0.15') ax.plot(x, weibull(x, 0.25, 3.5, 0.5, 0.05), label='threshx=0.25, slope=3.5, guess=0.5, flake=0.05') ax.plot(x, weibull(x, 0.75, 3.5, 0, 0.15), label='threshx=0.75, slope=3.5, guess=0, flake=0.15') ax.set_ylim([-0.1, 1.1]) ax.set_xlim([-0.1, 1.1]) ax.grid('on') fig.set_size_inches([8,8]) plt.legend(loc='lower right') # We guess the inital conditions again: initial = 0.5,3.5,0,0 # fit again with leastsq, this time passing weibull as the input: params_ortho, _ = opt.leastsq(err_func, initial, args=(x_ortho, y_ortho, weibull)) params_para, _ = opt.leastsq(err_func, initial, args=(x_para, y_para, weibull)) fig, ax = plt.subplots(1) ax.plot(x, weibull(x, *params_ortho)) ax.plot(x, weibull(x, *params_para)) ax.plot(x_ortho, y_ortho, 'bo') ax.plot(x_para, y_para, 'go') ax.set_ylim([-0.1, 1.1]) ax.set_xlim([-0.1, 1.1]) ax.grid('on') fig.set_size_inches([8,8]) SSE_ortho = np.sum((y_ortho - weibull(x_ortho, *params_ortho))**2) SSE_para = np.sum((y_para - weibull(x_para, *params_para))**2) print(SSE_ortho + SSE_para) beta_ortho = np.polyfit(x_ortho, y_ortho, 7) beta_para = np.polyfit(x_para, y_para, 7) fig, ax = plt.subplots(1) ax.plot(x, np.polyval(beta_ortho, x)) ax.plot(x_ortho, y_ortho, 'bo') ax.plot(x, np.polyval(beta_para, x)) ax.plot(x_para, y_para, 'go') ax.set_ylim([-0.1, 1.5]) ax.set_xlim([-0.1, 1.1]) ax.grid('on') fig.set_size_inches([8,8]) SSE_ortho = np.sum((y_ortho - np.polyval(beta_ortho, x_ortho))**2) SSE_para = np.sum((y_para - np.polyval(beta_para, x_para))**2) print(SSE_ortho + SSE_para) # Split the data into testing and training sets: x_ortho_1 = x_ortho[1::2] y_ortho_1 = y_ortho[1::2] x_ortho_2 = x_ortho[::2] y_ortho_2 = y_ortho[::2] x_para_1 = x_para[1::2] y_para_1 = y_para[1::2] x_para_2 = x_para[::2] y_para_2 = y_para[::2] initial = 0,0.5 # Fit to the training data params_ortho_1, _ = opt.leastsq(err_func, initial, args=(x_ortho_1, y_ortho_1, cumgauss)) params_para_1, _ = opt.leastsq(err_func, initial, args=(x_para_1, y_para_1, cumgauss)) # Check SSE on the testing data: SSE_cumgauss = (np.sum((y_ortho_2 - cumgauss(x_ortho_2, *params_ortho_1))**2) + np.sum((y_para_2 - cumgauss(x_para_2, *params_para_1))**2)) # Do this again, reversing training and testing data sets: params_ortho_2, _ = opt.leastsq(err_func, initial, args=(x_ortho_2, y_ortho_2, cumgauss)) params_para_2, _ = opt.leastsq(err_func, initial, args=(x_para_2, y_para_2, cumgauss)) # Check SSE on the testing data: SSE_cumgauss += (np.sum((y_ortho_1 - cumgauss(x_ortho_1, *params_ortho_2))**2) + np.sum((y_para_1 - cumgauss(x_para_1, *params_para_2))**2)) print("For the cumulative Gaussian SSE=%s"%SSE_cumgauss) # Let's do the same for the Weibull: initial = 0.5,3.5,0,0 params_ortho_1, _ = opt.leastsq(err_func, initial, args=(x_ortho_1, y_ortho_1, weibull)) params_para_1, _ = opt.leastsq(err_func, initial, args=(x_para_1, y_para_1, weibull)) SSE_weibull = (np.sum((y_ortho_2 - weibull(x_ortho_2, *params_ortho_1))**2) + np.sum((y_para_2 - weibull(x_para_2, *params_para_1))**2)) params_ortho_2, _ = opt.leastsq(err_func, initial, args=(x_ortho_2, y_ortho_2, weibull)) params_para_2, _ = opt.leastsq(err_func, initial, args=(x_para_2, y_para_2, weibull)) SSE_weibull += (np.sum((y_ortho_1 - weibull(x_ortho_1, *params_ortho_2))**2) + np.sum((y_para_1 - weibull(x_para_1, *params_para_2))**2)) print("For the Weibull SSE=%s"%SSE_weibull)