autodmri.gamma
1from __future__ import annotations # Needed for | annotations on python 3.9 2 3import numpy as np 4import numpy.typing as npt 5 6from scipy.special import digamma, polygamma 7 8 9def get_noise_distribution(data, method='moments'): 10 '''Computes sigma and N from an array of gamma distributed data 11 12 input 13 ----- 14 data 15 A numpy array of gamma distributed values 16 17 method='moments' or method='maxlk' 18 Use either the moments or maximum likelihood equations to estimate the parameters. 19 20 output 21 ------ 22 sigma, N 23 parameters related to the original Gaussian noise distribution 24 ''' 25 26 data = data[data > 0] 27 28 # If we have no voxel or only the same value 29 # it leads to a divide by 0 as an edge case 30 if data.size == 0 or np.std(data) == 0: 31 return 0, 0 32 33 # First get sigma 34 if method == 'moments': 35 mdata2 = np.mean(data**2) 36 mdata4 = np.mean(data**4) 37 sigma = np.sqrt(mdata4 / mdata2 - mdata2) / np.sqrt(2) 38 elif method == 'maxlk': 39 sigma = maxlk_sigma(data) 40 else: 41 raise ValueError(f'Invalid method name {method}') 42 43 t = 1/2 * (data / sigma)**2 44 45 # Now compute N 46 if method == 'moments': 47 N = np.mean(t) 48 elif method == 'maxlk': 49 y = np.mean(np.log(t)) 50 N = inv_digamma(y) 51 else: 52 raise ValueError(f'Invalid method name {method}') 53 54 return sigma, N 55 56 57def maxlk_sigma(m: npt.NDArray, xold: float | None = None, eps: float=1e-8, max_iter: int=100): 58 '''Maximum likelihood equation to estimate sigma from gamma distributed values''' 59 60 mean_m2 = np.mean(m**2) 61 mean_log_m2 = 2 * np.mean(np.log(m)) 62 63 def f(sigma): 64 return digamma(mean_m2 / (2*sigma**2)) - mean_log_m2 + np.log(2*sigma**2) 65 66 def fprime(sigma): 67 return -mean_m2 * polygamma(1, mean_m2 / (2*sigma**2)) / sigma**3 + 2/sigma 68 69 if xold is None: 70 xold = m.std() 71 72 for _ in range(max_iter): 73 74 xnew = xold - f(xold) / fprime(xold) 75 76 if np.abs(xold - xnew) < eps: 77 break 78 79 xold = xnew 80 81 return xnew 82 83 84def inv_digamma(y: float, eps: float=1e-8, max_iter: int=100) -> float: 85 '''Numerical inverse to the digamma function by root finding''' 86 87 if y >= -2.22: 88 xold = np.exp(y) + 1/2 89 else: 90 xold = -1 / (y + np.euler_gamma) 91 92 for _ in range(max_iter): 93 94 xnew = xold - (digamma(xold) - y) / polygamma(1, xold) 95 96 if np.abs(xold - xnew) < eps: 97 break 98 99 xold = xnew 100 101 return xnew
def
get_noise_distribution(data, method='moments'):
10def get_noise_distribution(data, method='moments'): 11 '''Computes sigma and N from an array of gamma distributed data 12 13 input 14 ----- 15 data 16 A numpy array of gamma distributed values 17 18 method='moments' or method='maxlk' 19 Use either the moments or maximum likelihood equations to estimate the parameters. 20 21 output 22 ------ 23 sigma, N 24 parameters related to the original Gaussian noise distribution 25 ''' 26 27 data = data[data > 0] 28 29 # If we have no voxel or only the same value 30 # it leads to a divide by 0 as an edge case 31 if data.size == 0 or np.std(data) == 0: 32 return 0, 0 33 34 # First get sigma 35 if method == 'moments': 36 mdata2 = np.mean(data**2) 37 mdata4 = np.mean(data**4) 38 sigma = np.sqrt(mdata4 / mdata2 - mdata2) / np.sqrt(2) 39 elif method == 'maxlk': 40 sigma = maxlk_sigma(data) 41 else: 42 raise ValueError(f'Invalid method name {method}') 43 44 t = 1/2 * (data / sigma)**2 45 46 # Now compute N 47 if method == 'moments': 48 N = np.mean(t) 49 elif method == 'maxlk': 50 y = np.mean(np.log(t)) 51 N = inv_digamma(y) 52 else: 53 raise ValueError(f'Invalid method name {method}') 54 55 return sigma, N
Computes sigma and N from an array of gamma distributed data
input
data A numpy array of gamma distributed values
method='moments' or method='maxlk' Use either the moments or maximum likelihood equations to estimate the parameters.
output
sigma, N parameters related to the original Gaussian noise distribution
def
maxlk_sigma( m: NDArray, xold: float | None = None, eps: float = 1e-08, max_iter: int = 100):
58def maxlk_sigma(m: npt.NDArray, xold: float | None = None, eps: float=1e-8, max_iter: int=100): 59 '''Maximum likelihood equation to estimate sigma from gamma distributed values''' 60 61 mean_m2 = np.mean(m**2) 62 mean_log_m2 = 2 * np.mean(np.log(m)) 63 64 def f(sigma): 65 return digamma(mean_m2 / (2*sigma**2)) - mean_log_m2 + np.log(2*sigma**2) 66 67 def fprime(sigma): 68 return -mean_m2 * polygamma(1, mean_m2 / (2*sigma**2)) / sigma**3 + 2/sigma 69 70 if xold is None: 71 xold = m.std() 72 73 for _ in range(max_iter): 74 75 xnew = xold - f(xold) / fprime(xold) 76 77 if np.abs(xold - xnew) < eps: 78 break 79 80 xold = xnew 81 82 return xnew
Maximum likelihood equation to estimate sigma from gamma distributed values
def
inv_digamma(y: float, eps: float = 1e-08, max_iter: int = 100) -> float:
85def inv_digamma(y: float, eps: float=1e-8, max_iter: int=100) -> float: 86 '''Numerical inverse to the digamma function by root finding''' 87 88 if y >= -2.22: 89 xold = np.exp(y) + 1/2 90 else: 91 xold = -1 / (y + np.euler_gamma) 92 93 for _ in range(max_iter): 94 95 xnew = xold - (digamma(xold) - y) / polygamma(1, xold) 96 97 if np.abs(xold - xnew) < eps: 98 break 99 100 xold = xnew 101 102 return xnew
Numerical inverse to the digamma function by root finding