@@ -277,37 +277,159 @@ setPheno = function(pop, h2=NULL, H2=NULL, varE=NULL, corE=NULL,
277277 return (pop )
278278}
279279
280+ # ' @title Convert a normal (Gaussian) trait to a log-normal trait
281+ # ' @param x matrix, values for one or more traits (if not a matrix,
282+ # ' we cast to a matrix).
283+ # ' @param meanlog \code{NULL}, numeric or list, trait mean(s) on the log scale;
284+ # ' when \code{NULL} mean of 0 is assumed,
285+ # ' when numeric means for all traits in \code{x} must be provided, and
286+ # ' when list means for all traits in \code{x} must be provided with possibility
287+ # ' to pass a \code{NULL} list node to skip the conversion for the trait
288+ # ' (see examples).
289+ # ' @details If input trait is normal (Gaussian) then this function generates
290+ # ' a log-normal trait by applying exponential link function on the input.
291+ # ' Note the possible terminological confusion, a log-normal trait is expressed
292+ # ' on exponential scale and its underlying (latent) values are on the log scale
293+ # ' (see examples). See \code{\link{rlnorm}} on the log-normal distribution,
294+ # ' including the expressions for the expected value and variance of observed trait,
295+ # ' which can help you tune the parameters to obtain targeted observed trait values.
296+ # ' Note that \code{asLogNormal} does not provide the \code{sdlog} argument because
297+ # ' latent trait variation is already controlled by other parameters.
298+ # ' See examples below.
299+ # ' @return matrix of log-normal values.
300+ # ' @examples
301+ # ' #Simulate a founder pop, set latent trait parameters, and create a population
302+ # ' founderPop = quickHaplo(nInd=10, nChr=1, segSites=10)
303+ # ' SP = SimParam$new(founderPop)
304+ # ' \dontshow{SP$nThreads = 1L}
305+ # ' trtMeanLog = c(0, 0)
306+ # ' trtVarGLog = c(1, 2)
307+ # ' SP$addTraitA(nQtlPerChr = 10, mean = trtMeanLog, var = trtVarGLog,
308+ # ' corA = matrix(data = c(1.0, 0.6,
309+ # ' 0.6, 1.0), ncol = 2))
310+ # ' trtVarELog = c(1, 1)
311+ # ' trtVarPLog = trtVarGLog + trtVarELog
312+ # ' SP$setVarE(varE = trtVarELog)
313+ # ' pop = newPop(founderPop)
314+ # ' popLarge = randCross(pop, nCrosses = 1000)
315+ # '
316+ # ' meanVarFun = function(x) list(mean = mean(x), var = var(x))
317+ # '
318+ # ' #Latent phenotypes and parameters
319+ # ' (phenoLog = pheno(pop))
320+ # ' phenoLogLarge = pheno(popLarge)
321+ # ' apply(X = phenoLog, MARGIN = 2, FUN = meanVarFun)
322+ # ' apply(X = phenoLogLarge, MARGIN = 2, FUN = meanVarFun)
323+ # '
324+ # ' #Convert a single input trait
325+ # ' (phenoExpMeanLog0 = asLogNormal(x = pheno(pop)[, 1]))
326+ # ' meanVarFun(phenoExpMeanLog0)
327+ # '
328+ # ' #Demonstrate meanlog argument
329+ # ' #Here we aim to obtain observed trait values with the mean of 1.
330+ # ' #Since E(trtExp)=exp(mean(trtLog)+var(trtLog)/2), trtMeanLog[1]=0, and trtVarPLog[1],
331+ # ' #to get E(trtExp)=1=exp(0) we set meanlog to -trtVarPLog[1]/2.
332+ # ' #See also ?rlnorm for the expression of variance.
333+ # ' (phenoExpMeanExp1 = asLogNormal(x = pheno(pop)[, 1], meanlog = -trtVarPLog[1]/2))
334+ # ' meanVarFun(phenoExpMeanExp1)
335+ # ' exp(trtVarPLog[1] - 1)
336+ # ' cbind(phenoLog = phenoLog[, 1],
337+ # ' phenoExpMeanLog0 = phenoExpMeanLog0,
338+ # ' phenoExpMeanExp1 = phenoExpMeanExp1)
339+ # '
340+ # ' tmp = cbind(phenoLog = phenoLogLarge[, 1],
341+ # ' phenoExpMeanLog0 = c(asLogNormal(phenoLogLarge[, 1])),
342+ # ' phenoExpMeanExp1 = c(asLogNormal(phenoLogLarge[, 1], meanlog = -trtVarPLog[1]/2)))
343+ # ' (tmp2 = apply(X = tmp, MARGIN = 2, FUN = meanVarFun))
344+ # ' par(mfrow = c(3, 1))
345+ # ' hist(tmp[, "phenoLog"], main = paste0("Mean: ", v=tmp2$phenoLog$mean))
346+ # ' abline(v=tmp2$phenoLog$mean, col = "red")
347+ # ' hist(tmp[, "phenoExpMeanLog0"], main = paste0("Mean: ", v=tmp2$phenoExpMeanLog0$mean))
348+ # ' abline(v = tmp2$phenoExpMeanLog0$mean, col = "red")
349+ # ' hist(tmp[, "phenoExpMeanExp1"], main = paste0("Mean: ", v=tmp2$phenoExpMeanExp1$mean))
350+ # ' abline(v= tmp2$phenoExpMeanExp1$mean, col = "red")
351+ # '
352+ # ' #Convert multiple input traits
353+ # ' asLogNormal(x = pheno(pop))
354+ # ' try(asLogNormal(x = pheno(pop), meanlog = 0))
355+ # ' asLogNormal(x = pheno(pop), meanlog = c(0, 1))
356+ # ' asLogNormal(x = pheno(pop), meanlog = list(0, NULL))
357+ # '
358+ # ' #Store the recoded trait manually
359+ # ' pheno(pop)
360+ # ' pop@pheno[, 1] = asLogNormal(x = pheno(pop)[, 1])
361+ # ' pheno(pop)
362+ # '
363+ # ' #Apply and store the transformation automatically via SimParam$finalizePop()
364+ # ' SP$finalizePop = function(pop, simParam = SP) {
365+ # ' pop@pheno[, 1] = asLogNormal(x = pheno(pop)[, 1])
366+ # ' return(pop)
367+ # ' }
368+ # ' pop = newPop(founderPop)
369+ # ' pheno(pop)
370+ # ' @export
371+ asLogNormal <- function (x , meanlog = NULL ) {
372+ if (! is.matrix(x )) {
373+ x = as.matrix(x )
374+ }
375+ nTraits = ncol(x )
376+ if (is.null(meanlog )) {
377+ meanlog = rep(x = 0 , times = nTraits )
378+ }
379+ if (is.numeric(meanlog )) {
380+ if (length(meanlog ) != nTraits ) {
381+ stop(" You must supply meanlog for all traits in x!" )
382+ }
383+ for (trt in 1 : nTraits ) {
384+ x [, trt ] = exp(meanlog [trt ] + x [, trt ])
385+ }
386+ } else if (is.list(meanlog )) {
387+ if (length(meanlog ) != nTraits ) {
388+ stop(" You must supply meanlog for all traits in x!" )
389+ }
390+ for (trt in 1 : nTraits ) {
391+ if (! is.null(meanlog [[trt ]])) {
392+ x [, trt ] = exp(meanlog [[trt ]] + x [, trt ])
393+ }
394+ }
395+ } else {
396+ stop(" meanlog must be NULL, numeric, or list!" )
397+ }
398+ return (x )
399+ }
400+
280401# ' @title Convert a normal (Gaussian) trait to an ordered categorical (threshold)
281402# ' trait
282403# ' @param x matrix, values for one or more traits (if not a matrix,
283- # ' we cast to a matrix)
284- # ' @param p NULL, numeric or list, when \code{NULL} the \code{threshold} argument
285- # ' takes precedence; when numeric, provide a vector of probabilities of
286- # ' categories to convert continuous values into categories for a single trait
287- # ' (if probabilities do not sum to 1, another category is added and a warning
288- # ' is raised); when list, provide a list of numeric probabilities - list node
289- # ' with \code{NULL} will skip conversion for a specific trait (see examples);
290- # ' internally \code{p} is converted to \code{threshold} hence input
291- # ' \code{threshold} is overwritten
404+ # ' we cast to a matrix).
405+ # ' @param p \code{ NULL} , numeric, or list, when \code{NULL} the \code{threshold}
406+ # ' argument takes precedence; when numeric, provide a vector of probabilities
407+ # ' of categories to convert continuous values into categories for a single
408+ # ' trait (if probabilities do not sum to 1, another category is added and
409+ # ' a warning is raised); when list, provide a list of numeric probabilities
410+ # ' - list node with \code{NULL} will skip conversion for a specific trait
411+ # ' (see examples); internally \code{p} is converted to \code{threshold} hence
412+ # ' input \code{threshold} is overwritten.
292413# ' @param mean numeric, assumed mean(s) of the normal (Gaussian) trait(s);
293- # ' used only when \code{p} is given
414+ # ' used only when \code{p} is given.
294415# ' @param var numeric, assumed variance(s) of the normal (Gaussian) trait(s);
295- # ' used only when \code{p} is given
296- # ' @param threshold NULL, numeric or list, when numeric, provide a vector of
416+ # ' used only when \code{p} is given.
417+ # ' @param threshold \code{ NULL} , numeric or, list, when numeric, provide a vector of
297418# ' threshold values to convert continuous values into categories for a single trait
298419# ' (the thresholds specify left-closed and right-opened intervals [t1, t2),
299420# ' which can be changed with \code{include.lowest} and \code{right};
300421# ' ensure you add \code{-Inf} and \code{Inf} or min and max to cover the whole
301422# ' range of values; otherwise you will get \code{NA} values);
302423# ' when list, provide a list of numeric thresholds - list node with \code{NULL}
303- # ' will skip conversion for a specific trait (see examples)
304- # ' @param include.lowest logical, see \code{\link{cut}}
305- # ' @param right logical, see \code{\link{cut}}
424+ # ' will skip conversion for a specific trait (see examples).
425+ # ' @param include.lowest logical, see \code{\link{cut}}.
426+ # ' @param right logical, see \code{\link{cut}}.
306427# ' @details If input trait is normal (Gaussian) then this function generates a
307428# ' categorical trait according to the ordered probit model.
308429# ' @return matrix of values with some traits recorded as ordered categories
309- # ' in the form of 1:nC with nC being the number of categories.
430+ # ' in the form of \code{ 1:nC} with \code{nC} being the number of categories.
310431# ' @examples
432+ # ' #Simulate a founder pop, set latent trait parameters, and create a population
311433# ' founderPop = quickHaplo(nInd=10, nChr=1, segSites=10)
312434# ' SP = SimParam$new(founderPop)
313435# ' \dontshow{SP$nThreads = 1L}
@@ -316,10 +438,10 @@ setPheno = function(pop, h2=NULL, H2=NULL, varE=NULL, corE=NULL,
316438# ' SP$addTraitA(nQtlPerChr = 10, mean = trtMean, var = trtVarG,
317439# ' corA = matrix(data = c(1.0, 0.6,
318440# ' 0.6, 1.0), ncol = 2))
319- # ' pop = newPop(founderPop)
320441# ' trtVarE = c(1, 1)
321442# ' trtVarP = trtVarG + trtVarE
322- # ' pop = setPheno(pop, varE = trtVarE)
443+ # ' SP$setVarE(varE = trtVarE)
444+ # ' pop = newPop(founderPop)
323445# ' pheno(pop)
324446# '
325447# ' #Convert a single input trait
@@ -356,6 +478,19 @@ setPheno = function(pop, h2=NULL, H2=NULL, varE=NULL, corE=NULL,
356478# ' p = list(c(0.5, 0.5),
357479# ' p),
358480# ' mean = trtMean, var = trtVarP)
481+ # '
482+ # ' #Store the recoded trait manually
483+ # ' pheno(pop)
484+ # ' pop@pheno[, 1] = asCategorical(x = pheno(pop)[, 1])
485+ # ' pheno(pop)
486+ # '
487+ # ' #Apply and store the transformation automatically via SimParam$finalizePop()
488+ # ' SP$finalizePop = function(pop, simParam = SP) {
489+ # ' pop@pheno[, 1] = asCategorical(x = pheno(pop)[, 1])
490+ # ' return(pop)
491+ # ' }
492+ # ' pop = newPop(founderPop)
493+ # ' pheno(pop)
359494# ' @export
360495asCategorical = function (x , p = NULL , mean = 0 , var = 1 ,
361496 threshold = c(- Inf , 0 , Inf ),
@@ -384,8 +519,11 @@ asCategorical = function(x, p = NULL, mean = 0, var = 1,
384519 for (trt in 1 : nTraits ) {
385520 if (! is.null(p [[trt ]])) {
386521 pSum = sum(p [[trt ]])
387- if (! (pSum == 1 )) { # TODO: how can we do floating point aware comparison?
388- warning(" Probabilities do not sum to 1 - creating one more category!" )
522+ if (pSum > 1 ) {
523+ stop(" Probabilities for trait " , trt , " sum to more than 1!" )
524+ } else if (pSum < 1 ) {
525+ warning(" Probabilities do not sum to 1 for trait " , trt ,
526+ " ! Creating one more category!" )
389527 p [[trt ]] = c(p [[trt ]], 1 - pSum )
390528 }
391529 tmp = qnorm(p = cumsum(p [[trt ]]), mean = mean [trt ], sd = sqrt(var [trt ]))
@@ -406,7 +544,7 @@ asCategorical = function(x, p = NULL, mean = 0, var = 1,
406544 threshold = list (threshold )
407545 }
408546 if (length(threshold ) != nTraits ) {
409- stop(" You must supply thresholds for all traits in x !" )
547+ stop(" You must supply thresholds for all traits in x!" )
410548 }
411549 for (trt in 1 : nTraits ) {
412550 if (! is.null(threshold [[trt ]])) {
0 commit comments