R/summary.scantwo.R

Defines functions `[.scantwoperm` subset.scantwoperm condense.scantwo condense cbind.scantwoperm c.scantwoperm c.scantwo print.summary.scantwoperm summary.scantwoperm subset.scantwo clean.scantwo max.scantwo print.scantwo print.summary.addpair print.summary.scantwo subrousummaryscantwo summary.scantwo

Documented in cbind.scantwoperm clean.scantwo condense condense.scantwo c.scantwo c.scantwoperm max.scantwo print.scantwo print.summary.addpair print.summary.scantwo print.summary.scantwoperm subrousummaryscantwo subset.scantwo subset.scantwoperm summary.scantwo summary.scantwoperm

######################################################################
#
# summary.scantwo.R
#
# copyright (c) 2001-2020, Karl W Broman, Hao Wu, and Brian Yandell
# last modified Feb, 2020
# first written Nov, 2001
#
#     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: summary.scantwo, print.summary.scantwo,
#           max.scantwo, clean.scantwo, print.scantwo, subset.scantwo
#           summary.scantwoperm, print.summary.scantwoperm
#           condense.scantwo, summary.scantwocondensed
#           max.scantwocondensed, print.summary.addpair
#           rbind.scantwoperm, c.scantwoperm, subset.scantwoperm
#           [.scantwoperm
#
######################################################################
#
# summarize the result from scantwo
#
######################################################################
summary.scantwo <-
    function(object, thresholds,
             what=c("best", "full", "add", "int"),
             perms, alphas, lodcolumn=1, pvalues=FALSE,
             allpairs=TRUE, ...)
{
    if(!inherits(object, "scantwo") &&
       !inherits(object, "scantwocondensed"))
        stop("Input should have class \"scantwo\".")

    addpair <- attr(object, "addpair")
    if(!is.null(addpair) && addpair) { # results from addpair() that need special treatment
        if("lod.minus1" %in% names(attributes(object))) { # asymmetric formula
            attr(object, "addpair") <- NULL
            x <- summary.scantwo(object, allpairs=allpairs)
            class(x) <- "data.frame"
            x <- x[,c(1,2,8,9,10,3,4,5),drop=FALSE]

            mlod.minus1 <- tapply(attr(object, "lod.minus1"), object$map$chr, max, na.rm=TRUE)
            mlod.minus2 <- tapply(attr(object, "lod.minus2"), object$map$chr, max, na.rm=TRUE)

            w <- which(x[,1] == x[,2])
            for(i in seq(along=w))
                if(x[w[i],5] < x[w[i],8]) x[w[i],3:5] <- x[w[i],c(7,6,8)]

            if(any(x[,1] != x[,2])) {
                w <- which(x[,1] != x[,2])
                y <- x[w,,drop=FALSE]
                for(i in 1:nrow(y))
                    y[,1:5] <- y[,c(2,1,7,6,8)]

                neworder <- cbind(1:nrow(x), rep(NA, nrow(x)))
                neworder[w,2] <- nrow(x) + 1:nrow(y)
                neworder <- as.numeric(t(neworder))

                x <- rbind(x, y)[neworder[!is.na(neworder)],,drop=FALSE]
            }

            x <- cbind(x[,1:5], lod.2v1b=rep(NA,nrow(x)), lod.2v1a=rep(NA,nrow(x)))
            names(x)[5] <- "lod.2v0"

            x[,6] <- x[,5] - mlod.minus1[as.character(x[,2])]
            x[,7] <- x[,5] - mlod.minus2[as.character(x[,1])]

            if(!missing(thresholds)) {
                if(length(thresholds) > 2)
                    warning("Only the first two values in thresholds are used.")

                if(length(thresholds) == 1) thresholds <- c(thresholds, 0)
                x <- x[!is.na(x[,5]) & x[,5] >= thresholds[1] &
                       ((!is.na(x[,6]) & x[,6] >= thresholds[2]) |
                        (!is.na(x[,7]) & x[,7] >= thresholds[2])),,drop=FALSE]
            }

            class(x) <- c("summary.addpair", "data.frame")
            return(x)
        }
        else { # symmetric formula
            attr(object, "addpair") <- NULL
            if(missing(thresholds))
                x <- summary.scantwo(object, allpairs=allpairs)
            else
                x <- summary.scantwo(object, thresholds=thresholds, allpairs=allpairs)

            x <- x[,1:6]
            colnames(x)[5:6] <- c("lod.2v0", "lod.2v1")

            if(!missing(thresholds)) {
                if(length(thresholds) > 2)
                    warning("Only the first two values in thresholds are used.")

                if(length(thresholds) == 1) thresholds <- c(thresholds, 0)
                x <- x[!is.na(x[,5]) & x[,5] >= thresholds[1] &
                       !is.na(x[,6]) & x[,6] >= thresholds[2],,drop=FALSE]
            }

            class(x) <- c("summary.addpair", "data.frame")
            return(x)
        }
    }

    what <- match.arg(what)

    if(!missing(thresholds)) {
        if(length(thresholds) != 5)
            stop("If thresholds are given, there must be 5 of them.")
        else if(!is.numeric(thresholds))
            stop("thresholds should be a numeric vector")
    }

    if(!missing(alphas)) {
        if(length(alphas) != 5) {
            if(length(alphas)==1)
                alphas <- rep(alphas, 5)
            else
                stop("If alphas are given, there must be 5 of them.")
        }
        else if(!is.numeric(alphas))
            stop("alphas should be a numeric vector")
    }

    if(!missing(perms) && !inherits(perms, "scantwoperm"))
        stop("perms must be in scantwoperm format.")

    # subset object and permutations, if necessary
    if(inherits(object, "scantwo")) {
        d <- dim(object$lod)
        if(length(d)==3) {
            if(!missing(perms)) {
                if("AA" %in% names(perms)) { # contains X-specific results
                    ncp <- sapply(perms$AA, ncol)
                }
                else {
                    ncp <- sapply(perms, ncol)
                }

                if(all(ncp==1)) onepermcol <- TRUE
                else onepermcol <- FALSE

                if(any(ncp != d[3])) {
                    if(onepermcol)  {
                        if(lodcolumn > 1)
                            warning("Just one column of permutation results; assuming they apply to all LOD score columns.")
                    }
                    else
                        stop("perms have different numbers of columns as object input.\n")
                }
            }

            if(lodcolumn < 1 || lodcolumn > d[3])
                stop("lodcolumn must be between 1 and ", d[3])

            object$lod <- object$lod[,,lodcolumn]
            if(!missing(perms) && !onepermcol) {
                if("AA" %in% names(perms)) {
                    for(i in seq(along=perms))
                        perms[[i]] <- lapply(perms[[i]], function(a, b) a[,b,drop=FALSE], lodcolumn)
                }
                else {
                    perms <- lapply(perms, function(a, b) a[,b,drop=FALSE], lodcolumn)
                }
            }
        }
    }
    else { # condensed version
        if(is.matrix(object$pos1.jnt)) {
            d <- ncol(object$pos1.jnt)
            if(!missing(perms)) {
                ncp <- sapply(perms, ncol)
                if(all(ncp==1)) onepermcol <- TRUE
                else onepermcol <- FALSE
                if(any(ncp != d[3])) {
                    if(onepermcol)
                        warning("Just one column of permutation results; reusing for all LOD score columns.")
                    else
                        stop("perms have different numbers of columns as object input.\n")
                }
            }

            if(lodcolumn < 1 || lodcolumn > d)
                stop("lodcolumn must be between 1 and ", d)

            for(i in 3:length(object))
                object[[i]] <- object[[i]][,lodcolumn]

            if(!missing(perms) && !onepermcol)
                perms <- lapply(perms, function(a, b) a[,b,drop=FALSE], lodcolumn)
        }
    }

    # check input
    if(missing(perms) && !missing(alphas))
        stop("If alphas are to be used, permutation results must be provided.")
    if(!missing(thresholds) && !missing(alphas))
        stop("Only one of threshold and alpha should be specified.")
    if(pvalues && what != "best") {
        pvalues <- FALSE
        warning("pvalues shown only with what=\"best\".")
    }
    if(pvalues && missing(perms)) {
        pvalues <- FALSE
        warning("p-values may be calculated only if perms are provided.")
    }

    if(inherits(object, "scantwo"))
        out <- subrousummaryscantwo(object, for.perm=FALSE)
    else
        out <- as.data.frame(object, stringsAsFactors=TRUE)

    if(!allpairs) # only look at self-self cases
        out <- out[out$chr1==out$chr2,]

    if(what=="best") {
        p1.f <- out$pos1.jnt
        p2.f <- out$pos2.jnt
        p1.a <- out$pos1.add
        p2.a <- out$pos2.add

        lf <- out$jnt.lod.full
        li <- out$jnt.lod.full - out$add.lod.add
        lfv1 <- out$jnt.lod.full - out$lod.1qtl
        la <- out$add.lod.add
        lav1 <- out$add.lod.add - out$lod.1qtl
    }
    else if(what=="full") {
        p1.f <- p1.a <- out$pos1.jnt
        p2.f <- p2.a <- out$pos2.jnt

        lf <- out$jnt.lod.full
        li <- out$jnt.lod.full - out$jnt.lod.add
        lfv1 <- out$jnt.lod.full - out$lod.1qtl
        la <- out$jnt.lod.add
        lav1 <- out$jnt.lod.add - out$lod.1qtl
    }
    else if(what=="add") {
        p1.f <- p1.a <- out$pos1.add
        p2.f <- p2.a <- out$pos2.add

        lf <- out$add.lod.full
        li <- out$add.lod.full - out$add.lod.add
        lfv1 <- out$add.lod.full - out$lod.1qtl
        la <- out$add.lod.add
        lav1 <- out$add.lod.add - out$lod.1qtl
    }
    else { # what == "int"
        p1.f <- p1.a <- out$pos1.int
        p2.f <- p2.a <- out$pos2.int

        lf <- out$int.lod.full
        li <- out$int.lod.full - out$int.lod.add
        lfv1 <- out$int.lod.full - out$lod.1qtl
        la <- out$int.lod.add
        lav1 <- out$int.lod.add - out$lod.1qtl
    }

    out <- data.frame(chr1=out$chr1, chr2=out$chr2,
                      pos1f=p1.f,
                      pos2f=p2.f,
                      lod.full=lf, lod.fv1=lfv1, lod.int=li,
                      pos1a=p1.a,
                      pos2a=p2.a,
                      lod.add=la, lod.av1=lav1, stringsAsFactors=TRUE)

    if(what != "best") {
        out <- out[,-(8:9)]
        names(out)[3:4] <- c("pos1", "pos2")
    }

    if(!missing(perms) && "AA" %in% names(perms)) {
        xchr_specific <- TRUE
        chr_type <- attr(perms, "chrtype")
        chrpair_type <- paste0(chr_type[out$chr1], chr_type[out$chr2])
        if(all(chrpair_type=="AA")) {
            perms <- perms$AA
            xchr_specific <- FALSE
        }
        else if(all(chrpair_type=="XX")) {
            perms <- perms$XX
            xchr_specific <- FALSE
        }
        else if(all(chrpair_type=="AX")) {
            perms <- perms$AX
            xchr_specific <- FALSE
        }
    }
    else xchr_specific <- FALSE

    if(!missing(alphas)) { # get thresholds
        if(!xchr_specific) {
            thresholds <- rep(0,5)
            for(i in 1:5)
                thresholds[i] <- quantile(perms[[i]], 1-alphas[i])
            thresholds[alphas==1] <- 0
            thresholds[alphas==0] <- Inf
        }
        else { # x-chr-specific
            thresholds <- matrix(nrow=3, ncol=5)
            for(j in 1:3) {
                for(i in 1:5)
                    thresholds[j,i] <- quantile(perms[[j]][[i]], 1-alphas[i])
            }
            thresholds[alphas==1] <- 0
            thresholds[alphas==0] <- Inf
        }
    }

    if(!missing(thresholds) || !missing(alphas)) { # apply thresholds
        if(!xchr_specific) {
            out <- out[(out$lod.full >= thresholds[1] & (out$lod.fv1 >= thresholds[2] |
                        out$lod.int >= thresholds[3])) |
                       (out$lod.add >= thresholds[4] & out$lod.av1 >= thresholds[5]),,drop=FALSE]
        }
        else {
            tokeep <- NULL
            for(i in 1:nrow(out)) {
                this_chrpair_type <- match(chrpair_type[i], c("AA", "AX", "XX"))
                if((out$lod.full[i] >= thresholds[this_chrpair_type, 1] &
                    (out$lod.fv1[i] >= thresholds[this_chrpair_type, 2] |
                     out$lod.int[i] >= thresholds[this_chrpair_type, 3])) |
                   (out$lod.add[i] >= thresholds[this_chrpair_type, 4] &
                    out$lod.av1[i] >= thresholds[this_chrpair_type, 5]))
                    tokeep <- c(tokeep, i)
            }
            out <- out[tokeep, , drop=FALSE]
        }
    }

    if(pvalues && nrow(out) > 0) {
        result <- as.data.frame(matrix(ncol=11+5, nrow=nrow(out)), stringsAsFactors=TRUE)
        wh <- c(1,2,3,4,5,7,9,11,12,13,15)
        wh2 <- (1:16)[-wh]
        result[,wh] <- out
        names(result)[wh] <- names(out)
        colnames(result)[wh2] <- rep("pval",5)

        if(!xchr_specific) {
            for(i in 1:5) {
                for(j in 1:nrow(out))
                    result[j,wh2[i]] <- mean(perms[[i]] >= result[j,wh2[i]-1], na.rm=TRUE)
            }
        }
        else { # X-chr-specific
            L <- attr(perms, "L")
            sumL <- sum(L)
            for(j in 1:nrow(out)) {
                this_chrpair_type <- match(chrpair_type[j], c("AA", "AX", "XX"))
                pow <- sumL/L[this_chrpair_type]
                for(i in 1:5) {
                    nominal_p <- mean(perms[[this_chrpair_type]][[i]] >= result[j,wh2[i]-1], na.rm=TRUE)
                    result[j,wh2[i]] <- 1 - (1-nominal_p)^pow # adjusted P-value
                }
            }
        }
        out <- result
    }

    class(out) <- c("summary.scantwo", "data.frame")

    out
}




# subroutine for summary.scantwo; pulls out the key info
#     on each pair of chromosomes
subrousummaryscantwo <-
    function(object, for.perm=FALSE)
{
    lod <- object$lod
    lod[is.na(lod) | lod == Inf | lod == -Inf] <- 0
    map <- object$map

    pos <- map[,2]
    chr <- as.factor(map[,1])

    tchr <- as.numeric(chr)
    n.chr <- max(tchr)
    xchr <- tapply(map[,4], map[,1], function(a) a[1])
    xchr <- xchr[!is.na(xchr)]
    n.phe <- 1
    if(length(dim(lod)) == 3) n.phe <- dim(lod)[3]

    if(!("scanoneX" %in% names(object)) ||
       is.null(object$scanoneX) || length(object$scanoneX)==0) {
        if(n.phe==1) scanoneX <- diag(lod)
        else {
            if(nrow(lod)==1) scanoneX <- lod[1,1,1]
            else scanoneX <- diag(lod[,,1])
            for(i in 2:n.phe) {
                if(nrow(lod)==1) scanoneX <- cbind(scanoneX, lod[1,1,i])
                else scanoneX <- cbind(scanoneX, diag(lod[,,i]))
            }
        }
    }
    else scanoneX <- object$scanoneX

    if((is.matrix(scanoneX) && nrow(scanoneX) != nrow(lod)) ||
       (!is.matrix(scanoneX) && length(scanoneX) != nrow(lod)))
        stop("scanoneX component has length ", length(scanoneX), " but should have length ", nrow(lod))

    n.chrpair <- n.chr*(n.chr+1)/2

    fill <- matrix(0, nrow=n.chrpair, ncol=n.phe)

    out <- .C("R_summary_scantwo",
              as.integer(nrow(map)),
              as.integer(n.phe),
              as.double(lod),
              as.integer(n.chr),
              as.integer(tchr),
              as.double(pos),
              as.integer(xchr),
              as.double(scanoneX),
              as.integer(n.chrpair),
              chr1=as.integer(rep(0,n.chrpair)),
              chr2=as.integer(rep(0,n.chrpair)),
              as.integer(rep(0,n.chr*n.chr)),
              pos1.jnt=as.double(fill),
              pos2.jnt=as.double(fill),
              pos1.add=as.double(fill),
              pos2.add=as.double(fill),
              pos1.int=as.double(fill),
              pos2.int=as.double(fill),
              jnt.lod.full=as.double(fill),
              jnt.lod.add=as.double(fill),
              add.lod.full=as.double(fill),
              add.lod.add=as.double(fill),
              int.lod.full=as.double(fill),
              int.lod.add=as.double(fill),
              lod.1qtl=as.double(fill),
              PACKAGE="qtl")

    chr1 <- factor(levels(chr)[out$chr1+1], levels=levels(chr))
    chr2 <- factor(levels(chr)[out$chr2+1], levels=levels(chr))

    if(n.phe == 1) {
        out <- data.frame(chr1=chr1, chr2=chr2,
                          pos1.jnt=out$pos1.jnt,
                          pos2.jnt=out$pos2.jnt,
                          jnt.lod.full=out$jnt.lod.full,
                          jnt.lod.add=out$jnt.lod.add,
                          pos1.add=out$pos1.add,
                          pos2.add=out$pos2.add,
                          add.lod.full=out$add.lod.full,
                          add.lod.add=out$add.lod.add,
                          pos1.int=out$pos1.int,
                          pos2.int=out$pos2.int,
                          int.lod.full=out$int.lod.full,
                          int.lod.add=out$int.lod.add,
                          lod.1qtl=out$lod.1qtl,
                          stringsAsFactors=TRUE)
    }
    else
        out <- list(chr1=chr1, chr2=chr2,
                    pos1.jnt=matrix(out$pos1.jnt, ncol=n.phe),
                    pos2.jnt=matrix(out$pos2.jnt, ncol=n.phe),
                    jnt.lod.full=matrix(out$jnt.lod.full, ncol=n.phe),
                    jnt.lod.add=matrix(out$jnt.lod.add, ncol=n.phe),
                    pos1.add=matrix(out$pos1.add, ncol=n.phe),
                    pos2.add=matrix(out$pos2.add, ncol=n.phe),
                    add.lod.full=matrix(out$add.lod.full, ncol=n.phe),
                    add.lod.add=matrix(out$add.lod.add, ncol=n.phe),
                    pos1.int=matrix(out$pos1.int, ncol=n.phe),
                    pos2.int=matrix(out$pos2.int, ncol=n.phe),
                    int.lod.full=matrix(out$int.lod.full, ncol=n.phe),
                    int.lod.add=matrix(out$int.lod.add, ncol=n.phe),
                    lod.1qtl=matrix(out$lod.1qtl, ncol=n.phe))

    if(for.perm) {
        if(n.phe==1) {
            out <- c("full"=max(out$jnt.lod.full,na.rm=TRUE),
                     "fv1"=max(out$jnt.lod.full - out$lod.1qtl, na.rm=TRUE),
                     "int"=max(out$jnt.lod.full - out$add.lod.add, na.rm=TRUE),
                     "add"=max(out$add.lod.add, na.rm=TRUE),
                     "av1"=max(out$add.lod.add - out$lod.1qtl, na.rm=TRUE),
                     "one"=max(out$lod.1qtl, na.rm=TRUE))
        }
        else {
            out <- list("full"=apply(out$jnt.lod.full, 2, max, na.rm=TRUE),
                        "fv1"=apply(out$jnt.lod.full - out$lod.1qtl, 2, max, na.rm=TRUE),
                        "int"=apply(out$jnt.lod.full - out$add.lod.add, 2, max, na.rm=TRUE),
                        "add"=apply(out$add.lod.add, 2, max, na.rm=TRUE),
                        "av1"=apply(out$add.lod.add - out$lod.1qtl, 2, max, na.rm=TRUE),
                        "one"=apply(out$lod.1qtl, 2, max, na.rm=TRUE))
        }
    }

    out
}


print.summary.scantwo <-
    function(x, ...)
{
    if(nrow(x)==0) {
        cat("    There were no pairs of loci meeting the criteria.\n")
        return(invisible(NULL))
    }

    z <- as.character(unlist(x[,1]))

    if(max(nchar(z)) == 1)
        rownames(x) <- apply(x[,1:2], 1, function(a)
                             paste("c", a, collapse=":", sep=""))
    else
        rownames(x) <- apply(x[,1:2], 1, function(a)
                             paste(sprintf("c%-2s", a), collapse=":"))

    x <- x[,-(1:2)]

    cn <- colnames(x)
    if(any(cn=="pos1a"))
        cn[cn=="pos1a"] <- "    pos1a"

    wh <- grep("^pval", cn)
    if(length(wh) > 0)
        cn[wh] <- "pval"
    colnames(x) <- cn

    print.data.frame(x, digits=3)
}


print.summary.addpair <-
    function(x, ...)
{
    if(nrow(x)==0) {
        cat("    There were no pairs of loci meeting the criteria.\n")
        return(invisible(NULL))
    }

    z <- as.character(unlist(x[,1]))

    if(max(nchar(z)) == 1)
        rownames(x) <- apply(x[,1:2], 1, function(a)
                             paste("c", a, collapse=":", sep=""))
    else
        rownames(x) <- apply(x[,1:2], 1, function(a)
                             paste(sprintf("c%-2s", a), collapse=":"))

    x <- x[,-(1:2), drop=FALSE]

    print.data.frame(x, digits=3)
}



print.scantwo <-
    function(x, ...)
{
    d <- dim(x$lod)
    dn <- dimnames(x$lod)[[3]]
    if(is.null(dn)) dn <- attr(x, "phenotypes")


    if(nrow(x$lod) == 0)
        cat("Empty scantwo object.\n")
    else {

        if(length(d)==2)
            print(summary(x))
        else {
            for(i in 1:d[3]) {
                if(is.null(dn)) cat("Phenotype", i, "\n")
                else cat(dn[i], "\n")
                print(summary(x, lod=i))
            }
        }
    }
}


######################################################################
#
# max.scantwo:  Give pair of chromosome with maximum 2-locus LOD score
#
######################################################################

max.scantwo <-
    function(object, lodcolumn=1,
             what=c("best", "full", "add", "int"),
             na.rm=TRUE, ...)
{
    if(!inherits(object, "scantwo") &&
       !inherits(object, "scantwocondensed"))
        stop("Input must have class \"scantwo\".")

    addpair <- attr(object, "addpair")
    if(!is.null(addpair) && addpair) { # special treatment for output for addpair
        temp <- summary(object)
        mx <- max(temp[,5],na.rm=TRUE)
        return(temp[!is.na(temp[,5]) & temp[,5]==mx,])
    }

    what <- match.arg(what)
    if(inherits(object, "scantwo")) {
        d <- dim(object$lod)
        if(length(d)==3) {
            if(lodcolumn < 1 || lodcolumn > d[3])
                stop("lodcolumn must be between 1 and ", d[3])
            object$lod <- object$lod[,,lodcolumn]
        }

        out <- subrousummaryscantwo(object, for.perm=FALSE)
    }
    else { # condensed version
        if(is.matrix(object$pos1.jnt)) {
            d <- ncol(object$pos1.jnt)
            if(lodcolumn < 1 || lodcolumn > d)
                stop("lodcolumn must be between 1 and ", d)

            for(i in 3:length(object))
                object[[i]] <- object[[i]][,lodcolumn]
        }
        out <- as.data.frame(object, stringsAsFactors=TRUE)
    }

    if(what=="best") {
        wh <- which(!is.na(out$jnt.lod.full) & out$jnt.lod.full==max(out$jnt.lod.full, na.rm=TRUE))
        p1.f <- out$pos1.jnt[wh]
        p2.f <- out$pos2.jnt[wh]
        p1.a <- out$pos1.add[wh]
        p2.a <- out$pos2.add[wh]

        lf <- out$jnt.lod.full[wh]
        li <- out$jnt.lod.full[wh] - out$add.lod.add[wh]
        lfv1 <- out$jnt.lod.full[wh] - out$lod.1qtl[wh]
        la <- out$add.lod.add[wh]
        lav1 <- out$add.lod.add[wh] - out$lod.1qtl[wh]
    }
    else if(what=="full") {
        wh <- which(!is.na(out$jnt.lod.full) & out$jnt.lod.full==max(out$jnt.lod.full, na.rm=TRUE))
        p1.f <- p1.a <- out$pos1.jnt[wh]
        p2.f <- p2.a <- out$pos2.jnt[wh]

        lf <- out$jnt.lod.full[wh]
        li <- out$jnt.lod.full[wh] - out$jnt.lod.add[wh]
        lfv1 <- out$jnt.lod.full[wh] - out$lod.1qtl[wh]
        la <- out$jnt.lod.add[wh]
        lav1 <- out$jnt.lod.add[wh] - out$lod.1qtl[wh]
    }
    else if(what=="add") {
        wh <- which(!is.na(out$add.lod.add) & out$add.lod.add==max(out$add.lod.add, na.rm=TRUE))
        p1.f <- p1.a <- out$pos1.add[wh]
        p2.f <- p2.a <- out$pos2.add[wh]

        lf <- out$add.lod.full[wh]
        li <- out$add.lod.full[wh] - out$add.lod.add[wh]
        lfv1 <- out$add.lod.full[wh] - out$lod.1qtl[wh]
        la <- out$add.lod.add[wh]
        lav1 <- out$add.lod.add[wh] - out$lod.1qtl[wh]
    }
    else { # what == "int"
        lod.int <- out$int.lod.full - out$int.lod.add
        wh <- which(!is.na(lod.int) & lod.int == max(lod.int, na.rm=TRUE))
        p1.f <- p1.a <- out$pos1.int[wh]
        p2.f <- p2.a <- out$pos2.int[wh]

        lf <- out$int.lod.full[wh]
        li <- out$int.lod.full[wh] - out$int.lod.add[wh]
        lfv1 <- out$int.lod.full[wh] - out$lod.1qtl[wh]
        la <- out$int.lod.add[wh]
        lav1 <- out$int.lod.add[wh] - out$lod.1qtl[wh]
    }

    out <- data.frame(chr1=out$chr1[wh], chr2=out$chr2[wh],
                      pos1f=p1.f,
                      pos2f=p2.f,
                      lod.full=lf, lod.fv1=lfv1, lod.int=li,
                      pos1a=p1.a,
                      pos2a=p2.a,
                      lod.add=la, lod.av1=lav1, stringsAsFactors=TRUE)

    if(what != "best") {
        out <- out[,-(8:9)]
        names(out)[3:4] <- c("pos1", "pos2")
    }

    class(out) <- c("summary.scantwo", "data.frame")
    rownames(out) <- what

    out
}



######################################################################
# clean.scantwo
#
# sets LOD scores that are missing or < 0 to 0
# If full LOD < add've LOD, set full = add've
# sets LOD scores, for pairs of positions that are not separated
#     by n.mar markers and distance cM, to 0
######################################################################

clean.scantwo <-
    function(object, n.mar=1, distance=0, ...)
{
    if(!inherits(object, "scantwo"))
        stop("Input should have class \"scantwo\".")

    addpair <- attr(object, "addpair")
    if(is.null(addpair)) addpair <- FALSE

    lod <- object$lod
    map <- object$map
    if(!("fullmap" %in% names(attributes(object))))
        stop("clean.scantwo only works on scantwo objects created with R/qtl ver >= 1.04-38.\n")
    fmap <- attr(object, "fullmap")

    lod[is.na(lod) | lod < 0] <- 0

    subrou <- function(x,y,z)
    {
        out <- x
        for(i in seq(along=x))
            out[i] <- sum((z > x[i] & z < y[i]) | (z < x[i] & z > y[i])) +
                (x[i] == y[i])
        out
    }

    last <- 0
    for(i in seq(along=fmap)) {
        m <- map[map[,1]==names(fmap)[i],2]
        idx <- 1:length(m)+last
        last <- last + length(m)

        toclean <- (outer(m, m, subrou, fmap[[i]]) < n.mar) | (abs(outer(m, m, "-")) <distance)
        diag(toclean) <- FALSE

        if(length(dim(lod))==3) # case of multiple phenotypes
            lod[idx,idx,][toclean] <- 0
        else
            lod[idx,idx][toclean] <- 0
    }

    # if full LOD < add've LOD, set full = add've
    if(!addpair) {
        if(length(dim(lod)) == 2) {
            lo <- lower.tri(lod)
            wh <- (lod[lo] < t(lod)[lo])
            if(any(wh))
                lod[lo][wh] <- t(lod)[lo][wh]
        }
        else {
            lo <- lower.tri(lod[,,1])
            for(i in 1:dim(lod)[3]) {
                wh <- (lod[,,i][lo] < t(lod[,,i])[lo])
                if(any(wh))
                    lod[,,i][lo][wh] <- t(lod[,,i])[lo][wh]
            }
        }
    }

    object$lod <- lod

    object
}

######################################################################
#
# subset.scantwo
#
######################################################################
subset.scantwo <-
    function(x, chr, lodcolumn, ...)
{
    if(!inherits(x, "scantwo"))
        stop("Input should have class \"scantwo\".")

    if((missing(chr) || length(chr)==0) && missing(lodcolumn)) return(x)

    if(!missing(lodcolumn)) {
        if(any(lodcolumn>0) && any(lodcolumn<0))
            stop("lodcolumn values can't be both >0 and <0.")

        if(any(lodcolumn<0) || is.logical(lodcolumn))
            lodcolumn <- (1:(dim(x$lod)[3]))[lodcolumn]
        if(length(lodcolumn)==0)
            stop("You must retain at least one LOD column.")
        if(any(lodcolumn < 1 || lodcolumn > dim(x$lod)[3]))
            stop("lodcolumn values must be >=1 and <=",dim(x$lod)[3])

        x$lod <- x$lod[,,lodcolumn]
        if("scanoneX" %in% names(x))
            x$scanoneX <- x$scanoneX[,lodcolumn]
    }

    if(!missing(chr)) {
        chr <- matchchr(chr, unique(x$map[,1]))

        wh <- x$map[,1] %in% chr

        x$map <- x$map[wh,,drop=FALSE]
        x$map[,1] <- droplevels(x$map[,1])

        if(length(dim(x$lod))==2)
            x$lod <- x$lod[wh,wh,drop=FALSE]
        else
            x$lod <- x$lod[wh,wh,,drop=FALSE]

        if(!is.null(x$scanoneX))
            x$scanoneX <- x$scanoneX[wh]

        if("fullmap" %in% names(attributes(x))) {
            fmap <- attr(x, "fullmap")
            fmap <- fmap[chr]
            attr(x, "fullmap") <- fmap
        }

    }

    x
}

######################################################################
# summary.scantwoperm
#
# Give genome-wide LOD thresholds on the basis of results of
# scantwo permutation test (from scantwo with n.perm > 0)
######################################################################
summary.scantwoperm <-
    function(object, alpha=c(0.05, 0.10), ...)
{
    if(!inherits(object, "scantwoperm"))
        stop("Input should have class \"scantwoperm\".")

    if("AA" %in% names(object)) { # X-chr-specific version
        # get region-specific (nominal) signif levels
        L <- attr(object, "L")
        LL <- attr(object, "LL")
        if(is.null(LL)) stop("LL attribute not found in input object")
        if(length(alpha)==1) {
            tmp <- L/sum(L); tmp <- c(tmp[1], 1, tmp[2])
            one_minus_alpha_onechr <- cbind((1-alpha)^tmp)
            one_minus_alpha <- cbind((1-alpha)^(LL/sum(LL)))
        }
        else {
            tmp <- L/sum(L); tmp <- c(tmp[1], 1, tmp[2])
            one_minus_alpha_onechr <- vapply(alpha, function(a,b) (1-a)^b, rep(0, length(tmp)), tmp)
            one_minus_alpha <- vapply(alpha, function(a,b) (1-a)^b, rep(0, length(LL)), LL/sum(LL))
        }
        # get quantiles
        out <- vector("list", length(object))
        names(out) <- names(object)
        for(i in seq(along=out)) {
            f <- function(a, qu) {
                b <- apply(a, 2, quantile, qu)
                if(!is.matrix(b)) {
                    nam <- names(b)
                    b <- matrix(b, nrow=1)
                    colnames(b) <- nam
                }
                rownames(b) <- paste0(alpha*100, "%")
                b
            }
            out[[i]] <- lapply(unclass(object[[i]])[1:5], f, one_minus_alpha[i,,drop=FALSE])
            qu_one <- f(object[[i]][[6]], one_minus_alpha_onechr[i,,drop=FALSE])
            out[[i]] <- c(out[[i]], "one"=list(qu_one))
        }

        attr(out, "n.perm") <- vapply(object, function(a) nrow(a[[1]]), 0)
        class(out) <- c("summary.scantwoperm", "list")

        return(out)
    }

    out <- lapply(object, apply, 2, quantile, 1-alpha)

    for(i in 1:length(out)) {
        if(!is.matrix(out[[i]]))
            out[[i]] <- matrix(out[[i]], nrow=length(alpha))
        rownames(out[[i]]) <- paste0(alpha*100, "%")
    }

    attr(out, "n.perm") <- nrow(object[[1]])
    class(out) <- c("summary.scantwoperm", "list")
    out
}

print.summary.scantwoperm <-
    function(x, ...)
{
    if("AA" %in% names(x)) { # X-sp perms
        nperm <- attr(x, "n.perm")
        rn <- rownames(x[[1]][[1]])
        nc <- rapply(x, ncol)
        if(length(unique(nc)) != 1)
            stop("The components shouldn't have varying numbers of columns.\n")
        nc <- nc[1]
        phe <- colnames(x[[1]][[1]])

        convert2matrix <-
            function(a, which_col=1) {
                a <- lapply(a, "[", , which_col, drop=FALSE)
                b <- matrix(unlist(a), ncol=6)
                rownames(b) <- rn
                colnames(b) <- names(a)
                b
            }

        for(i in seq(along=phe)) {
            cat(phe[i], ":\n", sep="")
            y <- lapply(x, convert2matrix, i)
            lab <- c(AA="A:A", AX="A:X", XX="X:X")
            for(j in c("AA", "AX", "XX")) {
                cat("  ", lab[j], " (", nperm[j], " permutations)\n", sep="")
                print(y[[j]], digits=3)
                cat("\n")
            }
            if(i != length(phe)) cat("\n")
        }
        invisible(return(x))
    }

    nam <- names(x)
    rn <- rownames(x[[1]])
    n.perm <- attr(x, "n.perm")

    nc <- sapply(x, ncol)
    if(length(unique(nc)) != 1)
        stop("The components shouldn't have varying numbers of columns.\n")
    nc <- nc[1]

    if(nc==1) {
        phe <- colnames(x[[1]])
        if(is.null(phe)) phe <- ""

        x <- matrix(unlist(x), nrow=length(rn))
        dimnames(x) <- list(rn, nam)

        if(is.null(phe))
            cat("(", n.perm, " permutations)\n", sep="")
        else
            cat(phe, " (", n.perm, " permutations)\n", sep="")
        print(x, digits=3)
    }
    else {
        phe <- colnames(x[[1]])
        if(is.null(phe)) phe <- paste("pheno", 1:nc)

        for(i in 1:nc) {
            y <- matrix(ncol=length(x), nrow=nrow(x[[1]]))
            for(j in 1:length(x))
                y[,j] <- x[[j]][,i]
            dimnames(y) <- list(rn, nam)

            cat(phe[i], " (", n.perm, " permutations)\n", sep="")
            print(y, digits=3)
            if(i != nc) cat("\n")
        }
    }
}

######################################################################
# combine scantwo results ... paste the phenotype columns together
######################################################################
cbind.scantwo <- c.scantwo <-
    function(...)
{
    dots <- list(...)

    if(length(dots)==1 && is.list(dots[[1]])) dots <- dots[[1]]

    if(length(dots)==1) return(dots[[1]])
    for(i in seq(along=dots)) {
        if(!inherits(dots[[i]], "scantwo"))
            stop("Input should have class \"scantwo\".")
    }

    # check dimensions of LODs
    nr <- sapply(dots, function(a) nrow(a$lod))
    nc <- sapply(dots, function(a) ncol(a$lod))
    nd3 <- sapply(dots, function(a) dim(a$lod)[3])
    if(any(nr[-1]!=nr[1]) || any(nc[-1] != nc[1]))
        stop("Input objects are not the same dimensions.")

    # check maps
    map1 <- dots$map[[1]]
    for(i in 2:length(dots)) {
        if(!all(dots$map[[i]] != map1))
            stop("Maps are not all the same.")
    }

    output <- dots[[1]]
    nd3[is.na(nd3)] <- 1
    output$lod <- array(dim=c(nr[1], nc[1], sum(nd3)))
    start <- cumsum(c(0,nd3))[-length(nd3)-1]
    end <- start+nd3

    for(i in seq(along=dots))
        output$lod[,,(start[i]+1):end[i]] <- dots[[i]]$lod
    attr(output, "phenotypes") <- unlist(lapply(dots, attr, "phenotypes"))
    dimnames(output$lod) <- list(NULL, NULL, attr(output, "phenotypes"))

    # check scanoneX
    if(is.null(dots[[1]]$scanoneX)) {
        for(i in 2:length(dots)) {
            if(!is.null(dots[[i]]$scanoneX))
                stop("Some but not all input objects have null scanoneX.")
        }
    }
    else {
        nrx <- sapply(dots, function(a) nrow(a$scanoneX))
        if(any(nrx[-1]!=nrx[1]))
            stop("Mismatch in scanoneX dimensions.")
        for(i in 2:length(dots))
            output$scanoneX <- cbind(output$scanoneX, dots[[i]]$scanoneX)
        colnames(output$scanoneX) <- attr(output, "phenotypes")
    }

    methods <- sapply(dots, attr, "method")
    if(length(unique(methods)) != 1)
        attr(output, "method") <- rep(sapply(dots, attr, "method"), nd3)

    fm <- lapply(dots, attr, "fullmap")
    for(i in 2:length(fm)) {
        if(length(fm[[1]]) != length(fm[[i]]) || !all(names(fm[[1]]) == names(fm[[i]])))
            stop("Mismatch in \"fullmap\" attributes (1).")
        for(j in 1:length(fm[[1]])) {
            if(length(fm[[1]][[j]]) != length(fm[[i]][[j]]) ||
               !all(names(fm[[1]][[j]]) == names(fm[[i]][[j]])) ||
               max(abs(fm[[1]][[j]] - fm[[i]][[j]])) > 0.001)
                stop("Mismatch in \"fullmap\" attributes (2).")
        }
    }

    output
}


######################################################################
# combine scantwoperm results ... paste the rows together
######################################################################
rbind.scantwoperm <- c.scantwoperm <-
    function(...)
{
    dots <- list(...)

    if(length(dots)==1 && is.list(dots[[1]])) dots <- dots[[1]]
    if(length(dots)==1) return(dots[[1]])

    xchrsp <- vapply(dots, function(a) "AA" %in% names(a), TRUE)
    if(any(xchrsp)) {
        if(!all(xchrsp)) stop("Some but not all inputs are X-chr specific")

        for(i in 2:length(dots)) {
            for(j in seq(along=dots[[1]])) {
                for(k in seq(along=dots[[1]][[j]])) {
                    if(ncol(dots[[1]][[j]][[k]]) != ncol(dots[[i]][[j]][[k]]))
                        stop("Mismatch in no. columns")
                    if(any(colnames(dots[[1]][[j]][[k]]) != colnames(dots[[1]][[j]][[k]])))
                        warning("Mismatch in column names")
                    dots[[1]][[j]][[k]] <- rbind(dots[[1]][[j]][[k]], dots[[i]][[j]][[k]])
                }
            }
        }
        return(dots[[1]])
    }

    for(i in seq(along=dots)) {
        if(!inherits(dots[[i]], "scantwoperm"))
            stop("Input should have class \"scantwoperm\".")
    }

    nc <- sapply(dots, function(a) ncol(a[[1]]))
    if(length(unique(nc)) != 1)
        stop("Number of LOD columns in the input objects must be constant.\n")

    flag <- 0
    for(j in 1:length(dots[[1]])) {
        for(i in 2:length(dots)) {
            dots[[1]][[j]] <- rbind(dots[[1]][[j]], dots[[i]][[j]])
            if(any(colnames(dots[[i]][[j]]) != colnames(dots[[1]][[j]]))) flag <- 1
        }
    }
    if(flag) warning("Mismatch in column names; input may not be consistent.\n")

    dots[[1]]
}

# paste columns together
cbind.scantwoperm <-
function(...)
{
    dots <- list(...)
    if(length(dots)==1 && is.list(dots[[1]])) dots <- dots[[1]]

    if(length(dots)==1) return(dots)

    xchrsp <- vapply(dots, function(a) "AA" %in% names(a), TRUE)
    if(any(xchrsp)) {
        if(!all(xchrsp)) stop("Some but not all inputs are X-chr specific")

        for(i in 2:length(dots)) {
            for(j in seq(along=dots[[1]])) {
                for(k in seq(along=dots[[1]][[j]])) {
                    if(nrow(dots[[1]][[j]][[k]]) != nrow(dots[[i]][[j]][[k]]))
                        stop("Mismatch in no. permutations")
                    dots[[1]][[j]][[k]] <- cbind(dots[[1]][[j]][[k]], dots[[i]][[j]][[k]])
                }
            }
        }
        return(dots[[1]])
    }

    for(i in 2:length(dots)) {
        for(j in seq(along=dots[[1]])) {
            if(nrow(dots[[1]][[j]]) != nrow(dots[[i]][[j]]))
                stop("Mismatch in no. permutations")
            dots[[1]][[j]] <- cbind(dots[[1]][[j]], dots[[i]][[j]])
        }
    }

    dots[[1]]
}



######################################################################
# condensed scantwo output

condense <-
    function(object)
    UseMethod("condense")

condense.scantwo <-
    function(object)
{
    out <- subrousummaryscantwo(object, for.perm=FALSE)
    class(out) <- c("scantwocondensed", "list")
    out
}

summary.scantwocondensed <- summary.scantwo
max.scantwocondensed <- max.scantwo


##############################
# subset.scantwoperm: pull out a set of lodcolumns
##############################
subset.scantwoperm <-
    function(x, repl, lodcolumn, ...)
{
    if("AA" %in% names(x)) { # x chr specific
        for(j in seq(along=x)) {
            if(missing(lodcolumn)) lodcolumn <- 1:ncol(x[[j]][[1]])
            else if(!check_colindex(lodcolumn, x[[j]][[1]]))
                stop("lodcolumn misspecified.")

            repl <- 1:nrow(x[[j]][[1]])

            x[[j]] <- lapply(x[[j]], function(a,b,d) unclass(a)[b,d,drop=FALSE], repl, lodcolumn)
        }
        return(x)
    }

    att <- attributes(x)

    if(any(!sapply(x, is.matrix)))
        x <- lapply(x, as.matrix)

    if(missing(lodcolumn)) lodcolumn <- 1:ncol(x[[1]])
    else if(!check_colindex(lodcolumn, x[[1]]))
        stop("lodcolumn misspecified.")

    if(missing(repl)) repl <- 1:nrow(x[[1]])
    else if(!check_rowindex(repl, x[[1]]))
        stop("repl misspecified.")

    cl <- class(x)
    x <- lapply(x, function(a,b,d) unclass(a)[b,d,drop=FALSE], repl, lodcolumn)
    class(x) <- cl

    for(i in seq(along=att)) {
        if(names(att)[i] == "dim" || length(grep("names", names(att)[i]))>0) next
        attr(x, names(att)[i]) <- att[[i]]
    }

    x
}

# subset.scantwoperm using [,]
`[.scantwoperm` <-
    function(x, repl, lodcolumn)
    subset.scantwoperm(x, repl, lodcolumn)

# end of summary.scantwo.R
kbroman/qtl documentation built on Jan. 13, 2024, 10:14 p.m.