######################################################################
#
# addqtl.R
#
# copyright (c) 2007-2023, Karl W. Broman
# last modified Mar, 2023
# first written Nov, 2007
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License,
# version 3, as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but without any warranty; without even the implied warranty of
# merchantability or fitness for a particular purpose. See the GNU
# General Public License, version 3, for more details.
#
# A copy of the GNU General Public License, version 3, is available
# at http://www.r-project.org/Licenses/GPL-3
#
# Part of the R/qtl package
# Contains: addint, print.addint, addqtl, addpair,
# reviseqtlnuminformula, qtlformulasymmetric
# dropfromqtlformula
# addcovarint, print.addcovarint, summary.addcovarint
#
######################################################################
######################################################################
# addint
#
# Try adding each possible QTL:QTL interaction (that is not
# already in the formula), and give results similar to the drop-one
# analysis.
######################################################################
addint <-
function(cross, pheno.col=1, qtl, covar=NULL, formula,
method=c("imp","hk"), model=c("normal", "binary"),
qtl.only=FALSE, verbose=TRUE, pvalues=TRUE, simple=FALSE,
tol=1e-4, maxit=1000, require.fullrank=FALSE)
{
if( !inherits(cross, "cross") )
stop("The cross argument must be an object of class \"cross\".")
if( !inherits(qtl, "qtl") )
stop("The qtl argument must be an object of class \"qtl\".")
if(!is.null(covar) && !is.data.frame(covar)) {
if(is.matrix(covar) && is.numeric(covar))
covar <- as.data.frame(covar, stringsAsFactors=TRUE)
else stop("covar should be a data.frame")
}
if(LikePheVector(pheno.col, nind(cross), nphe(cross))) {
cross$pheno <- cbind(pheno.col, cross$pheno)
pheno.col <- 1
}
if(length(pheno.col) > 1) {
pheno.col <- pheno.col[1]
warning("addint can take just one phenotype; only the first will be used")
}
if(is.character(pheno.col)) {
num <- find.pheno(cross, pheno.col)
if(is.na(num))
stop("Couldn't identify phenotype \"", pheno.col, "\"")
pheno.col <- num
}
if(pheno.col < 1 | pheno.col > nphe(cross))
stop("pheno.col values should be between 1 and the no. phenotypes")
pheno <- cross$pheno[,pheno.col]
if(!is.null(covar) && nrow(covar) != length(pheno))
stop("nrow(covar) != no. individuals in cross.")
method <- match.arg(method)
model <- match.arg(model)
# allow formula to be a character string
if(!missing(formula) && is.character(formula))
formula <- as.formula(formula)
if(method=="imp") {
if(!("geno" %in% names(qtl))) {
if("prob" %in% names(qtl)) {
warning("The qtl object doesn't contain imputations; using method=\"hk\".")
method <- "hk"
}
else
stop("The qtl object needs to be created with makeqtl with what=\"draws\".")
}
}
else {
if(!("prob" %in% names(qtl))) {
if("geno" %in% names(qtl)) {
warning("The qtl object doesn't contain QTL genotype probabilities; using method=\"imp\".")
method <- "imp"
}
else
stop("The qtl object needs to be created with makeqtl with what=\"prob\".")
}
}
if(qtl$n.ind != nind(cross)) {
warning("No. individuals in qtl object doesn't match that in the input cross; re-creating qtl object.")
if(method=="imp")
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="draws")
else
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="prob")
}
if(method=="imp" && dim(qtl$geno)[3] != dim(cross$geno[[1]]$draws)[3]) {
warning("No. imputations in qtl object doesn't match that in the input cross; re-creating qtl object.")
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="draws")
}
# check phenotypes and covariates; drop ind'ls with missing values
if(!is.null(covar)) phcovar <- cbind(pheno, covar)
else phcovar <- as.data.frame(pheno, stringsAsFactors=TRUE)
if(any(is.na(phcovar))) {
if(ncol(phcovar)==1) hasmissing <- is.na(phcovar)
else hasmissing <- apply(phcovar, 1, function(a) any(is.na(a)))
if(all(hasmissing))
stop("All individuals are missing phenotypes or covariates.")
if(any(hasmissing)) {
warning("Dropping ", sum(hasmissing), " individuals with missing phenotypes.\n")
pheno <- pheno[!hasmissing]
qtl$n.ind <- sum(!hasmissing)
if(method=="imp")
qtl$geno <- qtl$geno[!hasmissing,,,drop=FALSE]
else
qtl$prob <- lapply(qtl$prob, function(a) a[!hasmissing,,drop=FALSE])
if(!is.null(covar)) covar <- covar[!hasmissing,,drop=FALSE]
cross <- subset(cross, ind=!hasmissing)
}
}
# number of covariates
if( is.null(covar) ) n.covar <- 0
else n.covar <- ncol(covar)
# if formula is missing, build one
# all QTLs and covarariates will be additive by default
n.qtl <- qtl$n.qtl
if(missing(formula)) {
tmp.Q <- paste("Q", 1:n.qtl, sep="") # QTL term names
formula <- "y~Q1"
if(n.qtl > 1)
for (i in 2:n.qtl)
formula <- paste(formula, tmp.Q[i], sep="+")
if (n.covar) { # if covarariate is not empty
tmp.C <- colnames(covar) # covarariate term names
for(i in 1:n.covar)
formula <- paste(formula, tmp.C[i], sep="+")
}
formula <- as.formula(formula)
}
# check input formula
formula <- checkformula(formula, qtl$altname, colnames(covar))
# look for interactions that haven't been added
factors <- attr(terms(formula), "factors")
if(sum(factors[1,])==0) factors <- factors[-1,]
# replace QTL altnames (Q1 etc) with real names (chr1@20 etc)
fn <- fn.alt <- rownames(factors)
qan <- qtl$altname
qn <- qtl$name
m <- match(fn, qan)
fn.alt[!is.na(m)] <- qn[m[!is.na(m)]]
# all possible interactions
int2test <- int2test.alt <- NULL
for(i in 1:(nrow(factors)-1)) {
for(j in (i+1):nrow(factors)) {
temp <- rep(0, nrow(factors))
temp[c(i,j)] <- 1
if(!any(apply(factors, 2, function(a, b) all(a==b), temp))) {
int2test <- c(int2test, paste(fn[i], fn[j], sep=":"))
int2test.alt <- c(int2test.alt, paste(fn.alt[i], fn.alt[j], sep=":"))
}
}
}
if(qtl.only && length(int2test) > 0) {
z <- matrix(unlist(strsplit(int2test, ":")), ncol=2, byrow=TRUE)
wh <- apply(z, 1, function(a)
length(grep("^[Qq][0-9]+$", a)) )
int2test <- int2test[wh==2]
int2test.alt <- int2test.alt[wh==2]
}
n2test <- length(int2test)
if(n2test == 0) {
if(verbose) cat("No pairwise interactions to add.\n")
return(NULL)
}
sexpgm <- getsex(cross)
cross.attr <- attributes(cross)
# fit base model
thefit0 <- fitqtlengine(pheno=pheno, qtl=qtl, covar=covar, formula=formula,
method=method, model=model, dropone=FALSE, get.ests=FALSE,
run.checks=FALSE, cross.attr=cross.attr,
crosstype=crosstype(cross), sexpgm=sexpgm, tol=tol, maxit=maxit)
matrix0.rank <- attr(thefit0, "matrix.rank")
matrix0.ncol <- attr(thefit0, "matrix.ncol")
results <- matrix(ncol=7, nrow=n2test)
dimnames(results) <- list(int2test.alt, c("df", "Type III SS", "LOD", "%var",
"F value", "Pvalue(Chi2)", "Pvalue(F)"))
matrix1.rank <- matrix1.ncol <- rep(0, n2test)
for(k in seq(along=int2test)) {
thefit1 <- fitqtlengine(pheno=pheno, qtl=qtl, covar=covar,
formula=as.formula(paste(deparseQTLformula(formula), int2test[k], sep="+")),
method=method, model=model, dropone=FALSE, get.ests=FALSE,
run.checks=FALSE, cross.attr=cross.attr,
crosstype=crosstype(cross), sexpgm=sexpgm,
tol=tol, maxit=maxit)
results[k,1] <- thefit1$result.full[1,1] - thefit0$result.full[1,1]
results[k,2] <- thefit1$result.full[1,2] - thefit0$result.full[1,2]
results[k,3] <- thefit1$result.full[1,4] - thefit0$result.full[1,4]
results[k,4] <- 100*(1-10^(-2*thefit1$result.full[1,4]/qtl$n.ind)) -
100*(1-10^(-2*thefit0$result.full[1,4]/qtl$n.ind))
results[k,5] <- (results[k,2]/results[k,1])/thefit1$result.full[2,3]
results[k,6] <- pchisq(results[k,3]*2*log(10), results[k,1], lower.tail=FALSE)
results[k,7] <- pf(results[k,5], results[k,1], thefit1$result.full[3,1], lower.tail=FALSE)
matrix1.rank[k] <- attr(thefit1, "matrix.rank")
matrix1.ncol[k] <- attr(thefit1, "matrix.ncol")
}
matrix.fullrank <- (matrix1.rank - matrix0.rank == matrix1.ncol - matrix0.ncol)
results <- as.data.frame(results, stringsAsFactors=TRUE)
class(results) <- c("addint", "data.frame")
attr(results, "method") <- method
attr(results, "model") <- model
attr(results, "formula") <- deparseQTLformula(formula)
if(simple) pvalues <- FALSE
attr(results, "pvalues") <- pvalues
attr(results, "simple") <- simple
attr(results, "matrix.fullrank") <- matrix.fullrank
if(require.fullrank) results[!matrix.fullrank,3] <- 0
results
}
print.addint <-
function(x, ...)
{
meth <- attr(x, "method")
mod <- attr(x, "model")
simp <- attr(x, "simple")
if(is.null(mod)) mod <- "normal"
if(is.null(meth)) meth <- "unknown"
if(mod=="binary" || simp) attr(x, "pvalues") <- FALSE
if(meth=="imp") meth <- "multiple imputation"
else if(meth=="hk") meth <- "Haley-Knott regression"
cat("Method:", meth, "\n")
cat("Model: ", mod, "phenotype\n")
cat("Model formula:")
w <- options("width")[[1]]
printQTLformulanicely(attr(x, "formula"), " ", w+5, w)
cat("\n")
cat("Add one pairwise interaction at a time table:\n")
cat("--------------------------------------------\n")
pval <- attr(x, "pvalues")
if(!is.null(pval) && !pval)
x <- x[,-ncol(x)+(0:1)]
if(mod == "binary" || simp) x <- x[,c(1,3,4), drop=FALSE]
printCoefmat(x, digits=4, cs.ind=1, P.values=pval, has.Pvalue=pval)
cat("\n")
}
summary.addint <- function(object, ...) object
######################################################################
# addqtl
#
# scan for an additional QTL in the context of a multiple-QTL model
#
# If the formula includes one more QTL than in the QTL object, we
# use it as given; otherwise, a main effect for the additional QTL
# is added
#
# the output is like scanone
######################################################################
addqtl <-
function(cross, chr, pheno.col=1, qtl, covar=NULL, formula,
method=c("imp","hk"), model=c("normal", "binary"),
incl.markers=TRUE, verbose=FALSE, tol=1e-4, maxit=1000,
forceXcovar=FALSE, require.fullrank=FALSE)
{
method <- match.arg(method)
model <- match.arg(model)
if( !inherits(cross, "cross") )
stop("The cross argument must be an object of class \"cross\".")
if( !inherits(qtl, "qtl") )
stop("The qtl argument must be an object of class \"qtl\".")
# allow formula to be a character string
if(!missing(formula) && is.character(formula))
formula <- as.formula(formula)
if(!is.null(covar) && !is.data.frame(covar)) {
if(is.matrix(covar) && is.numeric(covar))
covar <- as.data.frame(covar, stringsAsFactors=TRUE)
else stop("covar should be a data.frame")
}
if(LikePheVector(pheno.col, nind(cross), nphe(cross))) {
cross$pheno <- cbind(pheno.col, cross$pheno)
pheno.col <- 1
}
if(method=="imp") {
if(!("geno" %in% names(qtl))) {
if("prob" %in% names(qtl)) {
warning("The qtl object doesn't contain imputations; using method=\"hk\".")
method <- "hk"
}
else
stop("The qtl object needs to be created with makeqtl with what=\"draws\".")
}
}
else {
if(!("prob" %in% names(qtl))) {
if("geno" %in% names(qtl)) {
warning("The qtl object doesn't contain QTL genotype probabilities; using method=\"imp\".")
method <- "imp"
}
else
stop("The qtl object needs to be created with makeqtl with what=\"prob\".")
}
}
if(verbose > 1) {
verbose <- TRUE
verbose.scanqtl <- TRUE
}
else verbose.scanqtl <- FALSE
n.qtl <- qtl$n.qtl
qtlchr <- qtl$chr
qtlpos <- qtl$pos
if(qtl$n.ind != nind(cross)) {
warning("No. individuals in qtl object doesn't match that in the input cross; re-creating qtl object.")
if(method=="imp")
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="draws")
else
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="prob")
}
if(method=="imp" && dim(qtl$geno)[3] != dim(cross$geno[[1]]$draws)[3]) {
warning("No. imputations in qtl object doesn't match that in the input cross; re-creating qtl object.")
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="draws")
}
if(method=="imp") {
if("stepwidth" %in% names(attributes(cross$geno[[1]]$draws)) &&
attr(cross$geno[[1]]$draws, "stepwidth") != "fixed") {
stepwidth.var <- TRUE
incl.markers <- TRUE
}
else stepwidth.var <- FALSE
}
else {
if("stepwidth" %in% names(attributes(cross$geno[[1]]$prob)) &&
attr(cross$geno[[1]]$prob, "stepwidth") != "fixed") {
stepwidth.var <- TRUE
incl.markers <- TRUE
}
else stepwidth.var <- FALSE
}
# look for the chr
if(missing(chr)) chr <- names(cross$geno)
else chr <- matchchr(chr, names(cross$geno))
# if formula is missing, make one.
# All QTLs and covariates will be additive by default
if(is.null(covar)) n.covar <- 0
else n.covar <- ncol(covar)
if(missing(formula)) {
tmp.Q <- paste("Q", 1:n.qtl, sep="") # QTL term names
formula <- "y~Q1"
if(n.qtl > 1)
for (i in 2:n.qtl)
formula <- paste(formula, tmp.Q[i], sep="+")
if(n.covar) { # if covariate is not empty
tmp.C <- names(covar) # covariate term names
for(i in 1:n.covar)
formula <- paste(formula, tmp.C[i], sep="+")
}
newformula <- as.formula(paste(formula, "+Q", n.qtl+1, sep=""))
formula <- as.formula(formula)
}
else { # formula given
newqtl <- paste("Q", n.qtl+1, sep="")
# check the formula
formula <- checkformula(formula, c(qtl$altname, newqtl), colnames(covar))
theterms <- rownames(attr(terms(formula), "factors"))
# is new QTL in the formula?
g <- grep(paste("^[Qq]", n.qtl+1, "$", sep=""), theterms)
if(length(g) == 0) { # no; add to formula
newformula <- as.formula(paste(deparseQTLformula(formula), "+ Q", n.qtl+1, sep=""))
}
else { # need a version without it
newformula <- formula
theterms <- colnames(attr(terms(formula), "factors"))
g <- unique(c(grep(paste("^[Qq]", n.qtl+1, "$", sep=""), theterms),
grep(paste("^[Qq]", n.qtl+1, " *:", sep=""), theterms),
grep(paste(": +[Qq]", n.qtl+1, " *:", sep=""), theterms),
grep(paste(": +[Qq]", n.qtl+1, "$", sep=""), theterms)))
if(length(g) > 0) {
theterms <- theterms[-g]
formula <- as.formula(paste("y ~ ", paste(theterms, collapse=" + "), sep=""))
}
}
}
# drop qtl that are not in the formula
thefactors <- rownames(attr(terms(formula), "factors"))
todrop <- NULL
for(i in 1:n.qtl) {
if(length(grep(paste("^[Qq]", i, "$", sep=""), thefactors))==0)
todrop <- c(todrop, i)
}
if(length(todrop) > 0) {
newqtlnum <- n.qtl+1
notdropped <- (1:n.qtl)[-todrop]
newnum <- 1:length(notdropped)
qtl <- dropfromqtl(qtl, index=todrop)
qtlchr <- qtlchr[-todrop]
qtlpos <- qtlpos[-todrop]
n.qtl <- n.qtl - length(todrop)
revnewqtlnum <- n.qtl+1
formula <- reviseqtlnuminformula(formula, notdropped, newnum)
newformula <- reviseqtlnuminformula(newformula, c(notdropped,newqtlnum),
c(newnum, revnewqtlnum))
}
# drop covariates that are not in the formula
if(!is.null(covar)) {
theterms <- rownames(attr(terms(formula), "factors"))
m <- match(colnames(covar), theterms)
if(all(is.na(m))) covar <- NULL
else covar <- covar[,!is.na(m),drop=FALSE]
}
# phenotype column
if(length(pheno.col) > 1) {
pheno.col <- pheno.col[1]
warning("addqtl can take just one phenotype; only the first will be used")
}
if(is.character(pheno.col)) {
num <- find.pheno(cross, pheno.col)
if(is.na(num))
stop("Couldn't identify phenotype \"", pheno.col, "\"")
pheno.col <- num
}
if(pheno.col < 1 || pheno.col > nphe(cross))
stop("pheno.col should be between 1 and ", nphe(cross))
pheno <- cross$pheno[,pheno.col]
if(!is.null(covar) && nrow(covar) != length(pheno))
stop("nrow(covar) != no. individuals in cross.")
# check phenotypes and covariates; drop ind'ls with missing values
if(!is.null(covar)) phcovar <- cbind(pheno, covar)
else phcovar <- as.data.frame(pheno, stringsAsFactors=TRUE)
if(any(is.na(phcovar))) {
if(ncol(phcovar)==1) hasmissing <- is.na(phcovar)
else hasmissing <- apply(phcovar, 1, function(a) any(is.na(a)))
if(all(hasmissing))
stop("All individuals are missing phenotypes or covariates.")
if(any(hasmissing)) {
warning("Dropping ", sum(hasmissing), " individuals with missing phenotypes.\n")
pheno <- pheno[!hasmissing]
qtl$n.ind <- sum(!hasmissing)
if(method=="imp")
qtl$geno <- qtl$geno[!hasmissing,,,drop=FALSE]
else
qtl$prob <- lapply(qtl$prob, function(a) a[!hasmissing,,drop=FALSE])
if(!is.null(covar)) covar <- covar[!hasmissing,,drop=FALSE]
cross <- subset(cross, ind=!hasmissing)
}
}
sexpgm <- getsex(cross)
cross.attr <- attributes(cross)
# fit the base model
fit0 <- fitqtlengine(pheno=pheno, qtl=qtl, covar=covar, formula=formula,
method=method, model=model, dropone=FALSE, get.ests=FALSE,
run.checks=FALSE, cross.attr=cross.attr,
crosstype=crosstype(cross), sexpgm=sexpgm,
tol=tol, maxit=maxit, forceXcovar=forceXcovar)
lod0 <- fit0$result.full[1,4]
matrix0.rank <- attr(fit0, "matrix.rank")
matrix0.ncol <- attr(fit0, "matrix.ncol")
results <- matrix1.rank <- matrix1.ncol <- NULL
for(i in chr) {
if(verbose) cat("Scanning chr", i, "\n")
thechr <- c(qtlchr, i)
thepos <- c(as.list(qtlpos), list(c(-Inf,Inf)))
sqout <- scanqtl(cross, pheno.col=pheno.col, chr=thechr, pos=thepos,
covar=covar, formula=newformula, method=method, model=model,
incl.markers=incl.markers, verbose=verbose.scanqtl,
tol=tol, maxit=maxit, forceXcovar=forceXcovar)
matrix1.rank <- c(matrix1.rank, attr(sqout, "matrix.rank"))
matrix1.ncol <- c(matrix1.ncol, attr(sqout, "matrix.ncol"))
# get map of positions
if(method=="imp") {
if("map" %in% names(attributes(cross$geno[[i]]$draws)))
map <- attr(cross$geno[[i]]$draws,"map")
else {
stp <- attr(cross$geno[[i]]$draws, "step")
oe <- attr(cross$geno[[i]]$draws, "off.end")
if("stepwidth" %in% names(attributes(cross$geno[[i]]$draws)))
stpw <- attr(cross$geno[[i]]$draws, "stepwidth")
else stpw <- "fixed"
map <- create.map(cross$geno[[i]]$map,stp,oe,stpw)
}
}
else {
if("map" %in% names(attributes(cross$geno[[i]]$prob)))
map <- attr(cross$geno[[i]]$prob,"map")
else {
stp <- attr(cross$geno[[i]]$prob, "step")
oe <- attr(cross$geno[[i]]$prob, "off.end")
if("stepwidth" %in% names(attributes(cross$geno[[i]]$prob)))
stpw <- attr(cross$geno[[i]]$prob, "stepwidth")
else stpw <- "fixed"
map <- create.map(cross$geno[[i]]$map,stp,oe,stpw)
}
}
# pull out the female map if there are sex-specific maps
if(is.matrix(map)) map <- map[1,]
if(method=="imp")
step <- attr(cross$geno[[i]]$draws,"step")
else
step <- attr(cross$geno[[i]]$prob,"step")
if(!incl.markers && step>0) { # equally spaced positions
eq.sp.pos <- seq(min(map), max(map), by=step)
wh.eq.pos <- match(eq.sp.pos, map)
map <- map[wh.eq.pos]
}
w <- names(map)
o <- grep("^loc-*[0-9]+",w)
if(length(o) > 0) # inter-marker locations cited as "c*.loc*"
w[o] <- paste("c",i,".",w[o],sep="")
z <- data.frame(lod=as.numeric(sqout)-lod0, stringsAsFactors=TRUE)
z <- cbind(chr=factor(rep(i,length(map)),levels=i),
pos=as.numeric(map), z)
rownames(z) <- w
results <- rbind(results, z)
}
matrix.fullrank <- (matrix1.rank - matrix0.rank == matrix1.ncol - matrix0.ncol)
class(results) <- c("scanone","data.frame")
attr(results,"method") <- method
attr(results,"formula") <- deparseQTLformula(newformula)
attr(results, "matrix.fullrank") <- matrix.fullrank
if(require.fullrank) results[!matrix.fullrank,3] <- 0
results
}
######################################################################
# addpair
#
# scan for an additional pair of QTL in the context of a multiple-QTL
# model
#
# If the formula includes one more QTL than in the QTL object, we
# use it as given (perhaps adding the second, additively);
# otherwise, we do as in scantwo, performing both additive and
# interactive models, plus a single-QTL scan
#
# The output is like scantwo. If we didn't do the scantwo type format,
# the results are placed where the full LODs usually are, and everything
# else is NA
######################################################################
addpair <-
function(cross, chr, pheno.col=1, qtl, covar=NULL, formula,
method=c("imp","hk"), model=c("normal", "binary"),
incl.markers=FALSE, verbose=TRUE, tol=1e-4, maxit=1000,
forceXcovar=FALSE)
{
method <- match.arg(method)
model <- match.arg(model)
if( !inherits(cross, "cross") )
stop("The cross argument must be an object of class \"cross\".")
if( !inherits(qtl, "qtl") )
stop("The qtl argument must be an object of class \"qtl\".")
# allow formula to be a character string
if(!missing(formula) && is.character(formula))
formula <- as.formula(formula)
if(!is.null(covar) && !is.data.frame(covar)) {
if(is.matrix(covar) && is.numeric(covar))
covar <- as.data.frame(covar, stringsAsFactors=TRUE)
else stop("covar should be a data.frame")
}
if(LikePheVector(pheno.col, nind(cross), nphe(cross))) {
cross$pheno <- cbind(pheno.col, cross$pheno)
pheno.col <- 1
}
if(method=="imp") {
if(!("geno" %in% names(qtl))) {
if("prob" %in% names(qtl)) {
warning("The qtl object doesn't contain imputations; using method=\"hk\".")
method <- "hk"
}
else
stop("The qtl object needs to be created with makeqtl with what=\"draws\".")
}
}
else {
if(!("prob" %in% names(qtl))) {
if("geno" %in% names(qtl)) {
warning("The qtl object doesn't contain QTL genotype probabilities; using method=\"imp\".")
method <- "imp"
}
else
stop("The qtl object needs to be created with makeqtl with what=\"prob\".")
}
}
if(verbose > 1) {
verbose <- TRUE
verbose.scanqtl <- TRUE
}
else verbose.scanqtl <- FALSE
n.qtl <- qtl$n.qtl
qtlchr <- qtl$chr
qtlpos <- qtl$pos
if(qtl$n.ind != nind(cross)) {
warning("No. individuals in qtl object doesn't match that in the input cross; re-creating qtl object.")
if(method=="imp")
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="draws")
else
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="prob")
}
if(method=="imp" && dim(qtl$geno)[3] != dim(cross$geno[[1]]$draws)[3]) {
warning("No. imputations in qtl object doesn't match that in the input cross; re-creating qtl object.")
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="draws")
}
if(method=="imp") {
if("stepwidth" %in% names(attributes(cross$geno[[1]]$draws)) &&
attr(cross$geno[[1]]$draws, "stepwidth") != "fixed") {
stepwidth.var <- TRUE
incl.markers <- TRUE
}
else stepwidth.var <- FALSE
}
else {
if("stepwidth" %in% names(attributes(cross$geno[[1]]$prob)) &&
attr(cross$geno[[1]]$prob, "stepwidth") != "fixed") {
stepwidth.var <- TRUE
incl.markers <- TRUE
}
else stepwidth.var <- FALSE
}
# look for the chr
if(missing(chr)) chr <- names(cross$geno)
else chr <- matchchr(chr, names(cross$geno))
fullmap <- pull.map(cross, chr)
# if formula is missing, make one.
# All QTLs and covariates will be additive by default
if(is.null(covar)) n.covar <- 0
else n.covar <- ncol(covar)
if(missing(formula)) {
tmp.Q <- paste("Q", 1:n.qtl, sep="") # QTL term names
formula <- "y~Q1"
if(n.qtl > 1)
for (i in 2:n.qtl)
formula <- paste(formula, tmp.Q[i], sep="+")
if(n.covar) { # if covariate is not empty
tmp.C <- names(covar) # covariate term names
for(i in 1:n.covar)
formula <- paste(formula, tmp.C[i], sep="+")
}
newformula1 <- as.formula(paste(formula, " + Q", n.qtl+1,
" + Q", n.qtl+2, " + Q", n.qtl+1,
":Q", n.qtl+2, sep=""))
newformula2 <- as.formula(paste(formula, " + Q", n.qtl+1,
" + Q", n.qtl+2, sep=""))
formula <- as.formula(formula)
scanbothways <- FALSE
}
else { # formula given
newqtl <- paste("Q", n.qtl+1:2, sep="")
# check the formula
formula <- checkformula(formula, c(qtl$altname, newqtl), colnames(covar))
theterms <- rownames(attr(terms(formula), "factors"))
# are either of the new QTL in the formula?
g <- c(grep(paste("^[Qq]", n.qtl+1, "$", sep=""), theterms),
grep(paste("^[Qq]", n.qtl+2, "$", sep=""), theterms))
g1 <- grep(paste("^[Qq]", n.qtl+1, "$", sep=""), theterms)
g2 <- grep(paste("^[Qq]", n.qtl+2, "$", sep=""), theterms)
if(length(g) == 0) { # no; add to formula
newformula1 <- as.formula(paste(deparseQTLformula(formula), "+ Q", n.qtl+1,
" + Q", n.qtl+2, " + Q", n.qtl+1,
":Q", n.qtl+2, sep=""))
newformula2 <- as.formula(paste(deparseQTLformula(formula), "+ Q", n.qtl+1,
" + Q", n.qtl+2, sep=""))
scanbothways <- FALSE
}
else { # need a version without them
# first make sure that *both* terms are in the formula
if(length(g1)==0) # add Q1
formula <- as.formula(paste(deparseQTLformula(formula), "+ Q", n.qtl+1, sep=""))
if(length(g2)==0) # add Q2
formula <- as.formula(paste(deparseQTLformula(formula), "+ Q", n.qtl+2, sep=""))
newformula1 <- formula
newformula2 <- NULL
theterms <- colnames(attr(terms(formula), "factors"))
g <- unique(c(grep(paste("^[Qq]", n.qtl+1, "$", sep=""), theterms),
grep(paste("^[Qq]", n.qtl+1, " *:", sep=""), theterms),
grep(paste(": *[Qq]", n.qtl+1, " *:", sep=""), theterms),
grep(paste(": *[Qq]", n.qtl+1, "$", sep=""), theterms),
grep(paste("^[Qq]", n.qtl+2, "$", sep=""), theterms),
grep(paste("^[Qq]", n.qtl+2, " *:", sep=""), theterms),
grep(paste(": *[Qq]", n.qtl+2, " *:", sep=""), theterms),
grep(paste(": *[Qq]", n.qtl+2, "$", sep=""), theterms)))
if(length(g) > 0) {
theterms <- theterms[-g]
formula <- as.formula(paste("y ~ ", paste(theterms, collapse=" + "), sep=""))
}
# if the QTL formula is symmetric in the two new QTL, need scan only for i<j
# if not symmetric, we also need to scan with j<i
scanbothways <- !qtlformulasymmetric(newformula1, n.qtl+1, n.qtl+2)
if(scanbothways) {
newformula1.minus1 <- dropfromqtlformula(newformula1, n.qtl+1)
newformula1.minus2 <- dropfromqtlformula(newformula1, n.qtl+2)
newformula1.minus1 <- reviseqtlnuminformula(newformula1.minus1, n.qtl+2, n.qtl+1)
}
}
}
# drop qtl that are not in the formula
thefactors <- rownames(attr(terms(formula), "factors"))
todrop <- NULL
for(i in 1:n.qtl) {
if(length(grep(paste("^[Qq]", i, "$", sep=""), thefactors))==0)
todrop <- c(todrop, i)
}
if(length(todrop) > 0) {
newqtlnum1 <- n.qtl+1
newqtlnum2 <- n.qtl+2
notdropped <- (1:n.qtl)[-todrop]
newnum <- 1:length(notdropped)
qtl <- dropfromqtl(qtl, index=todrop)
qtlchr <- qtlchr[-todrop]
qtlpos <- qtlpos[-todrop]
n.qtl <- n.qtl - length(todrop)
revnewqtlnum1 <- n.qtl+1
revnewqtlnum2 <- n.qtl+2
formula <- reviseqtlnuminformula(formula, notdropped, newnum)
newformula1 <- reviseqtlnuminformula(newformula1, c(notdropped, newqtlnum1, newqtlnum2),
c(newnum, revnewqtlnum1, revnewqtlnum2))
if(!is.null(newformula2))
newformula2 <- reviseqtlnuminformula(newformula2, c(notdropped, newqtlnum1, newqtlnum2),
c(newnum, revnewqtlnum1, revnewqtlnum2))
if(scanbothways) {
newformula1.minus1 <- reviseqtlnuminformula(newformula1.minus1,
c(notdropped, newqtlnum1),
c(newnum, revnewqtlnum1))
newformula1.minus2 <- reviseqtlnuminformula(newformula1.minus2,
c(notdropped, newqtlnum1),
c(newnum, revnewqtlnum1))
}
}
# drop covariates that are not in the formula
if(!is.null(covar)) {
theterms <- rownames(attr(terms(formula), "factors"))
m <- match(colnames(covar), theterms)
if(all(is.na(m))) covar <- NULL
else covar <- covar[,!is.na(m),drop=FALSE]
}
# phenotype column
if(length(pheno.col) > 1) {
pheno.col <- pheno.col[1]
warning("addpair can take just one phenotype; only the first will be used")
}
if(is.character(pheno.col)) {
num <- find.pheno(cross, pheno.col)
if(is.na(num))
stop("Couldn't identify phenotype \"", pheno.col, "\"")
pheno.col <- num
}
if(pheno.col < 1 || pheno.col > nphe(cross))
stop("pheno.col should be between 1 and ", nphe(cross))
pheno <- cross$pheno[,pheno.col]
if(!is.null(covar) && nrow(covar) != length(pheno))
stop("nrow(covar) != no. individuals in cross.")
# check phenotypes and covariates; drop ind'ls with missing values
if(!is.null(covar)) phcovar <- cbind(pheno, covar)
else phcovar <- as.data.frame(pheno, stringsAsFactors=TRUE)
if(any(is.na(phcovar))) {
if(ncol(phcovar)==1) hasmissing <- is.na(phcovar)
else hasmissing <- apply(phcovar, 1, function(a) any(is.na(a)))
if(all(hasmissing))
stop("All individuals are missing phenotypes or covariates.")
if(any(hasmissing)) {
warning("Dropping ", sum(hasmissing), " individuals with missing phenotypes.\n")
pheno <- pheno[!hasmissing]
qtl$n.ind <- sum(!hasmissing)
if(method=="imp")
qtl$geno <- qtl$geno[!hasmissing,,,drop=FALSE]
else
qtl$prob <- lapply(qtl$prob, function(a) a[!hasmissing,,drop=FALSE])
if(!is.null(covar)) covar <- covar[!hasmissing,,drop=FALSE]
cross <- subset(cross, ind=!hasmissing)
}
}
sexpgm <- getsex(cross)
cross.attr <- attributes(cross)
# fit the base model
fit0 <- fitqtlengine(pheno=pheno, qtl=qtl, covar=covar, formula=formula,
method=method, model=model, dropone=FALSE, get.ests=FALSE,
run.checks=FALSE, cross.attr=cross.attr,
crosstype=crosstype(cross), sexpgm=sexpgm,
tol=tol, maxit=maxit, forceXcovar=forceXcovar)
lod0 <- fit0$result.full[1,4]
gmap <- NULL
# form map
for(i in 1:length(chr)) {
ci <- chr[i]
# get map of positions
if(method=="imp") {
if("map" %in% names(attributes(cross$geno[[ci]]$draws)))
map <- attr(cross$geno[[ci]]$draws,"map")
else {
stp <- attr(cross$geno[[ci]]$draws, "step")
oe <- attr(cross$geno[[ci]]$draws, "off.end")
if("stepwidth" %in% names(attributes(cross$geno[[ci]]$draws)))
stpw <- attr(cross$geno[[ci]]$draws, "stepwidth")
else stpw <- "fixed"
map <- create.map(cross$geno[[ci]]$map,stp,oe,stpw)
}
}
else {
if("map" %in% names(attributes(cross$geno[[ci]]$prob)))
map <- attr(cross$geno[[ci]]$prob,"map")
else {
stp <- attr(cross$geno[[ci]]$prob, "step")
oe <- attr(cross$geno[[ci]]$prob, "off.end")
if("stepwidth" %in% names(attributes(cross$geno[[ci]]$prob)))
stpw <- attr(cross$geno[[ci]]$prob, "stepwidth")
else stpw <- "fixed"
map <- create.map(cross$geno[[ci]]$map,stp,oe,stpw)
}
}
# pull out the female map if there are sex-specific maps
if(is.matrix(map)) map <- map[1,]
w <- names(map)
o <- grep("^loc-*[0-9]+",w)
if(length(o) > 0) # inter-marker locations cited as "c*.loc*"
w[o] <- paste("c",ci,".",w[o],sep="")
map <- data.frame(chr=factor(rep(ci,length(map)),levels=ci),
pos=as.numeric(map), stringsAsFactors=TRUE)
rownames(map) <- w
if(method=="imp")
step <- attr(cross$geno[[ci]]$draws,"step")
else
step <- attr(cross$geno[[ci]]$prob,"step")
if(step==0 || stepwidth.var) # just use markers
eq.sp.pos <- rep(1,nrow(map))
else {
eq.sp.pos <- seq(min(map[,2]),max(map[,2]),by=step)
wh.eq.sp <- match(eq.sp.pos,map[,2])
if(any(is.na(wh.eq.sp))) { # this shouldn't happen
warning("Possible error in determining the equally spaced positions.")
wh.eq.sp <- wh.eq.sp[!is.na(wh.eq.sp)]
}
eq.sp.pos <- rep(0,nrow(map))
eq.sp.pos[wh.eq.sp] <- 1
}
if(!incl.markers && any(eq.sp.pos==0)) {
map <- map[eq.sp.pos==1,]
eq.sp.pos <- eq.sp.pos[eq.sp.pos==1]
}
gmap <- rbind(gmap, cbind(map,
eq.spacing=eq.sp.pos,
xchr=inherits(cross$geno[[i]], "X")))
}
lod <- matrix(ncol=nrow(gmap), nrow=nrow(gmap))
if(scanbothways) lod.m1 <- lod.m2 <- rep(NA, nrow(gmap))
for(i in 1:length(chr)) {
ci <- chr[i]
whi <- which(gmap[,1]==ci)
for(j in i:length(chr)) {
cj <- chr[j]
whj <- which(gmap[,1]==cj)
if(verbose) {
if(is.null(newformula2))
cat("Scanning chr", ci, "and", cj, "\n")
else
cat("Scanning full model for chr", ci, "and", cj, "\n")
}
thechr <- c(qtlchr, ci, cj)
thepos <- c(as.list(qtlpos), list(c(-Inf, Inf)), list(c(-Inf, Inf)))
temp1 <- scanqtl(cross, pheno.col=pheno.col, chr=thechr, pos=thepos,
covar=covar, formula=newformula1, method=method, model=model,
incl.markers=incl.markers, verbose=verbose.scanqtl,
tol=tol, maxit=maxit, forceXcovar=forceXcovar) - lod0
if(!is.null(newformula2)) {
if(verbose)
cat("Scanning add've model for chr", ci, "and", cj, "\n")
temp2 <- scanqtl(cross, pheno.col=pheno.col, chr=thechr, pos=thepos,
covar=covar, formula=newformula2, method=method, model=model,
incl.markers=incl.markers, verbose=verbose.scanqtl,
tol=tol, maxit=maxit, forceXcovar=forceXcovar) - lod0
}
else {
if(i != j && scanbothways) {
if(verbose) cat("Scanning chr", cj, "and", ci, "\n")
thechr <- c(qtlchr, cj, ci)
temp1r <- scanqtl(cross, pheno.col=pheno.col, chr=thechr, pos=thepos,
covar=covar, formula=newformula1, method=method, model=model,
incl.markers=incl.markers, verbose=verbose.scanqtl,
tol=tol, maxit=maxit, forceXcovar=forceXcovar) - lod0
}
}
if(i==j) {
if(!is.null(newformula2))
temp1[upper.tri(temp1)] <- temp2[upper.tri(temp1)]
else temp1 <- t(temp1)
lod[whi,whi] <- temp1
}
else {
if(!is.null(newformula2)) {
lod[whi,whj] <- t(temp2)
lod[whj,whi] <- temp1
}
else {
lod[whi,whj] <- t(temp1)
if(scanbothways)
lod[whj,whi] <- t(temp1r)
else
lod[whj,whi] <- temp1
}
}
}
if(scanbothways) {
if(verbose) cat("Scanning chr", ci, "alone\n")
thechr <- c(qtlchr, ci)
thepos <- c(as.list(qtlpos), list(c(-Inf, Inf)))
lod.m1[whi] <- scanqtl(cross, pheno.col=pheno.col, chr=thechr, pos=thepos,
covar=covar, formula=newformula1.minus1, method=method,
model=model, incl.markers=incl.markers,
verbose=verbose.scanqtl, tol=tol, maxit=maxit, forceXcovar=forceXcovar) - lod0
lod.m2[whi] <- scanqtl(cross, pheno.col=pheno.col, chr=thechr, pos=thepos,
covar=covar, formula=newformula1.minus2, method=method,
model=model, incl.markers=incl.markers,
verbose=verbose.scanqtl, tol=tol, maxit=maxit, forceXcovar=forceXcovar) - lod0
}
}
result <- list(lod=lod, map=gmap, scanoneX=NULL)
class(result) <- "scantwo"
attr(result, "fullmap") <- fullmap
attr(result,"method") <- method
attr(result,"formula") <- deparseQTLformula(newformula1)
if(scanbothways) {
attr(result, "lod.minus1") <- lod.m1
attr(result, "lod.minus2") <- lod.m2
}
if(is.null(newformula2)) attr(result, "addpair") <- TRUE
result
}
######################################################################
# consider, e.g., oldnum=5 and newnum = 3
# This functions replaces any instances of "Q5" or "q5" in the
# formula with "Q3".
######################################################################
reviseqtlnuminformula <-
function(formula, oldnum, newnum)
{
if(is.character(formula))
formula <- as.formula(formula)
if(length(oldnum) != length(newnum))
stop("oldnum and newnum must be the same length.")
newterms <- theterms <- colnames(attr(terms(formula), "factors"))
for(i in seq(along=oldnum)) {
g <- grep(paste("^[Qq]", oldnum[i], "$", sep=""), theterms)
if(length(g) > 0)
newterms[g] <- paste("Q", newnum[i], sep="")
}
intxn <- grep(":", theterms)
if(length(intxn) > 0) {
temp <- strsplit(theterms[intxn], ":")
for(i in seq(along=temp)) {
for(j in seq(along=oldnum)) {
g <- grep(paste("^[Qq]", oldnum[j], "$", sep=""), temp[[i]])
if(length(g) > 0)
temp[[i]][g] <- paste("Q", newnum[j], sep="")
}
}
temp <- sapply(temp, paste, collapse=":")
newterms[intxn] <- temp
}
as.formula(paste("y ~ ", paste(newterms, collapse=" + "), sep=""))
}
######################################################################
# return TRUE or FALSE according to whether the formula is symmetric
# in QTL qtlnum1 and qtlnum2
######################################################################
qtlformulasymmetric <-
function(formula, qtlnum1, qtlnum2)
{
theterms <- attr(terms(formula), "factors")
rn <- rownames(theterms)
wh1 <- grep(paste("^[Qq]", qtlnum1, "$", sep=""), rn)
wh2 <- grep(paste("^[Qq]", qtlnum2, "$", sep=""), rn)
cn <- colnames(theterms)
if(length(wh1)==0 && length(wh2)==0) return(TRUE)
if(length(wh1)==0 || length(wh2)==0) return(FALSE)
revterms <- theterms
revterms[c(wh1,wh2),] <- revterms[c(wh2, wh1),]
theterms <- sort(apply(theterms, 2, paste, collapse=""))
revterms <- sort(apply(revterms, 2, paste, collapse=""))
all(theterms==revterms)
}
######################################################################
# If qtlnum=5, drop any terms containing Q5 or q5
######################################################################
dropfromqtlformula <-
function(formula, qtlnum)
{
theterms <- colnames(attr(terms(formula), "factors"))
todrop <- NULL
for(i in seq(along=qtlnum)) {
g <- grep(paste("^[Qq]", qtlnum[i], "$", sep=""), theterms)
if(length(g) > 0) todrop <- c(todrop, g)
}
intxn <- grep(":", theterms)
if(length(intxn) > 0) {
temp <- strsplit(theterms[intxn], ":")
for(i in seq(along=temp)) {
for(j in seq(along=qtlnum)) {
g <- grep(paste("^[Qq]", qtlnum[j], "$", sep=""), temp[[i]])
if(length(g) > 0) todrop <- c(todrop, intxn[i])
}
}
}
todrop <- unique(todrop)
as.formula(paste("y ~ ", paste(theterms[-todrop], collapse=" + "), sep=""))
}
######################################################################
# addcovarint
#
# Try adding each QTL x covariate interaction (that is not
# already in the formula), and give results similar to the drop-one
# analysis.
######################################################################
addcovarint <-
function(cross, pheno.col=1, qtl, covar=NULL, icovar, formula,
method=c("imp","hk"), model=c("normal", "binary"),
verbose=TRUE, pvalues=TRUE, simple=FALSE, tol=1e-4, maxit=1000,
require.fullrank=FALSE)
{
if( !inherits(cross, "cross"))
stop("The cross argument must be an object of class \"cross\".")
if( !inherits(qtl, "qtl"))
stop("The qtl argument must be an object of class \"qtl\".")
if(missing(covar) || is.null(covar))
stop("Must include covariate data frame.")
if(!is.data.frame(covar)) {
if(is.matrix(covar) && is.numeric(covar))
covar <- as.data.frame(covar, stringsAsFactors=TRUE)
else stop("covar should be a data.frame")
}
if(LikePheVector(pheno.col, nind(cross), nphe(cross))) {
cross$pheno <- cbind(pheno.col, cross$pheno)
pheno.col <- 1
}
if(length(pheno.col) > 1) {
pheno.col <- pheno.col[1]
warning("addcovarint can take just one phenotype; only the first will be used")
}
if(is.character(pheno.col)) {
num <- find.pheno(cross, pheno.col)
if(is.na(num))
stop("Couldn't identify phenotype \"", pheno.col, "\"")
pheno.col <- num
}
if(pheno.col < 1 | pheno.col > nphe(cross))
stop("pheno.col values should be between 1 and the no. phenotypes")
pheno <- cross$pheno[,pheno.col]
if(nrow(covar) != length(pheno))
stop("nrow(covar) != no. individuals in cross.")
if(missing(icovar))
stop("Must include icovar (the covariate to consider in interactions)")
if(!is.character(icovar) || any(is.na(match(icovar, colnames(covar)))))
stop("icovar must be a vector of character strings corresonding to columns in covar.")
method <- match.arg(method)
model <- match.arg(model)
# allow formula to be a character string
if(!missing(formula) && is.character(formula))
formula <- as.formula(formula)
if(method=="imp") {
if(!("geno" %in% names(qtl))) {
if("prob" %in% names(qtl)) {
warning("The qtl object doesn't contain imputations; using method=\"hk\".")
method <- "hk"
}
else
stop("The qtl object needs to be created with makeqtl with what=\"draws\".")
}
}
else {
if(!("prob" %in% names(qtl))) {
if("geno" %in% names(qtl)) {
warning("The qtl object doesn't contain QTL genotype probabilities; using method=\"imp\".")
method <- "imp"
}
else
stop("The qtl object needs to be created with makeqtl with what=\"prob\".")
}
}
if(qtl$n.ind != nind(cross)) {
warning("No. individuals in qtl object doesn't match that in the input cross; re-creating qtl object.")
if(method=="imp")
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="draws")
else
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="prob")
}
if(method=="imp" && dim(qtl$geno)[3] != dim(cross$geno[[1]]$draws)[3]) {
warning("No. imputations in qtl object doesn't match that in the input cross; re-creating qtl object.")
qtl <- makeqtl(cross, qtl$chr, qtl$pos, qtl$name, what="draws")
}
# check phenotypes and covariates; drop ind'ls with missing values
phcovar <- cbind(pheno, covar)
if(any(is.na(phcovar))) {
if(ncol(phcovar)==1) hasmissing <- is.na(phcovar)
else hasmissing <- apply(phcovar, 1, function(a) any(is.na(a)))
if(all(hasmissing))
stop("All individuals are missing phenotypes or covariates.")
if(any(hasmissing)) {
warning("Dropping ", sum(hasmissing), " individuals with missing phenotypes.\n")
pheno <- pheno[!hasmissing]
qtl$n.ind <- sum(!hasmissing)
if(method=="imp")
qtl$geno <- qtl$geno[!hasmissing,,,drop=FALSE]
else
qtl$prob <- lapply(qtl$prob, function(a) a[!hasmissing,,drop=FALSE])
covar <- covar[!hasmissing,,drop=FALSE]
cross <- subset(cross, ind=!hasmissing)
}
}
# number of covariates
n.covar <- ncol(covar)
# if formula is missing, build one
# all QTLs and covarariates will be additive by default
n.qtl <- qtl$n.qtl
if(missing(formula)) {
tmp.Q <- paste("Q", 1:n.qtl, sep="") # QTL term names
formula <- "y~Q1"
if(n.qtl > 1)
for (i in 2:n.qtl)
formula <- paste(formula, tmp.Q[i], sep="+")
if (n.covar) { # if covarariate is not empty
tmp.C <- colnames(covar) # covarariate term names
for(i in 1:n.covar)
formula <- paste(formula, tmp.C[i], sep="+")
}
formula <- as.formula(formula)
}
# check input formula
formula <- checkformula(formula, qtl$altname, colnames(covar))
# make sure icovar is in the formula
m <- is.na(match(icovar, rownames(attr(terms(formula), "factors"))))
if(any(m))
formula <- as.formula(paste(deparseQTLformula(formula), "+",
paste(icovar[m], collapse="+"), sep=""))
# look for interactions that haven't been added
factors <- attr(terms(formula), "factors")
if(sum(factors[1,])==0) factors <- factors[-1,]
# replace QTL altnames (Q1 etc) with real names (chr1@20 etc)
fn <- fn.alt <- rownames(factors)
qan <- qtl$altname
qn <- qtl$name
m <- match(fn, qan)
fn.alt[!is.na(m)] <- qn[m[!is.na(m)]]
theqtl <- fn[fn != fn.alt]
theqtl.alt <- fn.alt[fn != fn.alt]
theint <- theint.alt <- NULL
for(i in icovar) {
theint <- c(theint, paste(theqtl, ":", i, sep=""))
theint.alt <- c(theint.alt, paste(theqtl.alt, ":", i, sep=""))
}
wh <- match(theint, colnames(factors))
theint <- theint[is.na(wh)]
theint.alt <- theint.alt[is.na(wh)]
n2test <- length(theint)
if(n2test == 0) {
if(verbose) cat("No QTL x covariate interactions to add.\n")
return(NULL)
}
sexpgm <- getsex(cross)
cross.attr <- attributes(cross)
# fit base model
thefit0 <- fitqtlengine(pheno=pheno, qtl=qtl, covar=covar, formula=formula,
method=method, model=model, dropone=FALSE, get.ests=FALSE,
run.checks=FALSE, cross.attr=cross.attr,
crosstype=crosstype(cross), sexpgm=sexpgm,
tol=tol, maxit=maxit)
matrix0.rank <- attr(thefit0, "matrix.rank")
matrix0.ncol <- attr(thefit0, "matrix.ncol")
results <- matrix(ncol=7, nrow=n2test)
dimnames(results) <- list(theint.alt, c("df", "Type III SS", "LOD", "%var",
"F value", "Pvalue(Chi2)", "Pvalue(F)"))
matrix1.rank <- matrix1.ncol <- rep(0, n2test)
for(k in seq(along=theint)) {
thefit1 <- fitqtlengine(pheno=pheno, qtl=qtl, covar=covar,
formula=as.formula(paste(deparseQTLformula(formula), theint[k], sep="+")),
method=method, model=model, dropone=FALSE, get.ests=FALSE,
run.checks=FALSE, cross.attr=cross.attr,
crosstype=crosstype(cross), sexpgm=sexpgm,
tol=tol, maxit=maxit)
results[k,1] <- thefit1$result.full[1,1] - thefit0$result.full[1,1]
results[k,2] <- thefit1$result.full[1,2] - thefit0$result.full[1,2]
results[k,3] <- thefit1$result.full[1,4] - thefit0$result.full[1,4]
results[k,4] <- 100*(1-10^(-2*thefit1$result.full[1,4]/qtl$n.ind)) -
100*(1-10^(-2*thefit0$result.full[1,4]/qtl$n.ind))
results[k,5] <- (results[k,2]/results[k,1])/thefit1$result.full[2,3]
results[k,6] <- pchisq(results[k,3]*2*log(10), results[k,1], lower.tail=FALSE)
results[k,7] <- pf(results[k,5], results[k,1], thefit1$result.full[3,1], lower.tail=FALSE)
matrix1.rank[k] <- attr(thefit1, "matrix.rank")
matrix1.ncol[k] <- attr(thefit1, "matrix.ncol")
}
matrix.fullrank <- (matrix1.rank - matrix0.rank == matrix1.ncol - matrix0.ncol)
results <- as.data.frame(results, stringsAsFactors=TRUE)
class(results) <- c("addcovarint", "data.frame")
attr(results, "model") <- model
attr(results, "method") <- method
attr(results, "formula") <- deparseQTLformula(formula)
if(simple) pvalues <- FALSE
attr(results, "pvalues") <- pvalues
attr(results, "simple") <- simple
attr(results, "matrix.fullrank") <- matrix.fullrank
if(require.fullrank) results[!matrix.fullrank,3] <- 0
results
}
print.addcovarint <-
function(x, ...)
{
meth <- attr(x, "method")
mod <- attr(x, "model")
simp <- attr(x, "simple")
if(is.null(mod)) mod <- "normal"
if(is.null(meth)) meth <- "unknown"
if(mod=="binary" || simp) attr(x, "pvalues") <- FALSE
if(meth=="imp") meth <- "multiple imputation"
else if(meth=="hk") meth <- "Haley-Knott regression"
cat("Method:", meth, "\n")
cat("Model: ", mod, "phenotype\n")
cat("Model formula:")
w <- options("width")[[1]]
printQTLformulanicely(attr(x, "formula"), " ", w+5, w)
cat("\n")
cat("Add one QTL x covar interaction at a time table:\n")
cat("--------------------------------------------\n")
pval <- attr(x, "pvalues")
if(!is.null(pval) && !pval)
x <- x[,-ncol(x)+(0:1)]
if(mod == "binary" || simp) x <- x[,c(1,3,4), drop=FALSE]
printCoefmat(x, digits=4, cs.ind=1, P.values=pval, has.Pvalue=pval)
cat("\n")
}
summary.addcovarint <- function(object, ...) object
# end of addqtl.R
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.