1

I am trying to solve for the parameters of a gamma distribution that is convolved with both normal and lognormal distributions. I can experimentally derive parameters for both the normal and lognormal components, hence, I just want to solve for the gamma params.

I have attempted 3 approaches to this problem:

1) generating convolved random datasets (i.e. rnorm()+rlnorm()+rgamma()) and using least-squares regression on the linear- or log-binned histograms of the data (not shown, but was very biased by RNG and didn't optimize well at all.)

2) "brute-force" numerical integration of the convolving functions (example code #1)

3) numerical integration approaches w/ the distr package. (example code #2)

I have had limited success with all three approaches. Importantly, these approaches seem to work well for "nominal" values for the gamma parameters, but they all begin to fail when k(shape) is low and theta(scale) is high—which is where my experimental data resides. please find the examples below.

Straight-up numerical Integration

# make the functions
f.N <- function(n) dnorm(n, N[1], N[2])
f.L <- function(l) dlnorm(l, L[1], L[2])
f.G <- function(g) dgamma(g, G[1], scale=G[2])

# make convolved functions
f.Z <- function(z) integrate(function(x,z) f.L(z-x)*f.N(x), -Inf, Inf, z)$value # L+N
f.Z <- Vectorize(f.Z)
f.Z1 <- function(z) integrate(function(x,z) f.G(z-x)*f.Z(x), -Inf, Inf, z)$value # G+(L+N)
f.Z1 <- Vectorize(f.Z1)

# params of Norm, Lnorm, and Gamma
N <- c(0,5)
L <- c(2.5,.5)
G <- c(2,7) # this distribution is the one we ultimately want to solve for.
# G <- c(.5,10) # 0<k<1
# G <- c(.25,5e4) # ballpark params of experimental data 

# generate some data
set.seed(1)
rN <- rnorm(1e4, N[1], N[2])
rL <- rlnorm(1e4, L[1], L[2])
rG <- rgamma(1e4, G[1], scale=G[2])
Z <- rN + rL
Z1 <- rN + rL + rG

# check the fit
hist(Z,freq=F,breaks=100, xlim=c(-10,50), col=rgb(0,0,1,.25))
hist(Z1,freq=F,breaks=100, xlim=c(-10,50), col=rgb(1,0,0,.25), add=T)
z <- seq(-10,50,1)
lines(z,f.Z(z),lty=2,col="blue", lwd=2) # looks great... convolution performs as expected.
lines(z,f.Z1(z),lty=2,col="red", lwd=2) # this works perfectly so long as k(shape)>=1

# I'm guessing the failure to compute when shape 0 < k < 1 is due to 
# numerical integration problems, but I don't know how to fix it.
integrate(dgamma, -Inf, Inf, shape=1, scale=1) # ==1
integrate(dgamma, 0, Inf, shape=1, scale=1) # ==1
integrate(dgamma, -Inf, Inf, shape=.5, scale=1) # !=1
integrate(dgamma, 0, Inf, shape=.5, scale=1) # != 1

# Let's try to estimate gamma anyway, supposing k>=1
optimFUN <- function(par, N, L) {
  print(par)
  -sum(log(f.Z1(Z1[1:4e2])))
}
f.G <- function(g) dgamma(g, par[1], scale=par[2])
fitresult <- optim(c(1.6,5), optimFUN, N=N, L=L)
par <- fitresult$par
lines(z,f.Z1(z),lty=2,col="green3", lwd=2) # not so great... likely better w/ more data, 
# but it is SUPER slow and I observe large step sizes.

Attempting convolving via distr package

# params of Norm, Lnorm, and Gamma
N <- c(0,5)
L <- c(2.5,.5)
G <- c(2,7) # this distribution is the one we ultimately want to solve for.
# G <- c(.5,10) # 0<k<1
# G <- c(.25,5e4) # ballpark params of experimental data 

# make the distributions and "convolvings'
dN <- Norm(N[1], N[2])
dL <- Lnorm(L[1], L[2])
dG <- Gammad(G[1], G[2])
d.NL <- d(convpow(dN+dL,1))
d.NLG <- d(convpow(dN+dL+dG,1)) # for large values of theta, no matter how I change 
# getdistrOption("DefaultNrFFTGridPointsExponent"), grid size is always wrong.

# Generate some data
set.seed(1)
rN <- r(dN)(1e4)
rL <- r(dL)(1e4)
rG <- r(dG)(1e4)
r.NL <- rN + rL
r.NLG <- rN + rL + rG

# check the fit
hist(r.NL, freq=F, breaks=100, xlim=c(-10,50), col=rgb(0,0,1,.25))
hist(r.NLG, freq=F, breaks=100, xlim=c(-10,50), col=rgb(1,0,0,.25), add=T)
z <- seq(-10,50,1)
lines(z,d.NL(z), lty=2, col="blue", lwd=2) # looks great... convolution performs as expected.
lines(z,d.NLG(z), lty=2, col="red", lwd=2) # this appears to work perfectly 
# for most values of K and low values of theta

# this is looking a lot more promising... how about estimating gamma params?
optimFUN <- function(par, dN, dL) {
  tG <- Gammad(par[1],par[2])
  d.NLG <- d(convpow(dN+dL+tG,1))
  p <- d.NLG(r.NLG)
  p[p==0] <- 1e-15 # because sometimes very low probabilities evaluate to 0... 
# ...and logs don't like that.
  -sum(log(p))
}
fitresult <- optim(c(1,1e4), optimFUN, dN=dN, dL=dL)
fdG <- Gammad(fitresult$par[1], fitresult$par[2])
fd.NLG <- d(convpow(dN+dL+fdG,1))
lines(z,fd.NLG(z), lty=2, col="green3", lwd=2) ## this works perfectly when ~k>1 & ~theta<100... but throws
## "Error in validityMethod(object) : shape has to be positive" when k decreases and/or theta increases 
## (boundary subject to RNG).

Can i speed up the integration in example 1? can I increase the grid size in example 2 (distr package)? how can I address the k<1 problem? can I rescale the data in a way that will better facilitate evaluation at high theta values? Is there a better way all-together? Help!

PeterFoster
  • 319
  • 1
  • 3
  • 9
  • What meaning are you attaching to the word "convoluted"? (In English the word means confused. There is a mathematical verb "convolve".) Are you actually asking for mixture distribution estimation? – IRTFM Mar 18 '16 at 05:14
  • @42- To compute PDF of sum of two variates `A` and `B`, you have to compute convolution integral along the lines `PDF(z) = S PDFa(x)*PDFb(z-x) dx`, where `S` is an integral – Severin Pappadeux Mar 18 '16 at 16:24
  • Yes, I may be exchanging convolute for convolve... I'm sure you can guess that programming/stats is not my primary language. As illustrated in the examples, I'm interested the situation `rnorm(n, mu, sig) + rlnorm(n2, mu2, sig2)` as opposed to `append(rnorm(n, mu, sig), rlnorm(n2, mu2, sig2))`. I'm not sure which is classified as a "mixture distribution". – PeterFoster Mar 18 '16 at 16:26
  • Example code #1 takes exactly that form (@ Severin P) – PeterFoster Mar 18 '16 at 16:27

1 Answers1

1

Well, convolution of function with gaussian kernel calls for use of Gauss–Hermite quadrature. In R it is implemented in special package: https://cran.r-project.org/web/packages/gaussquad/gaussquad.pdf

UPDATE

For convolution with Gamma distribution this package might be useful as well via Gauss-Laguerre quadrature

UPDATE II

Here is quick code to convolute gaussian with lognormal, hopefully not a lot of bugs and and prints some reasonable looking graph

library(gaussquad)

n.quad <- 170 # integration order

# get the particular weights/abscissas as data frame with 2 observables and n.quad observations
rule <- ghermite.h.quadrature.rules(n.quad, mu = 0.0)[[n.quad]]

# test function - integrate 1 over exp(-x^2) from -Inf to Inf
# should get sqrt(pi) as an answer
f <- function(x) {
    1.0
}

q <- ghermite.h.quadrature(f, rule)
print(q - sqrt(pi))

# convolution of lognormal with gaussian
# because of the G-H rules, we have to make our own function
# for simplicity, sigmas are one and mus are zero

sqrt2 <- sqrt(2.0)

c.LG <- function(z) {
   #print(z)

   f.LG <- function(x) {
        t <- (z - x*sqrt2)
        q <- 0.0
        if (t > 0.0) {
            l <- log(t)
            q <- exp( - 0.5*l*l ) / t
        }
        q
   }

   ghermite.h.quadrature(Vectorize(f.LG), rule) / (pi*sqrt2)
}

library(ggplot2)

p <- ggplot(data = data.frame(x = 0), mapping = aes(x = x))
p <- p + stat_function(fun = Vectorize(c.LG))
p <- p + xlim(-1.0, 5.0)
print(p)
Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64
  • This looks interesting... it's going to take me some time to work through this approach for my data (and figure out what a quadrature is), but I'll return to this post once I have. – PeterFoster Mar 18 '16 at 18:08
  • @PeterFoster ok. Those are sets of weights/abscissas for integrals with `exp(-x^2)` or `exp(-x)` kernels under the integration sign. May help with gaussian convolution as well as with convolution with exponential term in the gamma distribution. BUt it will require some math to properly cut out kernel terms – Severin Pappadeux Mar 18 '16 at 18:17
  • @PeterFoster thinking a bit about it, I would do first Gamma with Lognormal, getting `exp` term from gamma and using Gauss-Laguerre. Gaussian via Gauss-Hermite would be last one – Severin Pappadeux Mar 18 '16 at 20:11
  • @ Severin P. Thanks for the advice; the gamma + lognormal components are the dominating functions in my data, so I'll start there. I might be able to get some results by later this week. – PeterFoster Mar 21 '16 at 19:08