fit_noise.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. #!/user/bin/env python
  2. # coding=utf-8
  3. """
  4. Fit PF to estimate 76% correctness threshold of noise level.
  5. x = xShift+sqrt(2)*sd*(erfinv(((yy-chance)/(1-chance-lapse)-.5)*2))
  6. @author: yannansu
  7. @created at: 06.04.21 14:53
  8. """
  9. import numpy as np
  10. import matplotlib.pyplot as plt
  11. from scipy import special
  12. from psychopy import data
  13. from .fit_curves import FitCumNormal
  14. from .load_data import LoadData
  15. # from scipy.optimize import curve_fit
  16. class FitNoise:
  17. def __init__(self, df):
  18. self.df = df
  19. def show_stairs(self):
  20. grp = self.df.groupby('time_index')
  21. fig, axes = plt.subplots(grp.ngroups, sharex=True, figsize=(8, 12))
  22. for i, (t_index, d) in enumerate(grp):
  23. noise = d.noise.values
  24. judge = d.judge.values
  25. wrong_idx = list(np.where(judge == 0)[0])
  26. correct_idx = list(np.where(judge == 1)[0])
  27. axes[i].plot(noise, linestyle=':')
  28. axes[i].plot(noise, linestyle='None', marker='o', fillstyle='none',
  29. markersize=5, markevery=wrong_idx)
  30. axes[i].plot(noise, linestyle='None', marker='o', fillstyle='full',
  31. markersize=5, markevery=correct_idx)
  32. #
  33. axes[i].set_xlabel('trial')
  34. axes[i].set_ylim([-25, -5])
  35. axes[i].set_ylabel('- noise')
  36. axes[i].label_outer()
  37. plt.suptitle('Hue angle = ' + str(self.df.standard_stim.unique()))
  38. fig.tight_layout()
  39. def fit_noise_pf(self, hue_angle, guess=[-15, 5], bins='unique', lapse=0):
  40. trial_n = len(self.df)
  41. intens = self.df[self.df['standard_stim'] == hue_angle]['noise'].values.astype(float)
  42. reps = self.df[self.df['standard_stim'] == hue_angle]['judge'].values.astype(float)
  43. bin_intens, bin_reps, bin_N = data.functionFromStaircase(intens, reps, bins=bins)
  44. sems = [sum(bin_N) / n for n in bin_N]
  45. chance = 0.5
  46. fit = FitCumNormal(bin_intens, bin_reps, sems=sems,
  47. guess=guess, expectedMin=chance, lapse=lapse)
  48. fit_params = fit.params
  49. sigma_params = np.sqrt(np.diagonal(fit.covar))
  50. CumNormal = lambda xx, xShift, sd: (chance + (1 - chance - lapse) *
  51. ((special.erf((xx - xShift) / (np.sqrt(2) * sd)) + 1) * 0.5))
  52. xx = np.linspace(-25, 20, 200)
  53. yy_max = CumNormal(xx, *(fit_params + sigma_params))
  54. yy_min = CumNormal(xx, *(fit_params - sigma_params))
  55. thre_y = 0.76
  56. thre_x = fit.inverse(thre_y)
  57. fig, ax = plt.subplots(figsize=(8, 6))
  58. for i, r, s in zip(bin_intens, bin_reps, sems):
  59. ax.plot(i, r, '.', alpha=0.5, markersize=250 / s)
  60. smoothResp = np.linspace(0.0, 1.0, int(1.0 / .02))
  61. smoothInt = fit.inverse(smoothResp)
  62. ax.plot(smoothInt, smoothResp, '-')
  63. # plt.fill_between(xx, yy_max, yy_min, color='grey', alpha=0.3)
  64. ax.hlines(y=0.76, xmin=-30, xmax=thre_x, linestyles='dashed', colors='grey')
  65. ax.vlines(x=fit.inverse(0.76), ymin=0.5, ymax=0.76, linestyles='dashed', colors='grey')
  66. ssq = np.round(fit.ssq, decimals=3)
  67. ax.text(3.5, 0.55, 'ssq = ' + str(ssq), fontsize=10)
  68. ax.set_xlim([-35, 0])
  69. ax.set_ylim([0., 1.0])
  70. ax.set_xlabel('- std')
  71. ax.set_ylabel('correctness')
  72. plt.title(
  73. 'Hue angle =' + str(hue_angle) + ': threshold = ' + str(np.round(thre_x, 2)) + ', ' + str(trial_n) + ' trials')
  74. plt.show()
  75. return thre_x
  76. """example"""
  77. # df = LoadData('test', data_path='data/noise_test', sel_par=['noise_HH_135']).read_data()
  78. # thre_x = noise_fit_pf(df, hue_angle=135)