File: IntDensTri.R

package info (click to toggle)
r-cran-lavasearch2 2.0.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,832 kB
  • sloc: cpp: 28; sh: 13; makefile: 2
file content (275 lines) | stat: -rw-r--r-- 10,778 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
### InTriGauss.R --- 
##----------------------------------------------------------------------
## author: Brice Ozenne
## created: aug 14 2017 (11:49) 
## Version: 
## last-updated: maj 23 2018 (09:42) 
##           By: Brice Ozenne
##     Update #: 495
##----------------------------------------------------------------------
## 
### Commentary: 
## 
### Change Log:
##----------------------------------------------------------------------
## 
### Code:



## * Documentation - intDensTri
##' @title Integrate a Gaussian/Student Density over a Triangle
##' @name intDensTri
##' @description Consider a univariate random variable X,
##' two multivariate random variables Y and Z,
##' and t1 and t2 two real numbers.
##' This function can compute either
##' P[|X|>t1,|X]>|Y1|,...,|X]>|Yp|] if zmin is not specified,
##' P[|Z1|<t2,...,|Zq|<t2,|X|>t1,|X]>|Y1|,...,|X]>|Yp|] if zmin is specified.
##' 
##' @param mu [numeric vector] the expectation of the joint distribution.
##' @param Sigma [matrix] the variance-covariance of the joint distribution.
##' @param df [integer > 0] the degree of freedom of the joint Student's t distribution.
##' Only used when \code{distribution="pvmt"}.
##' @param n [integer > 0] number of points for the numerical integration.
##' @param x.min [numeric] the minimum value along the x axis.
##' @param z.max [numeric vector, optional] the maximum value along the z axis.
##' Define the dimension of Z.
##' @param type [character] the type of mesh to be used.
##' Can be \code{\"raw\"}, \code{\"double\"}, or \code{\"fine\"}.
##' @param prune [integer >0] number of standard deviations after which the domain ends along the x axis. 
##' @param proba.min [numeric 0-1] the probability used to find the maximum value along the x axis.
##' Only used if \code{prune} is not specified.
##' @param distribution [character] type of joint distribution.
##' Can be \code{"pmvnorm"} (normal distribution) or \code{"pvmt"} (Student's t distribution)
##' @details
##' Argument \code{type}: \itemize{
##' \item \code{\"raw\"}: mesh with points inside the domain
##' \item \code{\"double\"}: mesh with points outside the domain
##' \item \code{\"fine\"}: mesh with points inside the domain plus additional rectangles trying to fill the missing domain.
##' }
##'
##' Argument \code{Sigma} and \code{mu}:
##' define the mean and variance-covariance of the random variables X, Y, Z
##' (in this order). The length of the argument \code{z.max} is used to define the dimension of Z.
##' The dimension of X is always 1.
##'
##' @return A numeric.
##' 
##' @examples
##' library(mvtnorm)
##' 
##' p <- 2
##' Sigma <- diag(p)
##' mu <- rep(0, p)
##' 
##' ## bivariate normal distribution
##' z2 <- qmvt(0.975, mean = mu, sigma = Sigma, df = 1e3)$quantile
##'
##' # compute integral
##' intDensTri(mu = mu, Sigma = Sigma, n=5, x.min=0, type = "fine")$value-1/2
##' intDensTri(mu = mu, Sigma = Sigma, n=30, x.min=0, type = "raw")$value-1/2
##' intDensTri(mu = mu, Sigma = Sigma, n=50, x.min=0, type = "raw")$value-1/2
##'
##' intDensTri(mu = mu, Sigma = Sigma, df = 5, n=5, x.min=0, distribution = "pmvt")$value-1/2
##' res <- intDensTri(mu = mu, Sigma = Sigma, df = 5, n=10, x.min=0, distribution = "pmvt")
##' res$value-1/2
##' ggplot2::autoplot(res)
##' 
##' ## trivariate normal distribution
##' \dontrun{
##' p <- 3
##' Sigma <- diag(p)
##' mu <- rep(0, p)
##'
##' res2 <- intDensTri(mu = mu, Sigma = Sigma, n=5, x.min = 0, z.max = 10)
##' ggplot2::autoplot(res2)
##' ggplot2::autoplot(res2, coord.plot = c("x","z1"))
##' res2
##' }
##' 
##' #### when the distribution is far from 0
##' \dontrun{
##' eq1 <- intDensTri(mu = c(10,0), Sigma = diag(1,2), 
##'                   x.min = 2, n=10)
##' eq1$value-1
##' ggplot2::autoplot(eq1)
##'
##' eq2 <- intDensTri(mu = c(10,0,0), Sigma = diag(1,3),
##'                   x.min=2, z.max = 10, type = "raw",
##'                   n=10)
##' ggplot2::autoplot(eq2, coord.plot = c("y1","z1"))
##' eq2$value-1
##'
##' ## more variables
##' p <- 5
##' Sigma <- diag(p)
##' mu <- rep(0, p)
##'
##' res2 <- intDensTri(mu = mu, Sigma = Sigma, n=5, x.min = 1, z.max = c(2,2))
##' res2$grid
##' }
##'
##' @concept post-selection inference

## * intDensTri
##' @rdname intDensTri
##' @export 
intDensTri <- function(mu, Sigma, df, n, x.min, z.max = NULL,
                       type = "double", proba.min = 1e-6, prune = NULL, distribution = "pmvnorm"){

     interior <- NULL ## [:for CRAN check] subset
    
    ## ** normalize arguments
    type <- match.arg(type, c("raw","fine","double"))
    
    p <- length(mu)
    d.z <- length(z.max)
    d.y <- p - d.z - 1
    z.max <- unique(z.max)
    
    if(is.null(prune)){
        if(distribution=="pmvnorm"){
            prune <- ceiling(abs(qmvnorm(proba.min, sigma = diag(1,d.y))$quantile))
        }else if(distribution=="pmvt"){
            prune <- ceiling(abs(mvtnorm::qmvt(proba.min, sigma = diag(1,d.y), df = df)$quantile))
        }else {
            stop("distribution must be either \"pmvnorm\" or \"pmvt\" \n")
        }
    }
    coordX.max <- (mu[1]+x.min) + prune * sqrt(diag(Sigma)[1])
    
    ls.args <- list(mu,Sigma)    
    if(distribution=="pmvt"){
        names(ls.args) <- c("delta","sigma")
        ls.args$df <- df
    }else{
        names(ls.args) <- c("mean","sigma")
    }

    ## ** create the grid of points to integrate over
    resGrid <- createGrid(n,
                          xmin = x.min, xmax = coordX.max, d.y = d.y, 
                          d.z = d.z, zmax = z.max, 
                          fine = (type=="fine"), double = FALSE
                          )
    grid <- resGrid$grid
    seqNames.min <- resGrid$seqNames.min
    seqNames.max <- resGrid$seqNames.max

    if(type=="double"){
        grid.double <- createGrid(n,
                                  xmin = x.min, xmax = coordX.max, d.y = d.y, 
                                  d.z = d.z, zmax = z.max, 
                                  fine = FALSE, double = TRUE
                                  )$grid
        grid <- rbind(cbind(grid,interior=TRUE),
                      cbind(grid.double,interior=FALSE))
    }

    ## ** integration
    total.area <- 0
    grid$area <- apply(grid, 1, function(x){
        do.call(distribution, args = c(lower = list(c(x[seqNames.min])),
                                       upper = list(c(x[seqNames.max])),
                                       ls.args))
    })


    if(type=="double"){
        grid.interior <- subset(grid, interior==TRUE,select = c("area", "weight"))
        area.interior <- sum(grid.interior$area * grid.interior$weight)
        grid.exterior <- subset(grid, interior==FALSE,select = c("area", "weight"))
        area.exterior <- sum(grid.exterior$area * grid.exterior$weight)
        total.area <- area.interior + (area.exterior-area.interior)/2
    }else{        
        total.area <- sum(grid$area * grid$weight)
    }

    ## ** export
    out <- list()
    out$value <- total.area    
    out$prune <- prune
    out$type <- type
    out$grid <- grid
    class(out) <- "intDensTri"
    return(out)
}

## * autoplot.intDensTri
#' @title 2D-display of the Domain Used to Compute the Integral
#' @description 2D-display of the domain used to compute the integral.
#'
#' @param object output of the function \code{intDensTri}.
#' @param coord.plot [character vector] the x and y coordinates. Can be \code{"x"}, \code{"y1"} to \code{"yd"}, \code{"z"} if \code{zmin} was specified when calling \code{intDensTri}.
#' @param plot [logical] should the plot be displayed?
#' @param ... [internal] Only used by the generic method.
#'
#' @return A \code{ggplot} object.
#' @seealso \code{\link{intDensTri}}
#' 
#' @method autoplot intDensTri
#' @export
autoplot.intDensTri <- function(object, coord.plot=c("x","y1"), plot = TRUE, ...){

    if(length(coord.plot) != 2){
        stop("coord.plot must have length 2 \n")
    }

    gg.data <- object$grid
    gg.data$index <- as.factor(gg.data$index)
    gg.data$weight <- as.factor(gg.data$weight)
    
    if("x" %in% coord.plot == FALSE){
        x.ref <- min(abs(unique(gg.data$x.min)))
        gg.data <- rbind(gg.data[gg.data$x.min == x.ref,,drop=FALSE],
                         gg.data[gg.data$x.max == -x.ref,,drop=FALSE])
    }

    if(object$type=="fine"){
        gg.grid <- ggplot2::ggplot(gg.data, aes_string(xmin = paste0(coord.plot[1],".min"), ymin = paste0(coord.plot[2],".min"),
                                                      xmax = paste0(coord.plot[1],".max"), ymax = paste0(coord.plot[2],".max"),
                                                      fill = "index", alpha = "weight"))
        gg.grid <- gg.grid + ggplot2::scale_alpha_manual(values = c(0.45,1))
    }else if(object$type=="raw"){
        gg.grid <- ggplot2::ggplot(gg.data, aes_string(xmin = paste0(coord.plot[1],".min"), ymin = paste0(coord.plot[2],".min"),
                                                      xmax = paste0(coord.plot[1],".max"), ymax = paste0(coord.plot[2],".max"),
                                                      fill = "index"))        
    }else if(object$type=="double"){
        gg.grid <- ggplot2::ggplot(gg.data, aes_string(xmin = paste0(coord.plot[1],".min"), ymin = paste0(coord.plot[2],".min"),
                                                      xmax = paste0(coord.plot[1],".max"), ymax = paste0(coord.plot[2],".max"),
                                                      fill = "index", alpha = "interior"))
        gg.grid <- gg.grid + ggplot2::scale_alpha_manual(values = c(0.4,1))
    }
    gg.grid <- gg.grid + ggplot2::geom_rect() + ggplot2::xlab(coord.plot[1]) + ggplot2::ylab(coord.plot[2])
    gg.grid <- gg.grid + ggplot2::geom_abline(slope = 1,color = "black") + ggplot2::geom_abline(slope = -1,color = "black")

    if(plot){
        print(gg.grid)
    }
    return(invisible(gg.grid))
}


## * print.intDensTri
#' @method print intDensTri
#' @export
print.intDensTri <- function(x, ...){
    interior <- NULL
    
    x.min <- min(abs(x$grid$x.min))
    x.max <- max(abs(x$grid$x.max))
    n.rectangle <- switch(x$type,
                          "double" = NROW(x$grid[interior==TRUE]),
                          NROW(x$grid)
                          )
    
    cat("integral=",x$value,"\n",
        "computed with ",n.rectangle," rectangles \n",
        "with x ranging from ",x.min," to ",x.max,"\n",
        sep = "")

    return(NULL)
}
#----------------------------------------------------------------------
### InTriGauss.R ends here