OSCR

Locus- and Gene-Level Insights into the Inverse Association Between Alzheimer's Disease and Cancer.

Code ↔ Paper

1 match between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 1 match
  1. [1] § 4. Methods › 4.2. SNP-Based Heritability Analysis (Observed Scale) ↔ ldscore/regressions.py, lines 538 to 677 · score 0.61 · Score Regression, LD scores, obs, heritability, partition, variance · Report a mismatch

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 743 lines · 29 KB · GPL-3.0 · 1 match

  1. '''
  2. (c) 2014 Brendan Bulik-Sullivan and Hilary Finucane
  3. Estimators of heritability and genetic correlation.
  4. Shape convention is (n_snp, n_annot) for all classes.
  5. Last column = intercept.
  6. '''
  7. from __future__ import division
  8. import numpy as np
  9. import pandas as pd
  10. from scipy.stats import norm, chi2
  11. import jackknife as jk
  12. from irwls import IRWLS
  13. from scipy.stats import t as tdist
  14. from collections import namedtuple
  15. np.seterr(divide='raise', invalid='raise')
  16. s = lambda x: remove_brackets(str(np.matrix(x)))
  17. def update_separators(s, ii):
  18. '''s are separators with ii masked. Returns unmasked separators.'''
  19. maplist = np.arange(len(ii))[np.squeeze(ii)]
  20. mask_to_unmask = lambda i: maplist[i]
  21. t = np.apply_along_axis(mask_to_unmask, 0, s[1:-1])
  22. t = np.hstack(((0), t, (len(ii))))
  23. return t
  24. def p_z_norm(est, se):
  25. '''Convert estimate and se to Z-score and P-value.'''
  26. try:
  27. Z = est / se
  28. except (FloatingPointError, ZeroDivisionError):
  29. Z = float('inf')
  30. P = chi2.sf(Z ** 2, 1, loc=0, scale=1) # 0 if Z=inf
  31. return P, Z
  32. def remove_brackets(x):
  33. '''Get rid of brackets and trailing whitespace in numpy arrays.'''
  34. return x.replace('[', '').replace(']', '').strip()
  35. def append_intercept(x):
  36. '''
  37. Appends an intercept term to the design matrix for a linear regression.
  38. Parameters
  39. ----------
  40. x : np.matrix with shape (n_row, n_col)
  41. Design matrix. Columns are predictors; rows are observations.
  42. Returns
  43. -------
  44. x_new : np.matrix with shape (n_row, n_col+1)
  45. Design matrix with intercept term appended.
  46. '''
  47. n_row = x.shape[0]
  48. intercept = np.ones((n_row, 1))
  49. x_new = np.concatenate((x, intercept), axis=1)
  50. return x_new
  51. def remove_intercept(x):
  52. '''Removes the last column.'''
  53. n_col = x.shape[1]
  54. return x[:, 0:n_col - 1]
  55. def gencov_obs_to_liab(gencov_obs, P1, P2, K1, K2):
  56. '''
  57. Converts genetic covariance on the observed scale in an ascertained sample to genetic
  58. covariance on the liability scale in the population
  59. Parameters
  60. ----------
  61. gencov_obs : float
  62. Genetic covariance on the observed scale in an ascertained sample.
  63. P1, P2 : float in (0,1)
  64. Prevalences of phenotypes 1,2 in the sample.
  65. K1, K2 : float in (0,1)
  66. Prevalences of phenotypes 1,2 in the population.
  67. Returns
  68. -------
  69. gencov_liab : float
  70. Genetic covariance between liabilities in the population.
  71. Note: if a trait is a QT, set P = K = None.
  72. '''
  73. c1 = 1
  74. c2 = 1
  75. if P1 is not None and K1 is not None:
  76. c1 = np.sqrt(h2_obs_to_liab(1, P1, K1))
  77. if P2 is not None and K2 is not None:
  78. c2 = np.sqrt(h2_obs_to_liab(1, P2, K2))
  79. return gencov_obs * c1 * c2
  80. def h2_obs_to_liab(h2_obs, P, K):
  81. '''
  82. Converts heritability on the observed scale in an ascertained sample to heritability
  83. on the liability scale in the population.
  84. Parameters
  85. ----------
  86. h2_obs : float
  87. Heritability on the observed scale in an ascertained sample.
  88. P : float in (0,1)
  89. Prevalence of the phenotype in the sample.
  90. K : float in (0,1)
  91. Prevalence of the phenotype in the population.
  92. Returns
  93. -------
  94. h2_liab : float
  95. Heritability of liability in the population.
  96. '''
  97. if np.isnan(P) and np.isnan(K):
  98. return h2_obs
  99. if K <= 0 or K >= 1:
  100. raise ValueError('K must be in the range (0,1)')
  101. if P <= 0 or P >= 1:
  102. raise ValueError('P must be in the range (0,1)')
  103. thresh = norm.isf(K)
  104. conversion_factor = K ** 2 * \
  105. (1 - K) ** 2 / (P * (1 - P) * norm.pdf(thresh) ** 2)
  106. return h2_obs * conversion_factor
  107. class LD_Score_Regression(object):
  108. def __init__(self, y, x, w, N, M, n_blocks, intercept=None, slow=False, step1_ii=None, old_weights=False):
  109. for i in [y, x, w, M, N]:
  110. try:
  111. if len(i.shape) != 2:
  112. raise TypeError('Arguments must be 2D arrays.')
  113. except AttributeError:
  114. raise TypeError('Arguments must be arrays.')
  115. n_snp, self.n_annot = x.shape
  116. if any(i.shape != (n_snp, 1) for i in [y, w, N]):
  117. raise ValueError(
  118. 'N, weights and response (z1z2 or chisq) must have shape (n_snp, 1).')
  119. if M.shape != (1, self.n_annot):
  120. raise ValueError('M must have shape (1, n_annot).')
  121. M_tot = float(np.sum(M))
  122. x_tot = np.sum(x, axis=1).reshape((n_snp, 1))
  123. self.constrain_intercept = intercept is not None
  124. self.intercept = intercept
  125. self.n_blocks = n_blocks
  126. tot_agg = self.aggregate(y, x_tot, N, M_tot, intercept)
  127. initial_w = self._update_weights(
  128. x_tot, w, N, M_tot, tot_agg, intercept)
  129. Nbar = np.mean(N) # keep condition number low
  130. x = np.multiply(N, x) / Nbar
  131. if not self.constrain_intercept:
  132. x, x_tot = append_intercept(x), append_intercept(x_tot)
  133. yp = y
  134. else:
  135. yp = y - intercept
  136. self.intercept_se = 'NA'
  137. del y
  138. self.twostep_filtered = None
  139. if step1_ii is not None and self.constrain_intercept:
  140. raise ValueError(
  141. 'twostep is not compatible with constrain_intercept.')
  142. elif step1_ii is not None and self.n_annot > 1:
  143. raise ValueError(
  144. 'twostep not compatible with partitioned LD Score yet.')
  145. elif step1_ii is not None:
  146. n1 = np.sum(step1_ii)
  147. self.twostep_filtered = n_snp - n1
  148. x1 = x[np.squeeze(step1_ii), :]
  149. yp1, w1, N1, initial_w1 = map(
  150. lambda a: a[step1_ii].reshape((n1, 1)), (yp, w, N, initial_w))
  151. update_func1 = lambda a: self._update_func(
  152. a, x1, w1, N1, M_tot, Nbar, ii=step1_ii)
  153. step1_jknife = IRWLS(
  154. x1, yp1, update_func1, n_blocks, slow=slow, w=initial_w1)
  155. step1_int, _ = self._intercept(step1_jknife)
  156. yp = yp - step1_int
  157. x = remove_intercept(x)
  158. x_tot = remove_intercept(x_tot)
  159. update_func2 = lambda a: self._update_func(
  160. a, x_tot, w, N, M_tot, Nbar, step1_int)
  161. s = update_separators(step1_jknife.separators, step1_ii)
  162. step2_jknife = IRWLS(
  163. x, yp, update_func2, n_blocks, slow=slow, w=initial_w, separators=s)
  164. c = np.sum(np.multiply(initial_w, x)) / \
  165. np.sum(np.multiply(initial_w, np.square(x)))
  166. jknife = self._combine_twostep_jknives(
  167. step1_jknife, step2_jknife, M_tot, c, Nbar)
  168. elif old_weights:
  169. initial_w = np.sqrt(initial_w)
  170. x = IRWLS._weight(x, initial_w)
  171. y = IRWLS._weight(yp, initial_w)
  172. jknife = jk.LstsqJackknifeFast(x, y, n_blocks)
  173. else:
  174. update_func = lambda a: self._update_func(
  175. a, x_tot, w, N, M_tot, Nbar, intercept)
  176. jknife = IRWLS(
  177. x, yp, update_func, n_blocks, slow=slow, w=initial_w)
  178. self.coef, self.coef_cov, self.coef_se = self._coef(jknife, Nbar)
  179. self.cat, self.cat_cov, self.cat_se =\
  180. self._cat(jknife, M, Nbar, self.coef, self.coef_cov)
  181. self.tot, self.tot_cov, self.tot_se = self._tot(self.cat, self.cat_cov)
  182. self.prop, self.prop_cov, self.prop_se =\
  183. self._prop(jknife, M, Nbar, self.cat, self.tot)
  184. self.enrichment, self.M_prop = self._enrichment(
  185. M, M_tot, self.cat, self.tot)
  186. if not self.constrain_intercept:
  187. self.intercept, self.intercept_se = self._intercept(jknife)
  188. self.jknife = jknife
  189. self.tot_delete_values = self._delete_vals_tot(jknife, Nbar, M)
  190. self.part_delete_values = self._delete_vals_part(jknife, Nbar, M)
  191. if not self.constrain_intercept:
  192. self.intercept_delete_values = jknife.delete_values[
  193. :, self.n_annot]
  194. self.M = M
  195. @classmethod
  196. def aggregate(cls, y, x, N, M, intercept=None):
  197. if intercept is None:
  198. intercept = cls.__null_intercept__
  199. num = M * (np.mean(y) - intercept)
  200. denom = np.mean(np.multiply(x, N))
  201. return num / denom
  202. def _update_func(self, x, ref_ld_tot, w_ld, N, M, Nbar, intercept=None, ii=None):
  203. raise NotImplementedError
  204. def _delete_vals_tot(self, jknife, Nbar, M):
  205. '''Get delete values for total h2 or gencov.'''
  206. n_annot = self.n_annot
  207. tot_delete_vals = jknife.delete_values[
  208. :, 0:n_annot] # shape (n_blocks, n_annot)
  209. # shape (n_blocks, 1)
  210. tot_delete_vals = np.dot(tot_delete_vals, M.T) / Nbar
  211. return tot_delete_vals
  212. def _delete_vals_part(self, jknife, Nbar, M):
  213. '''Get delete values for partitioned h2 or gencov.'''
  214. n_annot = self.n_annot
  215. return jknife.delete_values[:, 0:n_annot] / Nbar
  216. def _coef(self, jknife, Nbar):
  217. '''Get coefficient estimates + cov from the jackknife.'''
  218. n_annot = self.n_annot
  219. coef = jknife.est[0, 0:n_annot] / Nbar
  220. coef_cov = jknife.jknife_cov[0:n_annot, 0:n_annot] / Nbar ** 2
  221. coef_se = np.sqrt(np.diag(coef_cov))
  222. return coef, coef_cov, coef_se
  223. def _cat(self, jknife, M, Nbar, coef, coef_cov):
  224. '''Convert coefficients to per-category h2 or gencov.'''
  225. cat = np.multiply(M, coef)
  226. cat_cov = np.multiply(np.dot(M.T, M), coef_cov)
  227. cat_se = np.sqrt(np.diag(cat_cov))
  228. return cat, cat_cov, cat_se
  229. def _tot(self, cat, cat_cov):
  230. '''Convert per-category h2 to total h2 or gencov.'''
  231. tot = np.sum(cat)
  232. tot_cov = np.sum(cat_cov)
  233. tot_se = np.sqrt(tot_cov)
  234. return tot, tot_cov, tot_se
  235. def _prop(self, jknife, M, Nbar, cat, tot):
  236. '''Convert total h2 and per-category h2 to per-category proportion h2 or gencov.'''
  237. n_annot = self.n_annot
  238. n_blocks = jknife.delete_values.shape[0]
  239. numer_delete_vals = np.multiply(
  240. M, jknife.delete_values[:, 0:n_annot]) / Nbar # (n_blocks, n_annot)
  241. denom_delete_vals = np.sum(
  242. numer_delete_vals, axis=1).reshape((n_blocks, 1))
  243. denom_delete_vals = np.dot(denom_delete_vals, np.ones((1, n_annot)))
  244. prop = jk.RatioJackknife(
  245. cat / tot, numer_delete_vals, denom_delete_vals)
  246. return prop.est, prop.jknife_cov, prop.jknife_se
  247. def _enrichment(self, M, M_tot, cat, tot):
  248. '''Compute proportion of SNPs per-category enrichment for h2 or gencov.'''
  249. M_prop = M / M_tot
  250. enrichment = np.divide(cat, M) / (tot / M_tot)
  251. return enrichment, M_prop
  252. def _intercept(self, jknife):
  253. '''Extract intercept and intercept SE from block jackknife.'''
  254. n_annot = self.n_annot
  255. intercept = jknife.est[0, n_annot]
  256. intercept_se = jknife.jknife_se[0, n_annot]
  257. return intercept, intercept_se
  258. def _combine_twostep_jknives(self, step1_jknife, step2_jknife, M_tot, c, Nbar=1):
  259. '''Combine free intercept and constrained intercept jackknives for --two-step.'''
  260. n_blocks, n_annot = step1_jknife.delete_values.shape
  261. n_annot -= 1
  262. if n_annot > 2:
  263. raise ValueError(
  264. 'twostep not yet implemented for partitioned LD Score.')
  265. step1_int, _ = self._intercept(step1_jknife)
  266. est = np.hstack(
  267. (step2_jknife.est, np.array(step1_int).reshape((1, 1))))
  268. delete_values = np.zeros((n_blocks, n_annot + 1))
  269. delete_values[:, n_annot] = step1_jknife.delete_values[:, n_annot]
  270. delete_values[:, 0:n_annot] = step2_jknife.delete_values -\
  271. c * (step1_jknife.delete_values[:, n_annot] -
  272. step1_int).reshape((n_blocks, n_annot)) # check this
  273. pseudovalues = jk.Jackknife.delete_values_to_pseudovalues(
  274. delete_values, est)
  275. jknife_est, jknife_var, jknife_se, jknife_cov = jk.Jackknife.jknife(
  276. pseudovalues)
  277. jknife = namedtuple('jknife',
  278. ['est', 'jknife_se', 'jknife_est', 'jknife_var', 'jknife_cov', 'delete_values'])
  279. return jknife(est, jknife_se, jknife_est, jknife_var, jknife_cov, delete_values)
  280. class Hsq(LD_Score_Regression):
  281. __null_intercept__ = 1
  282. def __init__(self, y, x, w, N, M, n_blocks=200, intercept=None, slow=False, twostep=None, old_weights=False):
  283. step1_ii = None
  284. if twostep is not None:
  285. step1_ii = y < twostep
  286. LD_Score_Regression.__init__(self, y, x, w, N, M, n_blocks, intercept=intercept,
  287. slow=slow, step1_ii=step1_ii, old_weights=old_weights)
  288. self.mean_chisq, self.lambda_gc = self._summarize_chisq(y)
  289. if not self.constrain_intercept:
  290. self.ratio, self.ratio_se = self._ratio(
  291. self.intercept, self.intercept_se, self.mean_chisq)
  292. def _update_func(self, x, ref_ld_tot, w_ld, N, M, Nbar, intercept=None, ii=None):
  293. '''
  294. Update function for IRWLS
  295. x is the output of np.linalg.lstsq.
  296. x[0] is the regression coefficients
  297. x[0].shape is (# of dimensions, 1)
  298. the last element of x[0] is the intercept.
  299. intercept is None --> free intercept
  300. intercept is not None --> constrained intercept
  301. '''
  302. hsq = M * x[0][0] / Nbar
  303. if intercept is None:
  304. intercept = max(x[0][1]) # divide by zero error if intercept < 0
  305. else:
  306. if ref_ld_tot.shape[1] > 1:
  307. raise ValueError(
  308. 'Design matrix has intercept column for constrained intercept regression!')
  309. ld = ref_ld_tot[:, 0].reshape(w_ld.shape) # remove intercept
  310. w = self.weights(ld, w_ld, N, M, hsq, intercept, ii)
  311. return w
  312. def _summarize_chisq(self, chisq):
  313. '''Compute mean chi^2 and lambda_GC.'''
  314. mean_chisq = np.mean(chisq)
  315. # median and matrix don't play nice
  316. lambda_gc = np.median(np.asarray(chisq)) / 0.4549
  317. return mean_chisq, lambda_gc
  318. def _ratio(self, intercept, intercept_se, mean_chisq):
  319. '''Compute ratio (intercept - 1) / (mean chi^2 -1 ).'''
  320. if mean_chisq > 1:
  321. ratio_se = intercept_se / (mean_chisq - 1)
  322. ratio = (intercept - 1) / (mean_chisq - 1)
  323. else:
  324. ratio = 'NA'
  325. ratio_se = 'NA'
  326. return ratio, ratio_se
  327. def _overlap_output(self, category_names, overlap_matrix, M_annot, M_tot, print_coefficients):
  328. '''LD Score regression summary for overlapping categories.'''
  329. overlap_matrix_prop = np.zeros([self.n_annot,self.n_annot])
  330. for i in range(self.n_annot):
  331. overlap_matrix_prop[i, :] = overlap_matrix[i, :] / M_annot
  332. prop_hsq_overlap = np.dot(
  333. overlap_matrix_prop, self.prop.T).reshape((1, self.n_annot))
  334. prop_hsq_overlap_var = np.diag(
  335. np.dot(np.dot(overlap_matrix_prop, self.prop_cov), overlap_matrix_prop.T))
  336. prop_hsq_overlap_se = np.sqrt(
  337. np.maximum(0, prop_hsq_overlap_var)).reshape((1, self.n_annot))
  338. one_d_convert = lambda x: np.array(x).reshape(np.prod(x.shape))
  339. prop_M_overlap = M_annot / M_tot
  340. enrichment = prop_hsq_overlap / prop_M_overlap
  341. enrichment_se = prop_hsq_overlap_se / prop_M_overlap
  342. overlap_matrix_diff = np.zeros([self.n_annot,self.n_annot])
  343. for i in range(self.n_annot):
  344. if not M_tot == M_annot[0,i]:
  345. overlap_matrix_diff[i, :] = overlap_matrix[i,:]/M_annot[0,i] - \
  346. (M_annot - overlap_matrix[i,:]) / (M_tot-M_annot[0,i])
  347. diff_est = np.dot(overlap_matrix_diff,self.coef)
  348. diff_cov = np.dot(np.dot(overlap_matrix_diff,self.coef_cov),overlap_matrix_diff.T)
  349. diff_se = np.sqrt(np.diag(diff_cov))
  350. diff_p = ['NA' if diff_se[i]==0 else 2*tdist.sf(abs(diff_est[i]/diff_se[i]),self.n_blocks) \
  351. for i in range(self.n_annot)]
  352. df = pd.DataFrame({
  353. 'Category': category_names,
  354. 'Prop._SNPs': one_d_convert(prop_M_overlap),
  355. 'Prop._h2': one_d_convert(prop_hsq_overlap),
  356. 'Prop._h2_std_error': one_d_convert(prop_hsq_overlap_se),
  357. 'Enrichment': one_d_convert(enrichment),
  358. 'Enrichment_std_error': one_d_convert(enrichment_se),
  359. 'Enrichment_p':diff_p,
  360. 'Coefficient': one_d_convert(self.coef),
  361. 'Coefficient_std_error': self.coef_se,
  362. 'Coefficient_z-score': one_d_convert(self.coef) / one_d_convert(self.coef_se)
  363. })
  364. if print_coefficients:
  365. df = df[['Category', 'Prop._SNPs', 'Prop._h2', 'Prop._h2_std_error',
  366. 'Enrichment','Enrichment_std_error', 'Enrichment_p',
  367. 'Coefficient', 'Coefficient_std_error','Coefficient_z-score']]
  368. else:
  369. df = df[['Category', 'Prop._SNPs', 'Prop._h2', 'Prop._h2_std_error',
  370. 'Enrichment','Enrichment_std_error', 'Enrichment_p']]
  371. return df
  372. def summary(self, ref_ld_colnames=None, P=None, K=None, overlap=False):
  373. '''Print summary of the LD Score Regression.'''
  374. if P is not None and K is not None:
  375. T = 'Liability'
  376. c = h2_obs_to_liab(1, P, K)
  377. else:
  378. T = 'Observed'
  379. c = 1
  380. out = ['Total ' + T + ' scale h2: ' +
  381. s(c * self.tot) + ' (' + s(c * self.tot_se) + ')']
  382. if self.n_annot > 1:
  383. if ref_ld_colnames is None:
  384. ref_ld_colnames = ['CAT_' + str(i)
  385. for i in xrange(self.n_annot)]
  386. out.append('Categories: ' + ' '.join(ref_ld_colnames))
  387. if not overlap:
  388. out.append(T + ' scale h2: ' + s(c * self.cat))
  389. out.append(T + ' scale h2 SE: ' + s(c * self.cat_se))
  390. out.append('Proportion of SNPs: ' + s(self.M_prop))
  391. out.append('Proportion of h2g: ' + s(self.prop))
  392. out.append('Enrichment: ' + s(self.enrichment))
  393. out.append('Coefficients: ' + s(self.coef))
  394. out.append('Coefficient SE: ' + s(self.coef_se))
  395. out.append('Lambda GC: ' + s(self.lambda_gc))
  396. out.append('Mean Chi^2: ' + s(self.mean_chisq))
  397. if self.constrain_intercept:
  398. out.append(
  399. 'Intercept: constrained to {C}'.format(C=s(self.intercept)))
  400. else:
  401. out.append(
  402. 'Intercept: ' + s(self.intercept) + ' (' + s(self.intercept_se) + ')')
  403. if self.mean_chisq > 1:
  404. if self.ratio < 0:
  405. out.append(
  406. 'Ratio < 0 (usually indicates GC correction).')
  407. else:
  408. out.append(
  409. 'Ratio: ' + s(self.ratio) + ' (' + s(self.ratio_se) + ')')
  410. else:
  411. out.append('Ratio: NA (mean chi^2 < 1)')
  412. return remove_brackets('\n'.join(out))
  413. def _update_weights(self, ld, w_ld, N, M, hsq, intercept, ii=None):
  414. if intercept is None:
  415. intercept = self.__null_intercept__
  416. return self.weights(ld, w_ld, N, M, hsq, intercept, ii)
  417. @classmethod
  418. def weights(cls, ld, w_ld, N, M, hsq, intercept=None, ii=None):
  419. '''
  420. Regression weights.
  421. Parameters
  422. ----------
  423. ld : np.matrix with shape (n_snp, 1)
  424. LD Scores (non-partitioned).
  425. w_ld : np.matrix with shape (n_snp, 1)
  426. LD Scores (non-partitioned) computed with sum r^2 taken over only those SNPs included
  427. in the regression.
  428. N : np.matrix of ints > 0 with shape (n_snp, 1)
  429. Number of individuals sampled for each SNP.
  430. M : float > 0
  431. Number of SNPs used for estimating LD Score (need not equal number of SNPs included in
  432. the regression).
  433. hsq : float in [0,1]
  434. Heritability estimate.
  435. Returns
  436. -------
  437. w : np.matrix with shape (n_snp, 1)
  438. Regression weights. Approx equal to reciprocal of conditional variance function.
  439. '''
  440. M = float(M)
  441. if intercept is None:
  442. intercept = 1
  443. hsq = max(hsq, 0.0)
  444. hsq = min(hsq, 1.0)
  445. ld = np.fmax(ld, 1.0)
  446. w_ld = np.fmax(w_ld, 1.0)
  447. c = hsq * N / M
  448. het_w = 1.0 / (2 * np.square(intercept + np.multiply(c, ld)))
  449. oc_w = 1.0 / w_ld
  450. w = np.multiply(het_w, oc_w)
  451. return w
  452. class Gencov(LD_Score_Regression):
  453. __null_intercept__ = 0
  454. def __init__(self, z1, z2, x, w, N1, N2, M, hsq1, hsq2, intercept_hsq1, intercept_hsq2,
  455. n_blocks=200, intercept_gencov=None, slow=False, twostep=None):
  456. self.intercept_hsq1 = intercept_hsq1
  457. self.intercept_hsq2 = intercept_hsq2
  458. self.hsq1 = hsq1
  459. self.hsq2 = hsq2
  460. self.N1 = N1
  461. self.N2 = N2
  462. y = z1 * z2
  463. step1_ii = None
  464. if twostep is not None:
  465. step1_ii = np.logical_and(z1**2 < twostep, z2**2 < twostep)
  466. LD_Score_Regression.__init__(self, y, x, w, np.sqrt(N1 * N2), M, n_blocks,
  467. intercept=intercept_gencov, slow=slow, step1_ii=step1_ii)
  468. self.p, self.z = p_z_norm(self.tot, self.tot_se)
  469. self.mean_z1z2 = np.mean(np.multiply(z1, z2))
  470. def summary(self, ref_ld_colnames, P=None, K=None):
  471. '''Print summary of the LD Score regression.'''
  472. out = []
  473. if P is not None and K is not None and\
  474. all((i is not None for i in P)) and all((i is not None for i in K)):
  475. T = 'Liability'
  476. c = gencov_obs_to_liab(1, P[0], P[1], K[0], K[1])
  477. else:
  478. T = 'Observed'
  479. c = 1
  480. out.append('Total ' + T + ' scale gencov: ' +
  481. s(c * self.tot) + ' (' + s(c * self.tot_se) + ')')
  482. if self.n_annot > 1:
  483. out.append('Categories: ' + str(' '.join(ref_ld_colnames)))
  484. out.append(T + ' scale gencov: ' + s(c * self.cat))
  485. out.append(T + ' scale gencov SE: ' + s(c * self.cat_se))
  486. out.append('Proportion of SNPs: ' + s(self.M_prop))
  487. out.append('Proportion of gencov: ' + s(self.prop))
  488. out.append('Enrichment: ' + s(self.enrichment))
  489. out.append('Mean z1*z2: ' + s(self.mean_z1z2))
  490. if self.constrain_intercept:
  491. out.append(
  492. 'Intercept: constrained to {C}'.format(C=s(self.intercept)))
  493. else:
  494. out.append(
  495. 'Intercept: ' + s(self.intercept) + ' (' + s(self.intercept_se) + ')')
  496. return remove_brackets('\n'.join(out))
  497. def _update_func(self, x, ref_ld_tot, w_ld, N, M, Nbar, intercept=None, ii=None):
  498. '''
  499. Update function for IRWLS
  500. x is the output of np.linalg.lstsq.
  501. x[0] is the regression coefficients
  502. x[0].shape is (# of dimensions, 1)
  503. the last element of x[0] is the intercept.
  504. '''
  505. rho_g = M * x[0][0] / Nbar
  506. if intercept is None: # if the regression includes an intercept
  507. intercept = x[0][1]
  508. # remove intercept if we have one
  509. ld = ref_ld_tot[:, 0].reshape(w_ld.shape)
  510. if ii is not None:
  511. N1 = self.N1[ii].reshape((w_ld.shape))
  512. N2 = self.N2[ii].reshape((w_ld.shape))
  513. else:
  514. N1 = self.N1
  515. N2 = self.N2
  516. return self.weights(ld, w_ld, N1, N2, np.sum(M), self.hsq1, self.hsq2, rho_g,
  517. intercept, self.intercept_hsq1, self.intercept_hsq2, ii)
  518. def _update_weights(self, ld, w_ld, sqrt_n1n2, M, rho_g, intercept, ii=None):
  519. '''Weight function with the same signature for Hsq and Gencov.'''
  520. w = self.weights(ld, w_ld, self.N1, self.N2, M, self.hsq1, self.hsq2, rho_g,
  521. intercept, self.intercept_hsq1, self.intercept_hsq2)
  522. return w
  523. @classmethod
  524. def weights(cls, ld, w_ld, N1, N2, M, h1, h2, rho_g, intercept_gencov=None,
  525. intercept_hsq1=None, intercept_hsq2=None, ii=None):
  526. '''
  527. Regression weights.
  528. Parameters
  529. ----------
  530. ld : np.matrix with shape (n_snp, 1)
  531. LD Scores (non-partitioned)
  532. w_ld : np.matrix with shape (n_snp, 1)
  533. LD Scores (non-partitioned) computed with sum r^2 taken over only those SNPs included
  534. in the regression.
  535. M : float > 0
  536. Number of SNPs used for estimating LD Score (need not equal number of SNPs included in
  537. the regression).
  538. N1, N2 : np.matrix of ints > 0 with shape (n_snp, 1)
  539. Number of individuals sampled for each SNP for each study.
  540. h1, h2 : float in [0,1]
  541. Heritability estimates for each study.
  542. rhog : float in [0,1]
  543. Genetic covariance estimate.
  544. intercept : float
  545. Genetic covariance intercept, on the z1*z2 scale (so should be Ns*rho/sqrt(N1*N2)).
  546. Returns
  547. -------
  548. w : np.matrix with shape (n_snp, 1)
  549. Regression weights. Approx equal to reciprocal of conditional variance function.
  550. '''
  551. M = float(M)
  552. if intercept_gencov is None:
  553. intercept_gencov = 0
  554. if intercept_hsq1 is None:
  555. intercept_hsq1 = 1
  556. if intercept_hsq2 is None:
  557. intercept_hsq2 = 1
  558. h1, h2 = max(h1, 0.0), max(h2, 0.0)
  559. h1, h2 = min(h1, 1.0), min(h2, 1.0)
  560. rho_g = min(rho_g, 1.0)
  561. rho_g = max(rho_g, -1.0)
  562. ld = np.fmax(ld, 1.0)
  563. w_ld = np.fmax(w_ld, 1.0)
  564. a = np.multiply(N1, h1 * ld) / M + intercept_hsq1
  565. b = np.multiply(N2, h2 * ld) / M + intercept_hsq2
  566. sqrt_n1n2 = np.sqrt(np.multiply(N1, N2))
  567. c = np.multiply(sqrt_n1n2, rho_g * ld) / M + intercept_gencov
  568. try:
  569. het_w = 1.0 / (np.multiply(a, b) + np.square(c))
  570. except FloatingPointError: # bizarre error; should never happen
  571. raise FloatingPointError('Why did you set hsq intercept <= 0?')
  572. oc_w = 1.0 / w_ld
  573. w = np.multiply(het_w, oc_w)
  574. return w
  575. class RG(object):
  576. def __init__(self, z1, z2, x, w, N1, N2, M, intercept_hsq1=None, intercept_hsq2=None,
  577. intercept_gencov=None, n_blocks=200, slow=False, twostep=None):
  578. self.intercept_gencov = intercept_gencov
  579. self._negative_hsq = None
  580. n_snp, n_annot = x.shape
  581. hsq1 = Hsq(np.square(z1), x, w, N1, M, n_blocks=n_blocks, intercept=intercept_hsq1,
  582. slow=slow, twostep=twostep)
  583. hsq2 = Hsq(np.square(z2), x, w, N2, M, n_blocks=n_blocks, intercept=intercept_hsq2,
  584. slow=slow, twostep=twostep)
  585. gencov = Gencov(z1, z2, x, w, N1, N2, M, hsq1.tot, hsq2.tot, hsq1.intercept,
  586. hsq2.intercept, n_blocks, intercept_gencov=intercept_gencov, slow=slow,
  587. twostep=twostep)
  588. gencov.N1 = None # save memory
  589. gencov.N2 = None
  590. self.hsq1, self.hsq2, self.gencov = hsq1, hsq2, gencov
  591. if (hsq1.tot <= 0 or hsq2.tot <= 0):
  592. self._negative_hsq = True
  593. self.rg_ratio = self.rg = self.rg_se = 'NA'
  594. self.p = self.z = 'NA'
  595. else:
  596. rg_ratio = np.array(
  597. gencov.tot / np.sqrt(hsq1.tot * hsq2.tot)).reshape((1, 1))
  598. denom_delete_values = np.sqrt(
  599. np.multiply(hsq1.tot_delete_values, hsq2.tot_delete_values))
  600. rg = jk.RatioJackknife(
  601. rg_ratio, gencov.tot_delete_values, denom_delete_values)
  602. self.rg_jknife = float(rg.jknife_est)
  603. self.rg_se = float(rg.jknife_se)
  604. self.rg_ratio = float(rg_ratio)
  605. self.p, self.z = p_z_norm(self.rg_ratio, self.rg_se)
  606. def summary(self, silly=False):
  607. '''Print output of Gencor object.'''
  608. out = []
  609. if self._negative_hsq:
  610. out.append('Genetic Correlation: nan (nan) (h2 out of bounds) ')
  611. out.append('Z-score: nan (nan) (h2 out of bounds)')
  612. out.append('P: nan (nan) (h2 out of bounds)')
  613. out.append('WARNING: One of the h2\'s was out of bounds.')
  614. out.append(
  615. 'This usually indicates a data-munging error ' +
  616. 'or that h2 or N is low.')
  617. elif (self.rg_ratio > 1.2 or self.rg_ratio < -1.2) and not silly:
  618. out.append('Genetic Correlation: nan (nan) (rg out of bounds) ')
  619. out.append('Z-score: nan (nan) (rg out of bounds)')
  620. out.append('P: nan (nan) (rg out of bounds)')
  621. out.append('WARNING: rg was out of bounds.')
  622. if self.intercept_gencov is None:
  623. out.append(
  624. 'This often means that h2 is not significantly ' +
  625. 'different from zero.')
  626. else:
  627. out.append(
  628. 'This often means that you have constrained' +
  629. ' the intercepts to the wrong values.')
  630. else:
  631. out.append(
  632. 'Genetic Correlation: ' + s(self.rg_ratio) +
  633. ' (' + s(self.rg_se) + ')')
  634. out.append('Z-score: ' + s(self.z))
  635. out.append('P: ' + s(self.p))
  636. return remove_brackets('\n'.join(out))

regressions.py at commit 2fdeeb3, under GPL-3.0 · at the source

Overview

Authors: Dipti Debnath1,2, Mohammad Housini2, Sanjeev Sariya3, Nicole R. Phillips1,2, Gita A. Pathak3,4, Robert C. Barber2
  1. Department of Microbiology, Immunology & Genetics, University of North Texas Health Science Center, 3500 Camp Bowie Blvd, Fort Worth, TX 76107, USA
  2. Institute for Translational Research, University of North Texas Health Science Center, 3500 Camp Bowie Blvd, Fort Worth, TX 76107, USA
  3. Institute for Genomic Health, Icahn School of Medicine at Mount Sinai, 1 Gustave L. Levy Pl, New York, NY 10029, USA
  4. Department of Genetics and Genomic Sciences, Icahn School of Medicine at Mount Sinai, 1 Gustave L. Levy Pl, New York, NY 10029, USA
Journal: International journal of molecular sciences, volume 27, issue 6, article 2900
Dates: received 28 January 2026; accepted 17 March 2026; published online 23 March 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.3390/ijms27062900 · PMID 41898760 · PMCID PMC13026669 · OpenAlex W7140146012
Open access: gold, a free copy (OpenAlex)
Status: code verified (how the code is found and verified)
Categories: genetics / omics (modality), human (organism), other condition (population), Alzheimer's / dementia (population), cellular / molecular (subfield)
Methods: Statistics
Keywords: apoptosis, APOE, local genetic correlation, genetically regulated gene expression, inverse comorbidity
MeSH: Alzheimer Disease*, Genetic Predisposition to Disease*, Neoplasms*, Gene Expression Regulation, Neoplastic, Genome-Wide Association Study, Humans, Polymorphism, Single Nucleotide (* major topic)
Topic: Cancer-related cognitive impairment studies (Oncology, Medicine), according to OpenAlex
Citations: cited by 2 papers (Europe PMC); 124 references in the paper

Abstract

Alzheimer’s disease (AD) and cancer are both age-related conditions, yet numerous large-scale epidemiological studies have consistently documented an inverse association, with individuals diagnosed with cancer exhibiting a reduced risk of AD and vice versa. Although this relationship has been replicated across diverse populations, its biological basis remains poorly understood. To address this gap, the present study applies a framework that integrates locus-level genetic correlation (rg) with genetically regulated gene expression to clarify the molecular factors contributing to the inverse epidemiological patterns observed between the two diseases. We used the largest available genome-wide association studies (GWAS) (Nmax = 448,150) to quantify local genetic correlations between AD and several age-associated cancers, including breast, prostate, lung, colorectal, melanoma, basal cell carcinoma, bladder, and endometrial cancer. Eight genomic regions showed significant negative local rg, at the 19q13.31–19q13.32 locus demonstrating strong negative correlations across multiple cancers, including breast, prostate, lung, melanoma, and endometrial cancer. To evaluate the contribution of genetically regulated gene expression, we conducted transcriptome-wide association studies (TWAS) using precomputed gene expression weights from cancer tissues (The Cancer Genome Atlas-TCGA), disease-agnostic tissues (Genotype-Tissue Expression-GTEx), and brain tissue (dorsolateral prefrontal cortex-DLPFC). For each AD–cancer pair, we prioritized genes that were nominally significant in both traits (p < 0.05) and exhibited inverse TWAS Z scores. This analysis identified 24 genes with opposite effect directions between AD and at least three cancer types. TWAS signals also aligned with local rg findings at the 19q13.31–19q13.32 region, suggesting that regulatory variation near this locus contributes to shared but opposing genetic effects beyond the canonical APOE signal. Across cancer types, genes inversely associated with AD converged on pathways involved in cell cycle regulation, apoptosis, DNA-damage response, and metabolic processes. These results support the hypothesis that biological mechanisms promoting proliferation and survival in cancer may oppose those contributing to neurodegeneration in AD.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 1 match between paragraphs and lines of code.

bulik/ldsc

License: GPL-3.0
State: the link answers, verified on 26 October 2026
Evidence: files inventoried
Commit: 2fdeeb3b44379408794154993dbd6101b8946b7e, 16 January 2026
Languages: Python (19), MATLAB (4), Perl (1), R (1)
Size: 1,093 files, 25 scripts
Software Heritage: archived
Found in: the text, “4.3. Global Genetic Correlation Analysis”
Holds: README, license file, environment (environment.yml, requirements.txt, setup.py), tests
Not found: CITATION.cff, continuous integration, documentation
Tools: NumPy (18 files), pandas (11 files), SciPy (5 files), BEDTools (2 files)
Availability: 2 checks, the latest on 26 October 2026: the link answers
  • 26 October 2026: the link answers
  • 26 September 2026: the link answers
27 files
At the source: github.com/bulik/ldsc

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 25 scripts, each with its path and the digest of its content;
  • 1 match between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data Availability Statement

The original contributions presented in this study are included in the article/Supplementary Materials. Further inquiries can be directed to the corresponding authors.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 30 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 6 authors, 5 keywords, 7 MeSH terms, 123 references.

Cite

This paper

Debnath, D., Housini, M., Sariya, S., Phillips, N. R., Pathak, G. A., & Barber, R. C. (2026). Locus- and Gene-Level Insights into the Inverse Association Between Alzheimer's Disease and Cancer. International journal of molecular sciences, 27(6), 2900. https://doi.org/10.3390/ijms27062900

BibTeX

@article{debnath2026locus,
author = {Debnath, Dipti and Housini, Mohammad and Sariya, Sanjeev and Phillips, Nicole R. and Pathak, Gita A. and Barber, Robert C.},
title = {{Locus- and Gene-Level Insights into the Inverse Association Between Alzheimer's Disease and Cancer}},
journal = {International journal of molecular sciences},
year = {2026},
month = mar,
volume = {27},
number = {6},
pages = {2900},
publisher = {Multidisciplinary Digital Publishing Institute (MDPI)},
issn = {1422-0067},
doi = {10.3390/ijms27062900},
url = {https://doi.org/10.3390/ijms27062900},
pmid = {41898760},
pmcid = {PMC13026669}
}

RIS

TY - JOUR
AU - Debnath, Dipti
AU - Housini, Mohammad
AU - Sariya, Sanjeev
AU - Phillips, Nicole R.
AU - Pathak, Gita A.
AU - Barber, Robert C.
TI - Locus- and Gene-Level Insights into the Inverse Association Between Alzheimer's Disease and Cancer
T2 - International journal of molecular sciences
J2 - Int J Mol Sci
PY - 2026
DA - 2026/03/23
VL - 27
IS - 6
SP - 2900
SN - 1422-0067
PB - Multidisciplinary Digital Publishing Institute (MDPI)
DO - 10.3390/ijms27062900
UR - https://doi.org/10.3390/ijms27062900
LA - en
ER -

CSL-JSON

{
"id": "10.3390/ijms27062900",
"type": "article-journal",
"title": "Locus- and Gene-Level Insights into the Inverse Association Between Alzheimer's Disease and Cancer",
"container-title": "International journal of molecular sciences",
"author": [
{
"family": "Debnath",
"given": "Dipti"
},
{
"family": "Housini",
"given": "Mohammad"
},
{
"family": "Sariya",
"given": "Sanjeev"
},
{
"family": "Phillips",
"given": "Nicole R."
},
{
"family": "Pathak",
"given": "Gita A."
},
{
"family": "Barber",
"given": "Robert C."
}
],
"container-title-short": "Int J Mol Sci",
"volume": "27",
"issue": "6",
"page": "2900",
"DOI": "10.3390/ijms27062900",
"PMID": "41898760",
"PMCID": "PMC13026669",
"ISSN": "1422-0067",
"publisher": "Multidisciplinary Digital Publishing Institute (MDPI)",
"URL": "https://doi.org/10.3390/ijms27062900",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
23
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1371/journal.pcbi.1012356 [code]
Regression convolutional neural network models implicate peripheral immune regulatory variants in the predisposition to Alzheimer's disease.
Journal: PLoS computational biology
In common: BEDTools, pandas, SciPy, 1 other tool, Alzheimer's / dementia, 8 references
[2] doi:10.3389/fnmol.2022.948456 [code]
Cell type-specific histone acetylation profiling of Alzheimer's disease subjects and integration with genetics.
Journal: Frontiers in molecular neuroscience
In common: pandas, SciPy, NumPy, Alzheimer's / dementia, genetics / omics, cellular / molecular, 9 references
[3] doi:10.1016/j.isci.2023.107958 [code]
Identification of region-specific splicing QTLs in human hippocampal tissue and its distinctive role in brain disorders.
Journal: iScience
In common: BEDTools, pandas, SciPy, 1 other tool, genetics / omics, cellular / molecular, 7 references
[4] doi:10.1038/s42003-026-10030-4 [code]
Cell-type-aware transcriptome-wide association studies identify 91 independent risk genes for Alzheimer's disease dementia.
Journal: Communications biology
In common: Alzheimer's / dementia, genetics / omics, cellular / molecular, 9 references
[5] doi:10.1186/s13073-023-01248-6 [code]
DeepGAMI: deep biologically guided auxiliary learning for multimodal integration and imputation to improve genotype-phenotype prediction.
Journal: Genome medicine
In common: pandas, SciPy, NumPy, Alzheimer's / dementia, genetics / omics, cellular / molecular, 7 references
[6] doi:10.3390/ijms24021428 [code]
Molecular Landscape of Tourette's Disorder.
Journal: International journal of molecular sciences
In common: BEDTools, pandas, SciPy, 1 other tool, other condition, cellular / molecular, 6 references
[7] doi:10.1038/s41562-024-02051-y [code]
The shared genetic architecture and evolution of human language and musical rhythm.
Journal: Nature human behaviour
In common: BEDTools, pandas, SciPy, 1 other tool, genetics / omics, other condition, 6 references
[8] doi:10.1038/s41588-025-02189-z [code]
Genome-wide analyses identify 30 loci associated with obsessive-compulsive disorder.
Journal: Nature genetics
In common: BEDTools, pandas, SciPy, 1 other tool, genetics / omics, other condition, cellular / molecular, 6 references
[9] doi:10.1038/s41467-026-75193-4 [code]
Multi-ancestry gene expression models amplify transcriptome-wide association study discovery and validation.
Journal: Nature communications
In common: pandas, SciPy, NumPy, genetics / omics, cellular / molecular, 7 references
[10] doi:10.1038/s41467-024-52025-x [code]
Genetic, transcriptomic, histological, and biochemical analysis of progressive supranuclear palsy implicates glial activation and novel risk genes.
Journal: Nature communications
In common: pandas, NumPy, Alzheimer's / dementia, genetics / omics, cellular / molecular, 8 references

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.3390/ijms27062900.svg)](https://openscicode.org/badge/doi/10.3390/ijms27062900)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.3390/ijms27062900.svg
   :target: https://openscicode.org/badge/doi/10.3390/ijms27062900
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.3390/ijms27062900"><img src="https://openscicode.org/badge/doi/10.3390/ijms27062900.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.