mr_utilities.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import numpy as np
  2. import functools
  3. import mrestimator as mre
  4. from scipy import stats
  5. def exclude_inconsistencies(mr_results):
  6. rejected_inconsistencies = ['H_nonzero', 'H_linear']
  7. for incon in rejected_inconsistencies:
  8. mr_results = mr_results[[incon not in mr_results[
  9. 'inconsistencies'].tolist()[i] for i in range(
  10. len(mr_results['inconsistencies'].tolist()))]]
  11. return mr_results
  12. def consistency_checks(activity, rk, fitres):
  13. """ Checks the consistency of MR estimates
  14. We only use H_nonzero and H_linear as exclusion criteria.
  15. """
  16. tau_max = 2000 # unusually large tau
  17. tau_min = 0
  18. inconsistencies = []
  19. if fitres.tau > tau_max or fitres.tau < tau_min:
  20. inconsistencies.append('H_tau')
  21. if count_nonzero(activity) < 1000:
  22. inconsistencies.append('H_nonzero')
  23. if check_linear(rk, fitres.rsquared):
  24. inconsistencies.append('H_linear')
  25. if not random_residuals(rk, fitres.fitfunc, *fitres.popt):
  26. inconsistencies.append('H_residual')
  27. if not random_residuals_alternative(rk, fitres.fitfunc, *fitres.popt):
  28. inconsistencies.append('H_residual_alt')
  29. if Ftest05(rk, fitres.rsquared, fitres.popt):
  30. inconsistencies.append('H_F05')
  31. if Ftest10(rk, fitres.rsquared, fitres.popt):
  32. inconsistencies.append('H_F10')
  33. return inconsistencies
  34. def Ftest05(rk, adjusted_rsquared, popt):
  35. n_obs = len(rk.coefficients)
  36. n_param = len(popt)
  37. r_squared = 1.0- (1.0 - adjusted_rsquared) * (n_obs-1-n_param)/(n_obs-1)
  38. F = (r_squared / (n_param-1)) / ((1-r_squared)/(n_obs-n_param))
  39. df1 = n_param-1 #n_obs
  40. df2 = n_obs-n_param
  41. p_value = 1 - stats.f.cdf(F, df1, df2)
  42. if p_value > 0.05:
  43. # complex model is not significanty better in explaining the data
  44. return True
  45. else:
  46. return False
  47. def Ftest10(rk, adjusted_rsquared, popt):
  48. n_obs = len(rk.coefficients)
  49. n_param = len(popt)
  50. r_squared = 1.0- (1.0 - adjusted_rsquared) * (n_obs-1-n_param)/(n_obs-1)
  51. F = (r_squared / (n_param-1)) / ((1-r_squared)/(n_obs-n_param))
  52. df1 = n_param-1 #n_obs
  53. df2 = n_obs-n_param
  54. p_value = 1 - stats.f.cdf(F, df1, df2)
  55. if p_value > 0.10:
  56. # complex model is not significanty better in explaining the data
  57. return True
  58. else:
  59. return False
  60. def check_linear(rk, rsquared):
  61. """ Check if linear function better fits the rk than the fit that was
  62. done (typically exponential offset). Done by comparing the rsquared of
  63. both fits, which are weighted with the number of parameters i the
  64. fitfunction.
  65. :return: bool
  66. True: linear fit better (Inconsistency!)
  67. False: linear fit worse
  68. """
  69. try:
  70. linear_fit = mre.fit(rk, fitfunc=mre.f_linear, description='linear')
  71. if linear_fit.rsquared is not None and rsquared is not None:
  72. if linear_fit.rsquared > rsquared:
  73. return True
  74. else:
  75. return False
  76. else:
  77. return True
  78. except RuntimeError:
  79. return False
  80. def random_residuals(rk, fitfunc, *args):
  81. """ Check if residuals (fit minus data) are consistent with noise. If
  82. this is not the case, i.e. if there are systematic trends in the
  83. residuals, the fit is inconsistent.
  84. """
  85. residuals = rk.coefficients - fitfunc(rk.steps*rk.dt, *args)
  86. k_win = 20
  87. for k_1 in range(len(rk.steps)-(k_win+1)):
  88. sign = []
  89. for k in range(k_1, k_1+k_win):
  90. if residuals[k] > 1*np.median(abs(residuals)):
  91. sign.append(1)
  92. elif residuals[k] < -1*np.median(abs(residuals)):
  93. sign.append(-1)
  94. else:
  95. sign.append(0)
  96. if len(set(sign)) <= 1 and sign[0] != 0:
  97. return False
  98. return True
  99. def random_residuals_alternative(rk, fitfunc, *args):
  100. """ Check if residuals (fit minus data) are consistent with noise. If
  101. this is not the case, i.e. if there are systematic trends in the
  102. residuals, the fit is inconsistent.
  103. """
  104. residuals = rk.coefficients - fitfunc(rk.steps*rk.dt, *args)
  105. k_win = 10
  106. k_range = 50
  107. sign = []
  108. for k in range(rk.steps[0], rk.steps[-k_win]):
  109. res_mean = np.mean(residuals[k:k+k_win])
  110. if res_mean > 1*np.median(abs(residuals)):
  111. sign.append(1)
  112. elif res_mean < -1*np.median(abs(residuals)):
  113. sign.append(-1)
  114. else:
  115. sign.append(0)
  116. for si in range(len(sign)-k_range):
  117. if len(set(sign[si:si+k_range])) <= 1 and sign[si] != 0:
  118. return False
  119. return True
  120. def list_preictrecordings(patient):
  121. listfile = '../Data/preIct_recordings_patients.txt'
  122. list = np.loadtxt(listfile, dtype=str)
  123. indices = np.where(list[:, 1] == str(patient))
  124. recordings = []
  125. for i in indices:
  126. recordings.append(list[i, 0])
  127. return recordings
  128. def count_nonzero(activity):
  129. activity_nonzero = 0
  130. for t in activity:
  131. if t != 0:
  132. activity_nonzero += 1
  133. return activity_nonzero
  134. def conjunction(*conditions):
  135. return functools.reduce(np.logical_and, conditions)
  136. def string_to_func(fitmethod):
  137. if fitmethod == 'exp':
  138. fitfunc = mre.f_exponential
  139. elif fitmethod == 'offset':
  140. fitfunc = mre.f_exponential_offset
  141. elif fitmethod == 'complex':
  142. fitfunc = mre.f_complex
  143. else:
  144. print(
  145. 'Error: fitmethod {} does not exist'.format(fitmethod))
  146. exit()
  147. return fitfunc
  148. def twosided_pvalue(value, diff_distribution):
  149. diff_distribution = np.array(diff_distribution)
  150. if value < 0:
  151. p_value = len(diff_distribution[diff_distribution <=
  152. value])/len(
  153. diff_distribution) + len(diff_distribution[diff_distribution >=
  154. (-1)*value])/len(
  155. diff_distribution)
  156. else:
  157. p_value = len(diff_distribution[diff_distribution >=
  158. value])/len(
  159. diff_distribution) + len(diff_distribution[diff_distribution <=
  160. (-1)*value])/len(
  161. diff_distribution)
  162. return p_value
  163. def bootstrap_distribution(sample, statistic=np.var, nboot=5000):
  164. sample_stat = []
  165. for bssample in range(nboot):
  166. sample_vals = np.random.choice(sample, size=len(sample),
  167. replace=True)
  168. sample_stat.append(statistic(sample_vals))
  169. return sample_stat
  170. def binning_focus(binning, patient):
  171. binning_focus = 'X'
  172. focifile = '../Data/patients.txt'
  173. f = open(focifile, 'r')
  174. foci = [line.rstrip('\n').split('\t') for line in f.readlines()]
  175. focus = 'NA'
  176. for i, idf in enumerate(foci[1:]):
  177. if int(idf[0]) == int(patient):
  178. focus = idf[1]
  179. if focus == 'NA':
  180. print('Error: patient {} not in foci list.'.format(patient))
  181. return 1
  182. if binning.startswith('left') and focus == 'L' or \
  183. binning.startswith('right') and \
  184. focus == 'R':
  185. binning_focus = 'focal'
  186. elif binning.startswith('left') and focus == 'R' or \
  187. binning.startswith('right') and \
  188. focus == 'L':
  189. binning_focus = 'contra-lateral'
  190. elif focus == 'B' and binning.startswith('left'):
  191. binning_focus = 'B_L'
  192. elif focus == 'B' and binning.startswith('right'):
  193. binning_focus = 'B_R'
  194. elif '?' in focus and binning.startswith('right'):
  195. binning_focus = 'U_R'
  196. elif '?' in focus and binning.startswith('left'):
  197. binning_focus = 'U_L'
  198. elif (binning.startswith('L') and focus == 'L') or (
  199. binning.startswith('R') and focus == 'R'):
  200. binning_focus = 'focal'
  201. elif (binning.startswith('L') and focus == 'R') or (
  202. binning.startswith('R') and focus == 'L'):
  203. binning_focus = 'contra-lateral'
  204. return binning_focus