From 84bb060ccf6b1b53b820d0adcc77ba3180883c48 Mon Sep 17 00:00:00 2001 From: Voorrips <roeland.voorrips@wur.nl> Date: Tue, 17 Nov 2020 10:07:49 +0100 Subject: [PATCH] Last changes (hopefully) for publication: demo data for vignette changed, removed the "options" way to specify the ahcdir and to load the ahc(complete)lists, other minor internal changes --- DESCRIPTION | 2 +- R/PolyHaplotyper.R | 851 ++++++++++-------- data/PolyHaplotyper_demo.RData | Bin 0 -> 15313 bytes data/demo.RData | Bin 14821 -> 0 bytes ...otyper-vignette.Rmd => PolyHaplotyper.Rmd} | 154 ++-- vignettes/ahclist_6x.RData | Bin 4540 -> 0 bytes 6 files changed, 548 insertions(+), 459 deletions(-) create mode 100644 data/PolyHaplotyper_demo.RData delete mode 100644 data/demo.RData rename vignettes/{PolyHaplotyper-vignette.Rmd => PolyHaplotyper.Rmd} (77%) delete mode 100644 vignettes/ahclist_6x.RData diff --git a/DESCRIPTION b/DESCRIPTION index 5a57615..25a2a96 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -10,7 +10,7 @@ Description: Infer the genetic composition of individuals License: GPL-2 Encoding: UTF-8 LazyData: true -RoxygenNote: 7.0.2 +RoxygenNote: 7.1.1 Imports: XML Suggests: knitr, rmarkdown VignetteBuilder: knitr diff --git a/R/PolyHaplotyper.R b/R/PolyHaplotyper.R index 8834bfe..2b96e61 100644 --- a/R/PolyHaplotyper.R +++ b/R/PolyHaplotyper.R @@ -61,21 +61,18 @@ #'@importFrom utils combn flush.console read.table write.table #'@importFrom stats chisq.test pchisq pbinom qbinom -#the following line serves to stop devtools::check complaining about -#some identifiers that do exist but it doesn't find because they are -#loaded from RData files and/or created in the global evironment: -utils::globalVariables(names=c("ahclist", "ahccompletelist", "ahcinfo"), - add=TRUE) - -# documentation of the objects in Data/demo.RData: +# documentation of the objects in Data/PolyHaplotyper_demo.RData: +# #'@title dosages of SNP alleles -#'@description A data.frame with the allele dosages for 28 SNPs in 661 -#'individuals +#'@description A data.frame with the allele dosages for 30 SNPs in 661 +#'isamples #'@docType data #'@keywords datasets #'@name snpdos -#'@format a matrix with 28 rows (SNPs) and 661 columns (samples) containing -#'dosages (integers in 0..6 or NA) +#'@format a data.frame with 30 rows (SNPs) and 663 columns. The first +#'two columns contain the SNP name and contig; the remaining columns +#'contain the allele dosages (integers in 0..6 or NA) of the SNPs in +#'661 samples NULL #'@title pedigree @@ -83,7 +80,7 @@ NULL #'@docType data #'@keywords datasets #'@name ped -#'@format a data.frame with 672 rows (individuals, some replicated) and +#'@format a data.frame with 661 rows (samples) and #'4 columns: genotype, mother, father, sample_nr NULL @@ -109,167 +106,159 @@ allHaplotypes <- function(mrknames) { eg } #allHaplotypes -getahcdir <- function() { - # for internal use only; get ahcdir from options("PolyHaplotyper_ahcdir"); - # if that doesn't exist, return working dir +getahcdir <- function(ahcdir) { + # for internal use only; get ahcdir from (1) param ahcdir or (2) working dir # result ends always with "/" so a filename can be pasted onto it - ad <- unlist(options("PolyHaplotyper_ahcdir")) - if (is.null(ad)) { - ad <- getwd() - } else { - if (!is.character(ad) || length(ad)!= 1 || !dir.exists(ad) || ad == "") - ad <- "./" + if (is.null(ahcdir)) ahcdir <- getwd() else { + if (!is.character(ahcdir) || length(ahcdir) != 1) + stop(paste("invalid ahcdir:", ahcdir)) + if (ahcdir=="") ahcdir <- getwd() } - if (substr(ad, nchar(ad), nchar(ad)) != "/") - ad <- paste0(ad, "/") - ad -} #getahcdir within loadAllHaploCombList - - -loadAllHaploCombList <- function(ploidy) { - # Function is called only by getAllHapcomb. - # Load ahclist and ahccompletelist into GlobalEnv if needed. - # Checks variable ahcinfo in GlobalEnv. If that doesn't exist or ahcinfo$ploidy - # is not equal to ploidy, attempts to load ahclist and ahccompletelist into - # the GlobalEnv from a file 'ahclist_nx.RData' and 'ahccompletelist_nx.RData' - # (where n is the ploidy). These files are loaded from the working directory - # or, if options("PolyHaplotyper_ahcdir" is specified, from that directory. - # If that is successful, ploidy is stored in ahcinfo$ploidy - # in the GlobalEnv, else ahcinfo, ahclist, and ahccompletelist are not created - # or changed. - # Return value is NULL. - if (exists("ahcinfo", envir=.GlobalEnv, inherits==FALSE) && - exists("ahclist", envir=.GlobalEnv, inherits==FALSE)) { - if (ahcinfo$ploidy == ploidy) return(NULL) - #save any unsaved ahclist: - if (ahcinfo$unsaved) { - #the currently loaded ahclist is for a different ploidy and unsaved; - #we save it (to the old ahcdir!) before loading the ahclist for the - #current ploidy: - save(ahclist, - file=paste0(ahcinfo$ahcdir, "ahclist_", ahcinfo$ploidy,"x.RData")) + if (!dir.exists(ahcdir)) + stop(paste("non-existing ahcdir:", ahcdir)) + if (substr(ahcdir, nchar(ahcdir), nchar(ahcdir)) != "/") + ahcdir <- paste0(ahcdir, "/") + ahcdir +} #getahcdir + +loadAllHapcombLists <- function(ploidy, nmrk, ahcdir) { + # This function is called only at start of inferHaplotypes + # ploidy: 1 integer + # nmrk: 1 integer, the number of markers in the haploblock + # result: a list with elements ahccompletelist, ahclist, complete_nmrk, + # ploidy, ahcdir + + checklist <- function(lst, ploidy, namespresent) { + if (!is.list(lst) || length(lst) < 1) return(FALSE) + if (!all(sapply(lst, class) == "list")) return(FALSE) + if (length(lst[[length(lst)]]) == 0) return(FALSE) # in highest nmrk at least one mrkdid should be present + cl <- lapply(lst, FUN=function(x) sapply(x, class)) + if (!all(unlist(cl) == "matrix")) return(FALSE) + cl <- lapply(lst, FUN=function(x) sapply(x, nrow)) + if (!all(unlist(cl) == ploidy)) return(FALSE) + lnames <- names(lst[[length(lst)]]) + if (xor(!namespresent, (is.null(lnames) || any(lnames == "")))) return(FALSE) + return(TRUE) + } #checklist + + loadlist <- function(listname, ploidy, ahcdir, namespresent, message) { + # try to load: + if (message) cat(paste("loading ", listname, "...")) + x <- tryCatch({ + fname <- paste0(ahcdir, listname, "_", ploidy, "x.RData") + suppressWarnings(n <- load(fname)) + if (!(listname %in% n)) + return(paste("file", fname, "does not contain", listname)) + }, error=function(e) "error") + if (identical(x, "error")) { + if (message) cat (" not found!\n") + return("") # not an error but no list file + } else { + #checks: + if (!checklist(get(listname), ploidy, namespresent)) + return(paste(listname, " has incorrect format!\n")) + if (message) cat (" done!\n") + return(get(listname)) } - suppressWarnings(rm(ahcinfo, ahclist, ahccompletelist, - envir=.GlobalEnv, inherits==FALSE)) - } - newinfo <- list() - newinfo$ploidy <- ploidy - newinfo$unsaved <- FALSE - newinfo$complete_nmrk <- 0 - newinfo$ahcdir <- getahcdir() - #load the ahccompletelist: - cat("loading ahccompletelist ...") - x <- tryCatch( - suppressWarnings(load(paste0(newinfo$ahcdir, "ahccompletelist_", - ploidy,"x.RData"), - envir=.GlobalEnv)), - error=function(e) "error") - if (identical(x, "error")) { - cat (" not found!\n") + } #loadlist + + + ahcinfo <- list(ahccompletelist=NULL, ahclist=NULL, complete_nmrk=0, + ploidy=ploidy, ahcdir=getahcdir(ahcdir)) + if (exists("ahccompletelist") && # in .GlobalEnv, loaded by user + checklist(lst=ahccompletelist, ploidy=ploidy, namespresent=FALSE)) { + ahcinfo$ahccompletelist <- ahccompletelist } else { - #checks: - if (length(ahccompletelist) == 0) stop(" ahccompletelist empty!") - if (is.null(ahccompletelist[[1]][[1]]) || - !is.matrix(ahccompletelist[[1]][[1]]) || - nrow(ahccompletelist[[1]][[1]]) != ploidy) - stop(" ahccompletelist has incorrect format!\n") - - newinfo$complete_nmrk <- length(ahccompletelist) - cat (" done!\n") + ahccompletelist <- + loadlist(listname="ahccompletelist", ploidy=ploidy, ahcdir=ahcinfo$ahcdir, + namespresent=FALSE, message=TRUE) + if (is.list(ahccompletelist)) { + ahcinfo$ahccompletelist <- ahccompletelist + } else if (ahccompletelist != "") stop(ahccompletelist) + } + if (!is.null(ahcinfo$ahccompletelist)) + ahcinfo$complete_nmrk <- length(ahcinfo$ahccompletelist) + if (ahcinfo$complete_nmrk < nmrk) { + # we need an additional ahclist for the mrkdids with larger nmrk + if (exists("ahclist") && # in .GlobalEnv, loaded by user + checklist(ahclist, ploidy, namespresent=TRUE)) { + ahcinfo$ahclist <- ahclist + } else { + ahclist <- loadlist("ahclist", ploidy, ahcinfo$ahcdir, + namespresent=TRUE, message=FALSE) + if (is.list(ahclist)) { + ahcinfo$ahclist <- ahclist + } else { + if (ahclist != "") stop(ahclist) + ahcinfo$ahclist <- list() # new empty ahclist + } + } } - #load the ahclist: - x <- tryCatch( - suppressWarnings(load(paste0(newinfo$ahcdir, "ahclist_", - ploidy,"x.RData"), - envir=.GlobalEnv)), - error=function(e) "error") - if (identical(x, "error")) - #create new ahclist: - assign("ahclist", value=list(), envir=.GlobalEnv, inherits=FALSE) - #update ahcinfo: - assign("ahcinfo", value=newinfo, envir=.GlobalEnv, inherits=FALSE) - NULL -} #loadAllHaploCombList + ahcinfo +} # loadAllHapcombLists #'@title get all haplotype combinations that result in the given marker #'dosages #'@description get all haplotype combinations that result in the given #'marker dosages -#'@usage getAllHapcomb(mrkDosage=NULL, did=NULL, allhap, ploidy, -#'writeFile=TRUE, progress=FALSE) +#'@usage getAllHapcomb(mrkDosage=NULL, mrkdid=NULL, nmrk, ahcinfo) #'@param mrkDosage an integer vector with the dosages of each marker in one -#'individual, with the markers in the same order as in the columns of allhap. -#'Either mrkDosage or did must be specified, if both are specified they -#'should match but this is not checked -#'@param did dosage ID: in combination with ploidy specifying the mrkDosage. -#'Either mrkDosage or did must be specified, if both are specified they -#'should match but this is not checked -#'@param allhap a matrix as returned by allHaplotypes, for the same markers -#'in the same order as mrkDosage -#'@param ploidy the ploidy of the individual; mrkDosage should all be in -#'0:ploidy -#'@param writeFile The calculated haplotype combis are stored in list -#'ahclist in GlobalEnv. Default writeFile TRUE ensures that this list is written -#'to file each time a new mrkdid is calculated. With writeFile FALSE the list is -#'not written to file. -#'@param progress whether to print a message when starting calculations -#'for a did; default FALSE +#'individual; all dosages must be in 0:ahcinfo$ploidy or NA +#'Either mrkDosage or mrkdid must be specified, not both +#'@param mrkdid marker dosage ID: in combination with nmrk and ploidy specifying +#'the mrkDosage. Can be NA or an integer, numeric or character which must +#'specify an integer > 0 +#'Either mrkDosage or mrkdid must be specified, not both +#'@param nmrk the number of markers (should match mrkDosage if that is specified) +#'@param ahcinfo a list as returned by loadAllHapcombLists #'@details Each column of the return value represents one combination of #'haplotypes that yields the observed marker dosages. If any of the marker -#'dosages are NA or not in 0:ploidy, a matrix with 0 columns is returned.\cr -#'This function makes use of precalculated lists that are read from files -#'if present: ahccompletelist_nx.RData and ahclist_nx.RData (where n is the -#'ploidy). These lists ahccompletelist and ahclist are stored in the GlobalEnv, -#'as well as another list ahcinfo. List ahcinfo may be extended with newly -#'calculated results; if parameter writeFile is TRUE the extended ahclist is -#'written to file.\cr -#'These files ahccompletelist_nx.RData and ahclist_nx.RData are expected -#'in the working directory, or, if options("PolyHaplotyper_ahcdir") is -#'specified, from that directory. -#'@return an integer matrix with one row per haplotype (corresponding to allhap) -#'and one column per combination of haplotypes, with the number of copies -#'of each haplotype in each combination (the dosages of the haplotypes) +#'dosages, or mrkdid, are NA a matrix with 0 columns is returned.\cr +#'This function makes use of precalculated lists ahcinfo$ahccompletelist +#'and ahcinfo$ahclist; the mrkdid must be present in one of them. +#'@return an integer matrix with <ploidy> rows and one column for each haplotype +#'combination that matches the marker dosages; the haplotype numbers +#'within a column are in increasing order #'@export -getAllHapcomb <- function(mrkDosage=NULL, did=NULL, allhap, ploidy, - writeFile=TRUE, progress=FALSE) { - # TODO: implement parallel computing of combinations for new did combinations, - # https://artax.karlin.mff.cuni.cz/r-help/library/Rdsm/html/Rdsm-package.html - # seems useful (using shared memory) but may need to delay saving ahclist - # until all threads are finished? - # Alternative: package parallel, but then no writing to common variables - # like ahclist until all threads (foreach) are finished - #initial checks: - nmrk <- ncol(allhap) - if (!missing(mrkDosage) && !is.null(mrkDosage) && - (length(mrkDosage) != nmrk || !all(mrkDosage %in% 0:ploidy))) - stop("invalid mrkDosage") - if (missing(did) || is.null(did)) { - if (missing(mrkDosage) || is.null(mrkDosage)) { - stop("mrkDosage or did must be specified") - } else did <- mrkdidfun(mrkDosage, ploidy=ploidy) - } - loadAllHaploCombList(ploidy) #does nothing if list already in GlobalEnv - # first check if this nmrk is in ahccompletelist: - if (ahcinfo$complete_nmrk >= nmrk) { - return(ahccompletelist[[nmrk]][[as.numeric(did)]]) #now as hapcomb +getAllHapcomb <- function(mrkDosage=NULL, mrkdid=NULL, nmrk, ahcinfo) { + # mrkDosage has already been checked at start of inferHaplotypes, + # mrkdid are computed + # so we don't check either, only find out of did or mrkDosage specified: + if (is.null(mrkdid)) { + if (anyNA(mrkDosage)) return(matrix(integer(0), nrow=ahcinfo$ploidy)) + mrkdid <- mrkdidfun(mrkDosage, ploidy=ahcinfo$ploidy) } - # else look it up in ahclist, and if needed calculate it: - if (length(ahclist) >= nmrk) - didix <- which(names(ahclist[[nmrk]]) == did) else didix <- integer(0) - if (length(didix) == 1) { - #the haplotype combinations for this mrkdid were already calculated earlier: - return(ahclist[[nmrk]][[didix]]) # now as hapcomb + if (is.na(mrkdid)) return(matrix(integer(0), nrow=ahcinfo$ploidy)) + if (nmrk <= ahcinfo$complete_nmrk) + return(ahcinfo$ahccompletelist[[nmrk]][[as.integer(mrkdid)]]) + #debug check: + # if (nmrk>length(ahcinfo$ahclist) || + # !(mrkdid %in% names(ahcinfo$ahclist[[nmrk]]))) + # stop("mrkdid not in ahc(complete)list in getAllHaplocomb") + return(ahcinfo$ahclist[[nmrk]][[as.character(mrkdid)]]) +} # getAllHapcomb + +calcAllhapcomb4mrkdid <- function(mrkDosage=NULL, mrkdid=NULL, ploidy, allhap) { + # mrkDosage: an integer vector with the dosages (0:ploidy) of nmrk markers + # mrkdid: 1 integer (marker dosage id); if NULL, mrkDosage is used + # ploidy: 1 integer + # allhap: a matrix as returned by allHaplotypes, for nmrk markers + # in the same order as mrkDosage + # result: a matrix with <ploidy> rows and one column for each haplotype + # combination that matches mrkdid + # + # check if mrkdid or mrkDosage is valid (we assume ploidy and nmrk are valid) + nmrk <- ncol(allhap) + if (is.null(mrkdid)) { + # check mrkDosage: + if (length(mrkDosage) != nmrk || !all(mrkDosage %in% 0:ploidy)) + stop("invalid mrkDosage") + } else { + if (length(mrkdid)!=1 || is.na(mrkdid) || as.integer(mrkdid) != mrkdid || + mrkdid<1 || mrkdid > (ploidy+1)^nmrk) + stop("invalid mrkdid") + mrkDosage <- mrkdid2mrkdos(mrkdid, nmrk, ploidy) } - # did was not present in ahclist so we add it: - #we copy the list to a local tmplist, modify that, - #store it in .GlobalEnv and save it, and return the matrix with haplotype - #combinations. - if (progress) cat(paste0("getAllHapcomb: calculation for mrkdid=", did, "\n")) - tmplist <- ahclist - rm(list="ahclist", envir=.GlobalEnv, inherits=FALSE) - if (length(tmplist) < nmrk) tmplist[[nmrk]] <- list() - #calculate new matrix with haplotype combinations - if (missing(mrkDosage)) mrkDosage <- mrkdid2mrkdos(did, nmrk, ploidy) #TODO: the next algorithm is less efficient if the SNP dosages are high than if they are #low (because with low dosages a haplotype combination will sooner exceed the dosage of #at least one of the SNPs). Therefore in these cases (with average SNP dosage > ploidy/2) @@ -305,21 +294,9 @@ getAllHapcomb <- function(mrkDosage=NULL, did=NULL, allhap, ploidy, } if (ncol(allmat1) == 0) stop("getAllHapcomb: 0 solutions found") colnames(allmat1) <- NULL - #reverse the row order and save to tmplist and ahclist: - didix <- length(tmplist[[nmrk]]) + 1 - tmplist[[nmrk]][[didix]] <- allmat1[nrow(allmat1):1,, drop=FALSE] - names(tmplist[[nmrk]])[didix] <- did - assign("ahclist", value=tmplist, envir=.GlobalEnv, inherits=FALSE) - rm(tmplist) - if (writeFile) { - save(ahclist, file=paste0(ahcinfo$ahcdir, "ahclist_", ploidy,"x.RData")) - } else { - tmpinfo <- ahcinfo - tmpinfo$ahcunsaved <- TRUE - assign("ahcinfo", value=tmpinfo, envir=.GlobalEnv, inherits=FALSE) - } - allmat1 -} #getAllHapcomb + #reverse the row order: + allmat1[nrow(allmat1):1,, drop=FALSE] +} # calcAllhapcomb4mrkdid getHapcombCount_1mrk <- function(mrkdosage, ploidy, nhap) { # mrkdosage: the dosage of the marker (integer of length 1) @@ -435,7 +412,8 @@ completeAllHaploComb <- function(ploidy, nmrk, savesec=1800, printsec=60, lastprint <- proc.time() if ((lastsave-lastprint)[3] > savesec) { save(ahc, file=paste0(fname, "_", paste(allset, collapse="-"), - ".RData")) + ".RData"), + version=2) if (file.exists(lastfile)) file.remove(lastfile) lastfile <- fname lastsave <- proc.time() @@ -451,7 +429,7 @@ completeAllHaploComb <- function(ploidy, nmrk, savesec=1800, printsec=60, for (d in seq_along(ahc)) { ahc[[d]] <- ahc[[d]][ploidy:1, 1:lasthap[d], drop=FALSE] } - save(ahc, file=paste0(fname, ".RData")) + save(ahc, file=paste0(fname, ".RData"), version=2) if (file.exists(lastfile)) file.remove(lastfile) invisible(ahc) } #completeAllHaploComb @@ -513,7 +491,9 @@ build_ahccompletelist <- function(ploidy, maxmrk, savesec=1800, printsec=300, stop("overwrite must be FALSE or TRUE") if (file.exists(fname)) { # in working directory otherloc <- FALSE - load(fname) # contains ahccompletelist + x <- load(fname) # contains ahccompletelist + if (length(x) != 1 || x !="ahccompletelist") + stop(paste("the existing file", fname, "does not contain (only) ahccompletelist")) if (length(ahccompletelist) == maxmrk || (length(ahccompletelist) > maxmrk && !shorten)) { # file in working dir already ok @@ -534,7 +514,7 @@ build_ahccompletelist <- function(ploidy, maxmrk, savesec=1800, printsec=300, if (length(ahccompletelist) >= maxmrk) { ahccompletelist <- ahccompletelist[1:newmaxmrk] file.rename(from=fname, to=paste0(fname,".backup")) - save(ahccompletelist, file=fname) + save(ahccompletelist, file=fname, version=2) file.remove(paste0(fname,".backup")) } else { if (totHapcombCount(ploidy=ploidy, nmrk=maxmrk) > 2e9) { @@ -549,7 +529,7 @@ build_ahccompletelist <- function(ploidy, maxmrk, savesec=1800, printsec=300, completeAllHaploComb(ploidy=ploidy, nmrk=nmrk, savesec=savesec, printsec=printsec, fname=paste0(tmpfname, nmrk)) - save(ahccompletelist, file=fname) + save(ahccompletelist, file=fname, version=2) cat(paste0("saved ", fname, "for block sizes up to ", nmrk, "markers\n")) file.remove(paste0(tmpfname, nmrk, ".RData")) #left by completeAllHaploComb file.remove(paste0(fname,".backup")) @@ -1033,27 +1013,27 @@ getHaplotypeNames <- function(haploblock, hapcount, sep="_") { paste0(haploblock, sep, padded(1:hapcount)) } -##!title Assign haplotype combinations based on output of inferHaps_noFSs -##!description Assign haplotype combinations based on output of inferHaps_noFSs +##!title Assign haplotype combinations based on output of inferHaps_noFS +##!description Assign haplotype combinations based on output of inferHaps_noFS ##!usage hapcomb_from_IHlist(ihl, mrkdids, ploidy, nocombs=FALSE) ##!param haploblockname Prefix to which the (zero-padded) haplotype numbers ##!are appended with separator '_'. -##!param ihl A list as returned by inferHaps_noFSs +##!param ihl A list as returned by inferHaps_noFS ##!param mrkdids the mrkdids of all individuals -##!param ploidy the ploidy (the number of rows in the retrned matrix) +##!param ahcinfo a list as returned by loadAllHapcombLists ##!param nocombs if FALSE (default) the returned list does not have the matrix ##!hapcombs, only the vector ahccols -##!details The results of inferHaps_noFSs, as passed in parameter ihl, determine +##!details The results of inferHaps_noFS, as passed in parameter ihl, determine ##!the haplotype combinations that match each mrkdid. This function translates ##!these results in a set of haplotype combinations for each individual. ##!return a list with ##!2 element: $hapcombs is the matrix as before, $ahccols is an integer vector ##!with for each indiv the column number in its mrkdid element of ##!ahc(complete)list (or NA if more than 1 ahccol possible). -hapcomb_from_IHlist <- function(ihl, mrkdids, ploidy, nocombs=FALSE) { +hapcomb_from_IHlist <- function(ihl, mrkdids, ahcinfo, nocombs=FALSE) { #haplotypeNames <- getHaplotypeNames(haploblockname, nrow(ihl$allhap)) nind <- sum(ihl$mrkdidsTable) - hapcombs <- matrix(NA_integer_, nrow=ploidy, #length(haplotypeNames), + hapcombs <- matrix(NA_integer_, nrow=ahcinfo$ploidy, #length(haplotypeNames), ncol=length(mrkdids), dimnames=list(NULL, names(mrkdids))) # we also return for each indiv the column nr in ahc(complete)list @@ -1067,10 +1047,10 @@ hapcomb_from_IHlist <- function(ihl, mrkdids, ploidy, nocombs=FALSE) { if (length(ihl$hclist[[i]]) == 1) { #we don't assign a haplotype combination if there is more than #one combination possible - #hapcombs[, mrkdidind] <- ihl$hclist[[i]][, 1] ahccols[mrkdidind] <- ihl$hclist[[i]] if (!nocombs) hapcombs[, mrkdidind] <- - getAllHapcomb(did=did, allhap=ihl$allhap, ploidy=ploidy)[,ihl$hclist[[i]]] + getAllHapcomb(mrkdid=did, nmrk=ncol(ihl$allhap), + ahcinfo=ahcinfo)[,ihl$hclist[[i]]] } } res <- list(ahccols=ahccols) @@ -1184,9 +1164,9 @@ checkmrkDosage <- function(mrkDosage, ploidy, indiv=NULL, markers=NULL, } #checkmrkDosage -single_cycle_infer <- function(allhap, mrkdidsTable, ploidy, knownHap, +single_cycle_infer <- function(allhap, mrkdidsTable, ahcinfo, knownHap, useKnownDosage, progress) { - #function used only by inferHaps_noFSs + #function used only by inferHaps_noFS #For each mrkdid, select the hapcombs that require the least new haplotypes #(not present in knownHap); if useKnownDosages is TRUE, among these select the #hapcombs with the maximum dosage of known haplotypes. @@ -1209,8 +1189,8 @@ single_cycle_infer <- function(allhap, mrkdidsTable, ploidy, knownHap, for (i in seq_along(mrkdidsTable)) { # for the current mrkdid, get the matrix with all possible # haplotype combinations (different combinations in columns): - ahc <- getAllHapcomb(did=names(mrkdidsTable)[i], - allhap=allhap, ploidy=ploidy, progress=progress) + ahc <- getAllHapcomb(mrkdid=names(mrkdidsTable)[i], + nmrk=ncol(allhap), ahcinfo=ahcinfo) ahccols <- 1:ncol(ahc) if (length(knownHap) > 0) { # Based on comparisons of 20171023, testscheme 1 is clearly worse, so: @@ -1268,11 +1248,11 @@ single_cycle_infer <- function(allhap, mrkdidsTable, ploidy, knownHap, ##!title infer haplotypes from marker dosages ##!@description infer haplotypes from marker dosages for a single haploblock, ##!treating all individuals as unrelated -##!usage inferHaps_noFSs(mrkdids, ploidy, markers, +##!usage inferHaps_noFS(mrkdids, ahcinfo, markers, ##minfrac=c(0.1, 0.01), knownHap=integer(0), useKnownDosage=TRUE, progress=TRUE) ##!param mrkdids a named integer vector with for each indiv its mrkdid; the ##names are the individual names. -##!param ploidy all marker dosages should be in 0:ploidy or NA +##!param ahcinfo a list produced by loadAllHapcombFiles ##!param markers a character vector with the names of the markers in the ##haploblock, in the same order as used for calculating the mrkdids ##!param minfrac a vector of 1 or 2 fractions, the second smaller than the @@ -1340,7 +1320,7 @@ single_cycle_infer <- function(allhap, mrkdidsTable, ploidy, knownHap, ##!for each of the remaining haplotype combinations: (a subset of the columns ##!of) the matrix returned by getAllHapcomb for that mrkdid} ##!} -inferHaps_noFSs <- function(mrkdids, ploidy, markers, +inferHaps_noFS <- function(mrkdids, ahcinfo, markers, minfrac=c(0.1, 0.01), knownHap=integer(0), useKnownDosage=TRUE, progress=TRUE) { @@ -1359,7 +1339,7 @@ inferHaps_noFSs <- function(mrkdids, ploidy, markers, while (TRUE) { cycle <- cycle + 1 res <- single_cycle_infer(allhap=allhap, mrkdidsTable=mrkdidsTable, - ploidy=ploidy, knownHap=knownHap, + ahcinfo=ahcinfo, knownHap=knownHap, useKnownDosage=useKnownDosage[ukd], progress=progress) knownNew <- sort(union(origHap, which(res$nPresent >= minfrac[1] * nind))) @@ -1369,7 +1349,7 @@ inferHaps_noFSs <- function(mrkdids, ploidy, markers, #only be gained, not lost (except the origHap which need not be supported #by the current data, but these have been added back to knownNew). #Therefore the following warning was generated if haplotypes were lost: - #warning(paste0("inferHaps_noFSs: lost in cycle ", cycle, ": ", + #warning(paste0("inferHaps_noFS: lost in cycle ", cycle, ": ", # paste(haploLost, collapse=" "))) #However it seems that it is possible to lose previous must-have #haplotypes, although this does not happen often. @@ -1384,7 +1364,7 @@ inferHaps_noFSs <- function(mrkdids, ploidy, markers, #recalculate the result of the first cycle - very fast and avoids #having to store it: res <- single_cycle_infer(allhap=allhap, mrkdidsTable=mrkdidsTable, - ploidy=ploidy, knownHap=origHap, + ahcinfo=ahcinfo, knownHap=origHap, useKnownDosage=useKnownDosage[ukd], progress=FALSE) @@ -1404,7 +1384,7 @@ inferHaps_noFSs <- function(mrkdids, ploidy, markers, if (length(useKnownDosage) == 2 && length(knownHap_ukd) == 1 && !is.null(knownHap_ukd[[1]])) { res <- single_cycle_infer(allhap=allhap, mrkdidsTable=mrkdidsTable, - ploidy=ploidy, knownHap=knownHap_ukd[[1]], + ahcinfo=ahcinfo, knownHap=knownHap_ukd[[1]], useKnownDosage=FALSE, progress=FALSE) knownHap <- sort(union(origHap, which(res$nPresent >= minfrac[1] * nind))) @@ -1432,11 +1412,11 @@ inferHaps_noFSs <- function(mrkdids, ploidy, markers, names(hac1comb) <- names(mrkdidsTable)[onecomb] # a vector with the mrkdids as names and the ahc column nr as values # for the mrkdids with just 1 hapcomb - hapcomb <- matrix(NA_integer_, nrow=ploidy, ncol=length(hac1comb)) + hapcomb <- matrix(NA_integer_, nrow=ahcinfo$ploidy, ncol=length(hac1comb)) for (d in seq_along(hac1comb)) { hapcomb[, d] <- - getAllHapcomb(did=names(hac1comb)[d], allhap=allhap, - ploidy=ploidy)[, hac1comb[d]] + getAllHapcomb(mrkdid=names(hac1comb)[d], nmrk=ncol(allhap), + ahcinfo=ahcinfo)[, hac1comb[d]] } #calculate the nr of individuals in which each haplotype occurs: #select the mrkdids for which only 1 haplotype combination is possible, @@ -1446,10 +1426,11 @@ inferHaps_noFSs <- function(mrkdids, ploidy, markers, final_hap <- sort(union(origHap, which (hapfrq >= 2 & hapfrq > minfrac[2]*sum(mrkdidsTable)))) - resfinal <- single_cycle_infer(allhap=allhap, mrkdidsTable=mrkdidsTable, - ploidy=ploidy, knownHap=final_hap, - useKnownDosage=useKnownDosage[length(useKnownDosage)], - progress=progress) + resfinal <- + single_cycle_infer(allhap=allhap, mrkdidsTable=mrkdidsTable, + ahcinfo=ahcinfo, knownHap=final_hap, + useKnownDosage=useKnownDosage[length(useKnownDosage)], + progress=progress) mrkdidHaccountfinal <- vapply(resfinal$hclist[names(mrkdidsTable)], FUN=length, FUN.VALUE=0) #keep resfinal instead of res only if all mrkdids that had only one @@ -1480,7 +1461,7 @@ inferHaps_noFSs <- function(mrkdids, ploidy, markers, res$allhap <- allhap res$mrkdidsTable <- mrkdidsTable res -} #inferHaps_noFSs +} #inferHaps_noFS chisq.probtest <- function(x, p=rep(1/length(x), length(x)), @@ -1740,7 +1721,7 @@ testFSseg <- function(parhac, DRrate, errfrac, ##!usage solveOneFS(mrkDosage, ihl, FS, P, priorPhac, hacXmat, maxparcombs, ##!ploidy, minfrac, errfrac, DRrate, minPseg) ##!param mrkDosage dosage matrix with only the markers in the current haploblock -##!param ihl a list returned by inferHaps_noFSs +##!param ihl a list returned by inferHaps_noFS ##!param FS character vector with sample names of the current FS ##!param P vector with 2 sample names, one for parent1 and one for parent2. ##!There should be corresponding columns in mrkDosage , and orig_ihl should be @@ -1762,7 +1743,7 @@ testFSseg <- function(parhac, DRrate, errfrac, ##!considered to be certainly present if it occurs in at least a fraction ##!minfrac[1] of all individuals; in the final stage for the "other" ##!individuals (those that do not belong to the FS or its parents) this fraction -##!is lowered to minfrac[2]; see also inferHaps_noFSs +##!is lowered to minfrac[2]; see also inferHaps_noFS ##!param errfrac the assumed fraction marker genotypes with an error (over all ##!markers in the haploblock). The errors are assumed to be uniformly distributed ##!over all except the original marker dosage combinations (mrkdids) @@ -1798,10 +1779,10 @@ testFSseg <- function(parhac, DRrate, errfrac, ##! NEW: haxXmat not used or output anymore # solveOneFS is not exported: internal function solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, - ploidy, minfrac, errfrac, DRrate, minPseg) { + ahcinfo, minfrac, errfrac, DRrate, minPseg) { calcParcombok <- function(potPhacs, oldPhacs, allhap, Pdids, FSfrac, - P.reject=c(0.05, 0.001)) { + P.reject=c(0.05, 0.001), ahcinfo) { # Function within solveOneFS # Calculates the combinations of parental haplotype combinations # that can explain (almost) all FS individuals @@ -1818,6 +1799,7 @@ solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, # Pdids: vector of 2 integers: the parental mrkdids # P.reject: two P values, decending: used in a binomial test for the # nr of unexplained FS indiv + # ahcinfo: a list returned by loadAllHapcombLists #TODO: parallellize; see also getAllHapcomb # Phacs are column numbers in ahc(complete)list if (all(potPhacs[[1]] %in% oldPhacs[[1]]) && @@ -1837,10 +1819,10 @@ solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, if (isnewpc) { #this is a new combination, check: FScombs <- getFScombs(cbind( - getAllHapcomb(did=Pdids[1], allhap=allhap, - ploidy=ploidy)[, potPhacs[[1]][pc1]], - getAllHapcomb(did=Pdids[2], allhap=allhap, - ploidy=ploidy)[, potPhacs[[2]][pc2]])) + getAllHapcomb(mrkdid=Pdids[1], nmrk=ncol(allhap), + ahcinfo=ahcinfo)[, potPhacs[[1]][pc1]], + getAllHapcomb(mrkdid=Pdids[2], nmrk=ncol(allhap), + ahcinfo=ahcinfo)[, potPhacs[[2]][pc2]])) FSmrkdos <- hapcomb2mrkdos(FScombs, allhap=allhap) FScombmrkdids <- colnames(FSmrkdos) missedmrkdids <- setdiff(names(FSmrkdidsTable), FScombmrkdids) @@ -1886,7 +1868,7 @@ solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, } #calcParcombok within solveOneFS calcParcombstats <- function(pco, parcombok, Pdids, parcombstats, - didNhapcombLst, allhap, ploidy) { + didNhapcombLst, allhap, ahcinfo) { # pco: number, parental combination (index to parcombok rows) # Pdids: vector of 2 integers: the parental mrkdids # parcombstats: list with statistics for each parental combination, see @@ -1908,10 +1890,10 @@ solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, # $expHCfreq: vector of the expected hac frequencies in order of hapcomb # $dupdid: logical vector, TRUE where mrkdid[i] == mrkdid[i-1], else FALSE parhac <- cbind( - getAllHapcomb(did=Pdids[1], allhap=allhap, - ploidy=ploidy)[, parcombok[pco, 1]], - getAllHapcomb(did=Pdids[2], allhap=allhap, - ploidy=ploidy)[, parcombok[pco, 2]]) + getAllHapcomb(mrkdid=Pdids[1], nmrk=ncol(allhap), + ahcinfo=ahcinfo)[, parcombok[pco, 1]], + getAllHapcomb(mrkdid=Pdids[2], nmrk=ncol(allhap), + ahcinfo=ahcinfo)[, parcombok[pco, 2]]) # parhac does not contain NAs! segdat <- testFSseg(parhac=parhac, DRrate=DRrate, errfrac=errfrac, FSmrkdidsTable=FSmrkdidsTable, allhap=allhap) @@ -1947,7 +1929,7 @@ solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, tmp <- calcParcombstats(pco=pco, parcombok=parcombok, Pdids=Pdids, parcombstats=parcombstats, didNhapcombLst=didNhapcombLst, - allhap=allhap, ploidy=ploidy) + allhap=allhap, ahcinfo=ahcinfo) parcombstats <- tmp$parcombstats didNhapcombLst <- tmp$didNhapcombLst } # for pco @@ -1978,7 +1960,7 @@ solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, Phacs <- vector("list", 2) #Phacs: Parental haplotype combinations (column nrs in ahc(complete)list) for (p in 1:2) { if (is.na(Pdids[p])) { - Phacs[[p]] <- integer(0) # matrix(nrow=ploidy, ncol=0) + Phacs[[p]] <- integer(0) } else { #get the parental haplotype combinations based on ihl: if (is.na(knownPhac[p])) { @@ -2032,7 +2014,7 @@ solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, parcombok <- calcParcombok(potPhacs=Phacs, oldPhacs=PahccolsTried, allhap=ihl$allhap, Pdids=Pdids, - FSfrac=FSfrac) + FSfrac=FSfrac, ahcinfo=ahcinfo) PahccolsTried <- Phacs step1pc <- parcombok$setaside_pcs parcombok <- parcombok$parcombok @@ -2045,16 +2027,17 @@ solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, maxP <- max(maxP, parcombstats$P, na.rm=TRUE) #print(paste("firstcombs, firstsel: P=", parcombstats$P, "length(sel)=", length(sel))) if (length(sel) == 0) { - #No parental combinations from inferHaps_noFSs, preselected with + #No parental combinations from inferHaps_noFS, preselected with #stringent P.reject, fit #1. See how many parental combinations are possible without first - # inferHaps_noFSs (i.e. with getAllHapcomb). + # inferHaps_noFS (i.e. with getAllHapcomb). # If p1*p2 < maxparcombs we try them all. nwPhacs <- vector("list", 2) for (p in 1:2) { if (is.na(knownPhac[p])) { - nwPhacs[[p]] <- 1:ncol(getAllHapcomb(did=Pdids[p], allhap=ihl$allhap, - ploidy=ploidy)) + nwPhacs[[p]] <- + 1:ncol(getAllHapcomb(mrkdid=Pdids[p], nmrk=ncol(ihl$allhap), + ahcinfo=ahcinfo)) } else { nwPhacs[[p]] <- knownPhac[p] } @@ -2073,7 +2056,7 @@ solveOneFS <- function(mrkdids, ihl, FS, P, knownPhac, triedPhacs, maxparcombs, parcombok <- calcParcombok(potPhacs=nwPhacs, oldPhacs=PahccolsTried, allhap=ihl$allhap, Pdids=Pdids, - FSfrac=FSfrac) + FSfrac=FSfrac, ahcinfo=ahcinfo) for (p in 1:2) PahccolsTried[[p]] <- sort(union(PahccolsTried[[p]], nwPhacs[[p]])) step2pc <- parcombok$setaside_pcs @@ -2249,7 +2232,7 @@ checkHaploblock <- function(haploblock, mrkDosage) { # FS solutions (i.e. not all the ones present at the end of step C, # which might be conflicting between FSs in the same group) -hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, +hapOneBlock <- function(mrkDosage, hbname, ahcinfo, parents, FS, minfrac, errfrac, DRrate, dropUnused, maxparcombs, minPseg, knownHap, progress) { @@ -2257,7 +2240,8 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # mrkDosage is now checked, a matrix with only the rows for the markers # in the current haploblock # hbname: the name of the haploblock - # all other parameters as in inferHaplotypes + # all other parameters as in inferHaplotypes, plus ahcinfo: a list + # returned by loadAllHapcombLists at start of inferHaplotypes # parents and FS are checked, and FS has length 0 if no FS families defined # Return value is a list with elements: # $hapdos: a matrix with one column per individual and one row per @@ -2268,16 +2252,16 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # $mrkdids: the observed mrkdids for all individuals, in same order as the # columns of $hapdos; names are individual names # $allhap: a matrix with the composition of all haplotypes - # $FSsFit: logical vector with one element per FS family; TRUE if a (or + # $FSfit: logical vector with one element per FS family; TRUE if a (or # more than one) acceptable solution for the FS is found (although # if more than one solution they might not be used if unclear # which is the best solution) - # $FSsMessages: character vector with one message per FS. If not FSsFit then - # an error message; if FSsFit either "" or a report of mrkdids that + # $FSmessages: character vector with one message per FS. If not FSfit then + # an error message; if FSfit either "" or a report of mrkdids that # were not expected or that were not assigned a haplotype combination # because multiple possible (and likely) combinations would give the # same mrkdid - # $FSsPval: numeric vector with a P value for each FS. If one solution was + # $FSpval: numeric vector with a P value for each FS. If one solution was # chosen, the chi-squared P-value of that solution; else the maximum # P-value over all considered solutions @@ -2476,31 +2460,31 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, } # function newSolstats # hapOneBlock START #### - allmrkdids <- mrkdos2mrkdid(mrkDosage, ploidy=ploidy, check=FALSE) + allmrkdids <- mrkdos2mrkdid(mrkDosage, ploidy=ahcinfo$ploidy, check=FALSE) if (length(FS) == 0) { - ihl <- inferHaps_noFSs(mrkdids=allmrkdids, ploidy=ploidy, + ihl <- inferHaps_noFS(mrkdids=allmrkdids, ahcinfo=ahcinfo, markers=rownames(mrkDosage), minfrac=minfrac, knownHap=knownHap, useKnownDosage=TRUE, progress=progress) hapdos <- hapcomb2hapdos( hapcomb=hapcomb_from_IHlist(ihl=ihl, mrkdids=allmrkdids, - ploidy=ploidy, nocombs=FALSE)$hapcomb, + ahcinfo=ahcinfo, nocombs=FALSE)$hapcomb, nhap=nrow(ihl$allhap)) result <- list( hapdos=hapdos, mrkdids=allmrkdids, markers=colnames(ihl$allhap)) } else { - # one or more FSs + # one or more FS #first calculate ihl without using FS info: # - for elements of the returned list # - for a pre-selection of likely parental hapcomb - ihl <- inferHaps_noFSs(mrkdids=allmrkdids, ploidy=ploidy, + ihl <- inferHaps_noFS(mrkdids=allmrkdids, ahcinfo=ahcinfo, markers=rownames(mrkDosage), minfrac=minfrac[1], #without final inference knownHap=knownHap, useKnownDosage=FALSE, progress=progress) ahccols <- hapcomb_from_IHlist(ihl=ihl, mrkdids=allmrkdids, - ploidy=ploidy, nocombs=TRUE)$ahccols + ahcinfo=ahcinfo, nocombs=TRUE)$ahccols ihl <- ihl[names(ihl) != "hclist"] #sometimes big, not used here, delete nhap <- nrow(ihl$allhap) # we calculate the FS sizes per haploblock because of differences @@ -2596,7 +2580,7 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # No break, go on with new cycle solstats[[cycle]] <- newSolstats(knownHap, FS) #only the specified haplotypes # 1: for each FS find a set of acceptable solutions - ih <- inferHaps_noFSs(mrkdids=allmrkdids, ploidy=ploidy, + ih <- inferHaps_noFS(mrkdids=allmrkdids, ahcinfo=ahcinfo, markers=rownames(mrkDosage), minfrac=minfrac[1], knownHap=solstats[[cycle-1]]$knownHap, @@ -2609,7 +2593,7 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, knownPhac=solstats[[cycle-1]]$Pahccols[fs,], triedPhacs=FSdata[[fs]]$PahccolsTried, maxparcombs=maxparcombs, - ploidy=ploidy, minfrac=minfrac, errfrac=errfrac, + ahcinfo=ahcinfo, minfrac=minfrac, errfrac=errfrac, DRrate=DRrate, minPseg=minPseg) # we update FSdata[[fs]]: FSdata[[fs]]$message <- sof$message @@ -2789,7 +2773,7 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # knownHap for this cycle # - mark the hacs of the parent(s) with the same hac over all # solutions as "known" - # - mark all FSs in group as FSsFit + # - mark all FSs in group as FSfit tmpapc <- allparcombs[bestcomb,, drop=FALSE] # limit the solstats[[cycle]]$pcos to those allowed in any @@ -2816,8 +2800,8 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # for each parent we determine the haplotypes common to all selected # solutions and add these to the knownHap for this cycle: for (p in seq_len(ncol(tmpapc))) { - phacs <- getAllHapcomb(did=parmrkdids[p], allhap=ihl$allhap, - ploidy=ploidy)[, tmpapc[, p], drop=FALSE] + phacs <- getAllHapcomb(mrkdid=parmrkdids[p], nmrk=ncol(ihl$allhap), + ahcinfo=ahcinfo)[, tmpapc[, p], drop=FALSE] for (i in seq_len(ncol(phacs))) { if (i == 1) { phaps <- unique(phacs[, 1]) @@ -2944,10 +2928,10 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # we assign the FS individuals from the FS calculations based on these Phacs # we assign the rest (including the non-fitted FSs and their parents) based # on the known haplotypes in solstats[[bestcycle]]$knownhap - hac <- matrix(integer(0), nrow=ploidy, ncol=ncol(mrkDosage), + hac <- matrix(integer(0), nrow=ahcinfo$ploidy, ncol=ncol(mrkDosage), dimnames=list(NULL, colnames(mrkDosage))) - FSsFit <- logical(length(FS)) # all FALSE - FSsPval <- rep(NA_real_, length(FS)) # all NA + FSfit <- logical(length(FS)) # all FALSE + FSpval <- rep(NA_real_, length(FS)) # all NA rest <- setdiff(indiv, c(parents, unlist(FS))) imputedGeno <- matrix(integer(0), nrow=nrow(mrkDosage), dimnames=list(rownames(mrkDosage), NULL)) @@ -2970,7 +2954,7 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # else: length(pcorows)==0, nrow(parcombok)==0 and message not ''; # message already contains "no solution found" or something more # informative(?), we don't overwrite it. - FSsPval[fs] <- FSdata[[fs]]$maxPseg + FSpval[fs] <- FSdata[[fs]]$maxPseg rest <- union(rest, c(parents[fs,], FS[[fs]])) # note that some parents # may still be filled in from other FSs } else { @@ -2989,19 +2973,19 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # one single selected hac for this parent, assign: Pdid <- allmrkdids[names(allmrkdids)==parents[fs, p]] hac[, colnames(hac)==parents[fs, p]] <- - getAllHapcomb(did=Pdid, allhap=ihl$allhap, - ploidy=ploidy)[, solstats[[bestcycle]]$selPahccols[fs, p]] + getAllHapcomb(mrkdid=Pdid, nmrk=ncol(ihl$allhap), + ahcinfo=ahcinfo)[, solstats[[bestcycle]]$selPahccols[fs, p]] } } if (!is.na(solstats[[bestcycle]]$selpco[fs])) { # one solution for this FS selected (but there might be more # than one solstats[[bestcycle]]$pcos[[fs]]) - FSsFit[[fs]] <- TRUE - FSsPval[fs] <- FSdata[[fs]]$Pseg[solstats[[bestcycle]]$selpco[fs]] + FSfit[[fs]] <- TRUE + FSpval[fs] <- FSdata[[fs]]$Pseg[solstats[[bestcycle]]$selpco[fs]] } else { # no selected pco for this FS but >=1 FS solutions (pcorows) found FSdata[[fs]]$message <- "multiple FS solutions" - FSsPval[fs] <- max(FSdata[[fs]]$Pseg[pcorows]) + FSpval[fs] <- max(FSdata[[fs]]$Pseg[pcorows]) } # Next we assign all FS indivs with a mrkdid that has a unique solution # over all (1 or more) possible parental combinations: @@ -3016,7 +3000,7 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, knownPhac=FSdata[[fs]]$parcombok[pcr,], #known Phac both parents triedPhacs=list(integer(0), integer(0)), #to have sof recomputed maxparcombs=maxparcombs, - ploidy=ploidy, minfrac=minfrac, errfrac=errfrac, + ahcinfo=ahcinfo, minfrac=minfrac, errfrac=errfrac, DRrate=DRrate, minPseg=minPseg) # This sof has only one row in its parcombok because only one, valid, # combination of parents was tested! So sof$parcombok is different @@ -3059,7 +3043,7 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, colnames(mrkDosage) %in% FS[[fs]]) if (length(NAind) > 0 && length(NAind) < 0.5 * length(FS[[fs]])) { expmrkdos <- mrkdid2mrkdos(dosageIDs=expdid, nmrk=nrow(mrkDosage), - ploidy=ploidy) + ploidy=ahcinfo$ploidy) impdids <- matrix(c(NAind, rep(NA, length(NAind))), nrow=2, byrow=TRUE, dimnames=list(c("NAind", "mrkdid"), NULL)) @@ -3096,7 +3080,7 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # the imputed mrkdids should be among the expected mrkdids, check: if (anyNA(impdids[2,]) || !all(impdids[2,] %in% expdid)) stop("error in impdids") - if (impstats$stats$P > 0.1 * FSsPval[fs]) { + if (impstats$stats$P > 0.1 * FSpval[fs]) { # with the extra imputed FS mrkdids the fit of the FS family # is not too much worse, so add the imputed mrkdids allmrkdids[impdids[1,]] <- impdids[2,] @@ -3104,7 +3088,7 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # of the return value: FSimpGeno <- mrkdid2mrkdos(dosageIDs=impdids[2,], nmrk=nrow(mrkDosage), - ploidy=ploidy) + ploidy=ahcinfo$ploidy) rownames(FSimpGeno) <- rownames(mrkDosage) colnames(FSimpGeno) <- colnames(mrkDosage)[impdids[1,]] imputedGeno <- cbind(imputedGeno, FSimpGeno) @@ -3152,7 +3136,7 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # mrkdos2mrkdid( # mrkDosage=hapcomb2mrkdos(hapcomb=hac[, colnames(hac) %in% ind], # allhap=ihl$allhap), - # ploidy=ploidy, + # ploidy=ahcinfo$ploidy, # check=FALSE) # } } # for mdhcol @@ -3163,8 +3147,8 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, # solution or parental conflicts (but not the FSs that were left unsolved # because of too many equally good solutions) rest_ind <- intersect(rest, names(allmrkdids)[!is.na(allmrkdids)]) - rest_ihl <- inferHaps_noFSs(mrkdids=allmrkdids[names(allmrkdids) %in% rest_ind], - ploidy=ploidy, + rest_ihl <- inferHaps_noFS(mrkdids=allmrkdids[names(allmrkdids) %in% rest_ind], + ahcinfo=ahcinfo, markers=rownames(mrkDosage), minfrac=minfrac, knownHap=solstats[[bestcycle]]$knownHap, @@ -3176,14 +3160,14 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, allmrkdids==did] if (length(ind) > 0) { hac[, colnames(hac) %in% ind] <- - getAllHapcomb(did=did, allhap=ihl$allhap, - ploidy=ploidy)[, rest_ihl$hclist[[md]]] + getAllHapcomb(mrkdid=did, nmrk=ncol(ihl$allhap), + ahcinfo=ahcinfo)[, rest_ihl$hclist[[md]]] #TODO remove DEBUG CHECK: # nwdid <- # mrkdos2mrkdid( # mrkDosage=hapcomb2mrkdos(hapcomb=hac[, colnames(hac) %in% ind], # allhap=ihl$allhap), - # ploidy=ploidy, + # ploidy=ahcinfo$ploidy, # check=FALSE) } } @@ -3285,9 +3269,9 @@ hapOneBlock <- function(mrkDosage, hbname, ploidy, parents, FS, getHaplotypeNames(haploblock=hbname, hapcount=nrow(ihl$allhap)) result <- list(hapdos=hapdos, mrkdids=allmrkdids, markers=colnames(ihl$allhap), - FSsFit=FSsFit, - FSsMessages=sapply(FSdata, `[[`, "message"), - FSsPval=FSsPval, imputedGeno=imputedGeno) + FSfit=FSfit, + FSmessages=sapply(FSdata, `[[`, "message"), + FSpval=FSpval, imputedGeno=imputedGeno) # TODO?:(if rest_ihl with two minfrac values results in additional haplotypes, # see if these can help solve the remaining FSs? And then re-iterate? } # one or more FSs @@ -3350,7 +3334,7 @@ getLinkedFSgroups <- function(parents) { FSgroupnrs=FSgroupnrs, Parentgroupnrs=Parentgroupnrs) } #getLinkedFSgroups -getNewMrkdids <- function(mrkDosages, ploidy, haploblock, maxmrk) { +getNewMrkdids <- function(mrkDosages, haploblock, maxmrk, ahcinfo) { # non-exported function only for use by function inferHaplotypes. # Parameters as the corresponding ones of inferHaplotypes but already # checked, and mrkDosages as matrix not data.frame with only the selected @@ -3364,21 +3348,24 @@ getNewMrkdids <- function(mrkDosages, ploidy, haploblock, maxmrk) { hbsizes <- sapply(haploblock, length) maxmrk <- min(maxmrk, max(hbsizes)) result <- vector(mode="list", maxmrk) - loadAllHaploCombList(ploidy) if (maxmrk <= ahcinfo$complete_nmrk) return(result) # we need to check all mrkdids for new ones knownmrkdid <- result # list of same length for (hbsize in (ahcinfo$complete_nmrk+1):maxmrk) { - if (length(ahclist) < hbsize) { + if (length(ahcinfo$ahclist) < hbsize) { knownmrkdid[[hbsize]] <- integer(0) - } else knownmrkdid[[hbsize]] <- as.integer(names(ahclist[[hbsize]])) + } else knownmrkdid[[hbsize]] <- as.integer(names(ahcinfo$ahclist[[hbsize]])) } for (hb in seq_along(haploblock)) { if (hbsizes[[hb]] %in% (ahcinfo$complete_nmrk+1):maxmrk) { - mrkdids <- - unique(mrkdos2mrkdid(mrkDosages[rownames(mrkDosages) %in% - haploblock[[hb]],,drop=FALSE], - ploidy=ploidy, check=FALSE)) + # mrkdids <- + # unique(mrkdos2mrkdid(mrkDosages[rownames(mrkDosages) %in% + # haploblock[[hb]],,drop=FALSE], + # ploidy=ploidy, check=FALSE)) + uniqdos <- + unique(mrkDosages[match(haploblock[[hb]], rownames(mrkDosages)),, + drop=FALSE], MARGIN=2) + mrkdids <- mrkdos2mrkdid(uniqdos, ploidy=ahcinfo$ploidy, check=FALSE) result[[hbsizes[hb]]] <- c(result[[hbsizes[hb]]], setdiff(mrkdids, knownmrkdid[[hbsizes[hb]]])) } @@ -3390,6 +3377,67 @@ getNewMrkdids <- function(mrkDosages, ploidy, haploblock, maxmrk) { result } #getNewMrkdids +addNewMrkdids <- function(newMrkdids, ahcinfo, progress) { + # non-exported function only for use by function inferHaplotypes. + # newMrkdids: list as produced by getNewMrkdids + # Parameters as the corresponding ones of inferHaplotypes but already + # checked, and mrkDosages as matrix not data.frame with only the selected + # indiv. + # output: a list with one element for each haploblock size (nr of markers), + # from 1 to the maximum haploblock size in haploblock (or to maxmrk if that + # is smaller); + # each element has all mrkdid (as integers) that occur over all haploblocks + # of that size and for which no allhapcomb is yet available in + # ahclist or ahccompletelist + newdidcounts <- sapply(newMrkdids, length) + totnew <- sum(newdidcounts) + if (any(newdidcounts > 0)) { + maxnmrk <- max(which(newdidcounts > 0)) + # add the haplotype combinations for the newdids to ahcinfo$ahclist + # and save the new ahclist in ahcdir (overwriting any earlier version) + if (progress && (maxnmrk + ahcinfo$ploidy >= 12 || + sum(newdidcounts) > 100)) { + cat(paste(sum(newdidcounts), + "sets of haplotype combinations need to be calculated;\n")) + cat("this may take quite a long time\n") + } + cum <- 0 + for (m in which(newdidcounts > 0)) { + if (length(ahcinfo$ahclist) < m || !is.list(ahcinfo$ahclist[[m]])) { + ahcinfo$ahclist[[m]] <- list() + # initialize also all lower nmrk: + for (i in seq_len(m)) { + if (!is.list(ahcinfo$ahclist[[i]])) + ahcinfo$ahclist[[i]] <- list() + } + } + allhap <- allHaplotypes(1:m) + for (i in seq_along(newMrkdids[[m]])) { + did <- newMrkdids[[m]][i] + if (progress) { + cat(paste0("\rcalculation of new mrkdids: ", cum + i, " / ", totnew)) + flush.console() + } + ahcinfo$ahclist[[m]][[as.character(did)]] <- + calcAllhapcomb4mrkdid(mrkdid=did, ploidy=ahcinfo$ploidy, allhap=allhap) + # save after every 500 mrkdids so we can resume after crash: + if (i %% 500 == 0) { + save(ahcinfo$ahclist, + file=paste0(ahcinfo$ahcdir, "ahclist_", ahcinfo$ploidy, "x.RData"), + version=2) + } + } + cum <- cum + newdidcounts[m] + } + cat("\n"); flush.console() + ahclist <- ahcinfo$ahclist # because save will only save an entire R object, not a list element + save(ahclist, + file=paste0(ahcinfo$ahcdir, "ahclist_", ahcinfo$ploidy, "x.RData"), + version=2) + } + ahcinfo +} + #'@title infer haplotypes for one or more haploblocks #'@description infer haplotypes for one or more haploblocks, for all individuals, #'using FS family(s) (with parents) if present, and infer haplotypes for @@ -3397,7 +3445,7 @@ getNewMrkdids <- function(mrkDosages, ploidy, haploblock, maxmrk) { #'@usage inferHaplotypes(mrkDosage, indiv=NULL, ploidy, haploblock, #'parents=NULL, FS=NULL, minfrac=c(0.1, 0.01), errfrac=0.025, DRrate=0.025, #'maxmrk=0, dropUnused=TRUE, maxparcombs=150000, minPseg=1e-8, -#'knownHap=integer(0), progress=TRUE, printtimes=FALSE) +#'knownHap=integer(0), progress=TRUE, printtimes=FALSE, ahcdir) #'@param mrkDosage matrix or data.frame. Markers are in rows, individuals in #'columns, each cell has a marker dosage. Names of individuals are the column #'names, marker names are the row names or (if a data.frame) in a column named @@ -3422,15 +3470,15 @@ getNewMrkdids <- function(mrkDosages, ploidy, haploblock, maxmrk) { #'considered to be certainly present if it must occur in at least a fraction #'minfrac[1] of all individuals; in the final stage for the "other" #'individuals (those that do not belong to the FS or its parents) this fraction -#'is lowered to minfrac[2]; see also inferHaps_noFSs +#'is lowered to minfrac[2]; see also inferHaps_noFS #'@param errfrac the assumed fraction marker genotypes with an error (over all #'markers in the haploblock). The errors are assumed to be uniformly distributed #'over all except the original marker dosage combinations (mrkdids) -#'@param DRrate the rate of double reduction per meiosis (NOT per allele!); e.g. -#'with a DRrate of 0.04, a tetraploid parent with genotype ABCD will produce -#'a fraction of 0.04 of DR gametes AA, BB, CC and DD (each with a frequency of -#'0.01), and a fraction of 0.96 of the non-DR gametes AB, AC, AD, BC, BD, CD -#'(each with a frequency of 0.16) +#'@param DRrate default 0.025. The rate of double reduction per meiosis (NOT +#'per allele!); e.g. with a DRrate of 0.04, a tetraploid parent with +#'genotype ABCD will produce a fraction of 0.04 of DR gametes AA, BB, CC and DD +#'(each with a frequency of 0.01), and a fraction of 0.96 of the non-DR gametes +#'AB, AC, AD, BC, BD, CD (each with a frequency of 0.16) #'@param maxmrk Haploblocks with more than maxmrk markers will be skipped. #'Default 0: no haploblocks are skipped #'@param dropUnused TRUE (default) if the returned matrix should only contain @@ -3456,12 +3504,15 @@ getNewMrkdids <- function(mrkDosages, ploidy, haploblock, maxmrk) { #'by printed messages #'@param printtimes if TRUE, the time needed to process each haploblock is #'printed -#'@details inferHaplotypes uses lists that for each combination of marker +#'@param ahcdir a single directory, or not specified. +#'inferHaplotypes uses lists that for each combination of marker #'dosages give all possible combinations of haplotype dosages. These lists #'(ahclist and ahccompletelist) are loaded and saved at the directory -#'specified in options("PolyHaplotyper_ahcdir"). If this option is not -#'set (or invalid), they are loaded and saved at the current working -#'directory.\cr +#'specified by ahcdir. If no ahcdir is specified it is set to the current +#'working directory.\cr +#'If an ahclist or ahccompletelist for the correct ploidy is already in +#'GlobalEnv this is used and no new list is loaded, even if ahcdir is specified. +#'@details #'First we consider the case where one or more FS families and their #'parents are present in the set of samples. In that case, initially the #'possible haplotype configurations of the parents are determined. @@ -3474,19 +3525,27 @@ getNewMrkdids <- function(mrkDosages, ploidy, haploblock, maxmrk) { #'needs less haplotypes, that one is chosen. If there is no clear best solution #'still the parents and FS individuals that have the same haplotype #'configuration over all likely solutions are assigned that configuration.\cr -#'For FSs where no good solution is found (because of an error in the marker +#'For FS where no good solution is found (because of an error in the marker #'dosages of a parent, or because the correct solution was not considered) the #'parents and individuals will be considered as unrelated material.\cr #'If several FS families share common parents they are treated as a group, #'and only solutions are considered that are acceptable for all families #'in the group.\cr #'Finally (or if no FS families are present, immediately) the other samples -#'are haplotyped, assuming they consist of unrelated material. If FS families +#'are haplotyped, which are considered as unrelated material. If FS families #'have been solved the haplotypes in their parents are considered "known", #'and known haplotypes can also be supplied (parameter knownHap). For these #'samples we consecutively add haplotypes that must be present in a minimum #'number of individuals, always trying to minimize the number of needed -#'haplotypes. +#'haplotypes.\cr +#'InferHaplotypes uses tables that, for each combination of dosages of the +#'markers in the haploblock, list all haplotype combinations (ahc) that +#'result in these marker dosages. In principle inferHaplotypes uses a list +#'(ahccompletelist) that, for a given ploidy, has all the haplotype combinations +#'for haploblocks from 1 up to some maximum number of markers. This list can be +#'computed with function build_ahccompletelist. If this list is not available +#'(or is some haploblocks contain more markers than the list), the ahc for +#'the (extra) marker #'@return a list with for each haploblock one item that itself is a list #'with items:\cr #'message; if this is "" the haploblock is processed and further @@ -3513,18 +3572,20 @@ getNewMrkdids <- function(mrkDosages, ploidy, haploblock, maxmrk) { #' marker dosages match different possible haplotype combinations. #'The next elements are only present if one or more FS families were #'specified:\cr -#'FSsFit: a logical vector with one element per FS family; TRUE if a (or +#'FSfit: a logical vector with one element per FS family; TRUE if a (or #' more than one) acceptable solution for the FS is found (although #' if multiple solution are found they might not be used if unclear #' which one is the best solution). (Even if no #' solution was found for an FS, still its individuals may have a #' haplotype combination assigned ignoring their pedigree)\cr -#'FSsMessages: a character vector with one item per FS family: any +#'FSmessages: a character vector with one item per FS family: any #' message relating to the fitting of a model for that FS, #' not necessarily an error\cr -#'FSsPval: a vector of the chi-squared P-value associated with the selected +#'FSpval: a vector of the chi-squared P-value associated with the selected #' FS model for each FS family, or the maximum P value over all #' models in case none was selected\cr +#'If for new combinations of marker dosages the possible haplotype combinations +#'have to be calculated, an ahclist file is written to ahcdir #'@export inferHaplotypes <- function(mrkDosage, indiv=NULL, ploidy, haploblock, parents=NULL, FS=NULL, @@ -3533,41 +3594,22 @@ inferHaplotypes <- function(mrkDosage, indiv=NULL, ploidy, dropUnused=TRUE, maxparcombs=150000, minPseg=1e-8, knownHap=integer(0), progress=TRUE, - printtimes=FALSE) { + printtimes=FALSE, + ahcdir) { mrkDosage <- checkmrkDosage(mrkDosage, ploidy=ploidy, indiv=indiv) tmp <- checkpops(parents, FS, mrkDosage) if (tmp$message != "") stop(tmp$message) parents <- tmp$parents; FS <- tmp$FS; rm(tmp) #a.o. all as character haploblock <- checkHaploblock(haploblock, mrkDosage) - if (maxmrk == 0) maxmrk <- max(sapply(haploblock, length)) - newdids <- getNewMrkdids(mrkDosage, ploidy, haploblock, maxmrk) - newdidcounts <- sapply(newdids, length) - totnew <- sum(newdidcounts) - if (any(newdidcounts > 0)) { - if (progress && (max(which(newdidcounts > 0)) + ploidy >= 12 || - sum(newdidcounts) > 100)) { - cat(paste(sum(newdidcounts), - "haplotype combinations need to be calculated;\n")) - cat("this may take quite a long time\n") - } - cum <- 0 - for (m in which(newdidcounts > 0)) { - allhap <- allHaplotypes(1:m) - for (i in seq_along(newdids[[m]])) { - did <- newdids[[m]][i] - if (progress) { - cat(paste0("\rcalculation of new mrkdids: ", cum + i, " / ", totnew)) - flush.console() - } - ah <- getAllHapcomb(did=did, allhap=allhap, ploidy=ploidy, - writeFile=TRUE, progress=FALSE) - } - cum <- cum + newdidcounts[m] - } - cat("\n") - } - + nmrk <- max(sapply(haploblock, length)) + if (maxmrk == 0) maxmrk <- nmrk else maxmrk <- min(maxmrk, nmrk) + if (missing(ahcdir)) ahcdir <- NULL + ahcinfo <- loadAllHapcombLists(ploidy=ploidy, nmrk=maxmrk, ahcdir) + # see if there are new mrkdids for which all haplotype combinations + # must be calculated: + newdids <- getNewMrkdids(mrkDosage, haploblock, maxmrk, ahcinfo) + ahcinfo <- addNewMrkdids(newdids, ahcinfo, progress) nmrk <- sapply(haploblock, length) result <- vector(mode="list", length(haploblock)) for (hb in seq_along(haploblock)) { @@ -3583,7 +3625,7 @@ inferHaplotypes <- function(mrkDosage, indiv=NULL, ploidy, hapOneBlock(mrkDosage[match(haploblock[[hb]], rownames(mrkDosage)),, drop=FALSE], hbname=names(haploblock)[hb], - ploidy=ploidy, + ahcinfo=ahcinfo, parents=parents, FS=FS, minfrac=minfrac, errfrac=errfrac, DRrate=DRrate, dropUnused=dropUnused, @@ -3993,8 +4035,6 @@ getGameteFreqs <- function(parhac, DRrate) { HMx[1:2, cols] <- parhac[d] HMx[3:(ploidy/2), cols] <- combn(parhac[-d], ploidy/2-2) } - # dimHMx <- dim(apply(HMx, 2, sort)) - # if (is.null(dimHMx) || length(dimHMx)!=2) browser() HMx <- apply(HMx, 2, sort) #sort each column separately HMx <- HMx[, order(HMx[1,], HMx[2,], HMx[3,])] #reorder all columns # HMx is now sorted, and comparable to that from getAllGametes @@ -4552,9 +4592,9 @@ overviewByFS <- function(haploblock, parents, FS, hapresults) { if (hapresults[[hb]]$message != "") { messages[hb, 1] <- hapresults[[hb]]$message } else { - FSsok <- FSsok && "FSsFit" %in% names(hapresults[[hb]]) + FSsok <- FSsok && "FSfit" %in% names(hapresults[[hb]]) if (FSsok) - messages[hb, 2:(nFS+1)] <- hapresults[[hb]]$FSsMessages + messages[hb, 2:(nFS+1)] <- hapresults[[hb]]$FSmessages ovw[hb, 2] <- sum(rowSums(hapresults[[hb]]$hapdos, na.rm=TRUE) > 0) #nhap totimputed <- 0 @@ -4588,9 +4628,9 @@ overviewByFS <- function(haploblock, parents, FS, hapresults) { } if (FSsok) { # fit: was an FS solution found? - ovw[hb, basecol+1] <- hapresults[[hb]]$FSsFit[fs] + ovw[hb, basecol+1] <- hapresults[[hb]]$FSfit[fs] # P: P-value for best segregation, even if rejected: - ovw[hb, basecol+2] <- hapresults[[hb]]$FSsPval[fs] + ovw[hb, basecol+2] <- hapresults[[hb]]$FSpval[fs] } } # for fs #data for "rest": the non-FS, non-FS-parents indiv @@ -4628,6 +4668,83 @@ overviewByFS <- function(haploblock, parents, FS, hapresults) { list(ovw=ovw, messages=messages) } # overviewByFS +calcPedstats <- function(pedchk, indiv, haploblocks) { + # function used only in calcStatistics; for input and output see there + if (missing(indiv) || is.null(indiv)) + indiv <- dimnames(pedchk$ped_arr)[[1]] + indix <- match(indiv, dimnames(pedchk$ped_arr)[[1]]) + if (length(indix) == 0 || anyNA(indix)) + stop("names in indiv not found in pedchk") + if (missing(haploblocks) || is.null(haploblocks)) { + haploblocks <- dimnames(pedchk$ped_arr)[[3]] + } else if (is.numeric(haploblocks)) { + haploblocks <- dimnames(pedchk$ped_arr)[[3]][haploblocks] + } else if (is.list(haploblocks)) { + haploblocks <- names(haploblocks) + } + if (!all(haploblocks %in% dimnames(pedchk$ped_arr)[[3]])) + stop("not all haploblocks occur in pedchk") + hbix <- match(haploblocks, dimnames(pedchk$ped_arr)[[3]]) + if (length(hbix) == 0 || anyNA(hbix)) + stop("no selected haploblocks present in pedchk") + pedstats <- data.frame( + haploblock = haploblocks, + stringsAsFactors = FALSE + ) + pedstats$mrk <- apply(pedchk$ped_arr[indix, "mrk", hbix, drop=FALSE], + MARGIN=3, FUN=sum, na.rm=TRUE) + pedstats$imp <- apply(pedchk$ped_arr[indix, "imp", hbix, drop=FALSE], + MARGIN=3, FUN=sum, na.rm=TRUE) + pedstats$hap <- apply(pedchk$ped_arr[indix, "hap", hbix, drop=FALSE], + MARGIN=3, FUN=sum, na.rm=TRUE) + pedstats$match.NA <- apply(is.na(pedchk$ped_arr[indix, "noDR", hbix, drop=FALSE]), + MARGIN=3, FUN=sum, na.rm=TRUE) + pedstats$noDR.TRUE <- apply(pedchk$ped_arr[indix, "noDR", hbix, drop=FALSE], + MARGIN=3, FUN=sum, na.rm=TRUE) + pedstats$noDR.FALSE <- apply(!(pedchk$ped_arr[indix, "noDR", hbix, drop=FALSE]), + MARGIN=3, FUN=sum, na.rm=TRUE) + pedstats$withDR.TRUE <- apply(pedchk$ped_arr[indix, "withDR", hbix, drop=FALSE], + MARGIN=3, FUN=sum, na.rm=TRUE) + pedstats$withDR.FALSE <- apply(!(pedchk$ped_arr[indix, "withDR", hbix, drop=FALSE]), + MARGIN=3, FUN=sum, na.rm=TRUE) + pedstats +} + +calcFSstats <- function(ovwFS, haploblocks) { + # function used only in calcStatistics; for input and output see there + if (missing(haploblocks) || is.null(haploblocks)) { + haploblocks <- rownames(ovwFS$ovw) + } else if (is.numeric(haploblocks)) { + haploblocks <- rownames(ovwFS$ovw)[haploblocks] + } else if (is.list(haploblocks)) { + haploblocks <- names(haploblocks) + } + if (!all(haploblocks %in% rownames(ovwFS$ovw))) + stop("not all haploblocks occur in ovwFS") + hbix <- match(haploblocks, rownames(ovwFS$ovw)) + if (length(hbix) == 0 || anyNA(hbix)) + stop("no selected haploblocks present in ovwFS") + startcols <- which(substr(colnames(ovwFS$ovw), 1, 6) == "parmrk") + FSstats <- data.frame(FSfam = 1:length(startcols)) + FSstats$bothparmrk <- colSums(ovwFS$ovw[hbix, startcols, drop=FALSE] == 2) #na.rm should not be needed! + FSstats$FSFit <- colSums(ovwFS$ovw[hbix, startcols+1, drop=FALSE] == 1) + FSstats$mrk <- colSums(ovwFS$ovw[hbix, startcols+3, drop=FALSE]) / length(hbix) + FSstats$imp <- colSums(ovwFS$ovw[hbix, startcols+4, drop=FALSE]) / length(hbix) + FSstats$hap <- colSums(ovwFS$ovw[hbix, startcols+5, drop=FALSE]) / length(hbix) + startcols <- which(colnames(ovwFS$ovw) %in% c("mrkrest", "mrkall")) + reststats <- + data.frame(FSfam=c("rest", "all"), bothparmrk=c(NA, NA), + FSFit=c(NA, NA), + mrk=colSums(ovwFS$ovw[hbix, startcols, drop=FALSE]) / length(hbix), + imp=c(0, sum(FSstats$imp)), #only imputation in FS families + hap=c(sum(ovwFS$ovw[hbix, startcols[1]+1]), + sum(ovwFS$ovw[hbix, startcols[2]+2])) / length(hbix) + ) + FSstats <- rbind(FSstats, reststats) + rownames(FSstats) <- NULL + list(FSstats=FSstats, haploblocks=haploblocks) +} + #'@title calculate some statistics of the solutions of all haploblocks #'@description calculate some statistics of the solutions of all haploblocks #'@usage calcStatistics(pedchk, ovwFS, indiv, haploblocks) @@ -4680,71 +4797,29 @@ overviewByFS <- function(haploblock, parents, FS, hapresults) { #'the rows above. #'@export calcStatistics <- function(pedchk, ovwFS, indiv, haploblocks) { - if (missing(ovwFS) || is.null(ovwFS) || - (is.vector(ovwFS) && (length(ovwFS)==0 || is.na(ovwFS[1])))) - ovwFS <- NULL + if (missing(indiv)) indiv <- NULL + if (missing(haploblocks)) haploblocks <- NULL + if (missing(pedchk)) pedchk <- NULL else + if (!is.list(pedchk) || !identical(names(pedchk), c("ped_arr", "parents_arr"))) + stop ("invalid pedchk") + if (missing(ovwFS)) ovwFS <- NULL else + if (!is.list(ovwFS) || !identical(names(ovwFS), c("ovw", "messages"))) + stop("invalid ovwFS") + if (is.null(pedchk) && is.null(ovwFS)) + stop("no data") + + result <- list() + if (!is.null(pedchk)) + result$pedstats <- calcPedstats(pedchk, indiv, haploblocks) if (!is.null(ovwFS)) { - ovw <- ovwFS$ovw[rownames(ovwFS$ovw) %in% dimnames(pedchk$ped_arr)[[3]],, - drop=FALSE] - if (!all(rownames(ovw) == dimnames(pedchk$ped_arr)[[3]])) - stop("haploblocks in pedchk and ovwFS don't match") + FSstats <- calcFSstats(ovwFS, haploblocks) + result$FSstats <- FSstats$FSstats } - if (missing(indiv) || is.null(indiv)) - indiv <- dimnames(pedchk$ped_arr)[[1]] - indix <- match(indiv, dimnames(pedchk$ped_arr)[[1]]) - if (length(indix) == 0 || anyNA(indix)) - stop("names in indiv not found in pedchk") - if (missing(haploblocks) || is.null(haploblocks)) { - haploblocks <- dimnames(pedchk$ped_arr)[[3]] - } else if (is.numeric(haploblocks)) { - haploblocks <- dimnames(pedchk$ped_arr)[[3]][haploblocks] - } else if (is.list(haploblocks)) { - haploblocks <- names(haploblocks) + if (!is.null(pedchk) && !is.null(ovwFS) && + !setequal(result$pedstats$haploblock, FSstats$haploblocks)) { + cat("The haploblocks in pedchk and ovwFS do not match\n"); flush.console() } - if (!all(haploblocks %in% dimnames(pedchk$ped_arr)[[3]])) - stop("not all haploblocks occur in pedchk and ovwFS") - hbix <- match(haploblocks, dimnames(pedchk$ped_arr)[[3]]) - if (length(hbix) == 0 || anyNA(hbix)) - stop("no haploblocks selected") - pedstats <- data.frame( - haploblock = haploblocks - ) - pedstats$mrk <- apply(pedchk$ped_arr[indix, "mrk", hbix, drop=FALSE], - MARGIN=3, FUN=sum, na.rm=TRUE) - pedstats$imp <- apply(pedchk$ped_arr[indix, "imp", hbix, drop=FALSE], - MARGIN=3, FUN=sum, na.rm=TRUE) - pedstats$hap <- apply(pedchk$ped_arr[indix, "hap", hbix, drop=FALSE], - MARGIN=3, FUN=sum, na.rm=TRUE) - pedstats$match.NA <- apply(is.na(pedchk$ped_arr[indix, "noDR", hbix, drop=FALSE]), - MARGIN=3, FUN=sum, na.rm=TRUE) - pedstats$noDR.TRUE <- apply(pedchk$ped_arr[indix, "noDR", hbix, drop=FALSE], - MARGIN=3, FUN=sum, na.rm=TRUE) - pedstats$noDR.FALSE <- apply(!(pedchk$ped_arr[indix, "noDR", hbix, drop=FALSE]), - MARGIN=3, FUN=sum, na.rm=TRUE) - pedstats$withDR.TRUE <- apply(pedchk$ped_arr[indix, "withDR", hbix, drop=FALSE], - MARGIN=3, FUN=sum, na.rm=TRUE) - pedstats$withDR.FALSE <- apply(!(pedchk$ped_arr[indix, "withDR", hbix, drop=FALSE]), - MARGIN=3, FUN=sum, na.rm=TRUE) - if (is.null(ovwFS)) return(list(pedstats=pedstats)) - - startcols <- which(substr(colnames(ovwFS$ovw), 1, 6) == "parmrk") - FSstats <- data.frame(FSfam = 1:length(startcols)) - FSstats$bothparmrk <- colSums(ovw[hbix, startcols, drop=FALSE] == 2) #na.rm should not be needed! - FSstats$FSFit <- colSums(ovw[hbix, startcols+1, drop=FALSE] == 1) - FSstats$mrk <- colSums(ovw[hbix, startcols+3, drop=FALSE]) / length(hbix) - FSstats$imp <- colSums(ovw[hbix, startcols+4, drop=FALSE]) / length(hbix) - FSstats$hap <- colSums(ovw[hbix, startcols+5, drop=FALSE]) / length(hbix) - startcols <- which(colnames(ovw) %in% c("mrkrest", "mrkall")) - reststats <- data.frame(FSfam=c("rest", "all"), bothparmrk=c(NA, NA), - FSFit=c(NA, NA), - mrk=colSums(ovw[hbix, startcols, drop=FALSE]) / length(hbix), - imp=c(0, sum(FSstats$imp)), #only imputation in FS families - hap=c(sum(ovw[hbix, startcols[1]+1]), - sum(ovw[hbix, startcols[2]+2])) / length(hbix) - ) - FSstats <- rbind(FSstats, reststats) - rownames(FSstats) <- NULL - list(pedstats=pedstats, FSstats=FSstats) + result } #calcStatistics #'@title produce a table of nr of markers vs nr of haplotypes diff --git a/data/PolyHaplotyper_demo.RData b/data/PolyHaplotyper_demo.RData new file mode 100644 index 0000000000000000000000000000000000000000..324cec872e681f2e61fa6edfcc8ec637c4d9bbe0 GIT binary patch literal 15313 zcmb2|=3oE=wzswHGjvZEhP}V?PKnb%Old|+PI9E%rlWT~cYCv)PFUn9q2p7rZ`!=3 zwc=myG2d)>qBtW*xYr=z0Lx@!we)v;t(j*e6rDTyWRC8uH-7$qV}oU%y{WAF?z#JZ zc=<D*7yDQ5{`LOVtXVtr>n=yX3g7!Q^xV!<7c-w~J-Yk2>%V2u@pn`A*w+4gnSTG< zna}4gX8%~k|6yW(+Rqz5-?-O)`&(XfD>%RE`|bG_yEA^5)oyvGe?Hv(_vX*L;$_$V z{#8@ECG*_*YwqXues8XREwt-j+;*#~^_H*o!`yHGTNE2#eY13J;QeK<uc^kwd=Wpp zD=juVqvrjZ*ZSApcUPysUi>w+_I$?N>aR-|7T%7TBPsQL`s-hD^Wv|(UR-;*BwKga zv$)vxru%Z<t$lqv{@&{Arn}1v*T-F7v-gGc<Bamkv@K88O%8WIuW!AY&vyInO=mA} z-}j-f`un+We?s5?Ty1$`{WpdEyKL*fR{yiQ|Kaq`{HnX**2{KI48Ae%%<Z`{=Z~+h z{4cWZV|~T?=ITFNuh!I>p7`3&z0df=Mn3h=AMeT^tc+iG%)Y7kpVjk^j_t=;|8txw z(9^kpT5yZ>x8C)?Iq!J8MgEic_Fk>V;`^op@`pCrJ^E75^Py1Z=?C3^mTWrDuO(l8 zG+%fJ-|N+1s{=o@f7oUHhu?7D?e+gxbJ*RgJoRtxuJVKO*LJL6eiZ$+`(c7~1<(5L zd8?1_pI`3t_xbPq#rx&{b5FZ|KZE^>eCgrO5$tP}&z-)uH!A$~jg8u~|NB4qQRMe> z^NqU?&e(0wF}nU=^B?c8`>k)~UuUnCf0h5SzUU8c)7DG(FI-(E{Al^H`<xFfOa1z} z<JGhOoqiboYxynvWA<WUJ(Gie{kDp#%KGhouRC$+oAZ{t|9;<SpZ(|gFZ=EPZY-)l zu)pB0=|2HE?}yQk?@K=4@^9(m^-KOH|5<(8b^HE%Nju#y@a_C4FP*-8;^U1!*>ewl z`oBt1=kTTY$I-uzADf>l9Q{`RLH&+pF8?QY)LH(E-q5!7pV<E&xt#xXzL|SSXZ`N! zv%hfs{_IcoUbo_3FSzaSTYlq5@z?)X{1-mB{YU!C*^&Q~{>fjvKIO-L%m41*yVp;? zdi~&!*{>&m-e2(Vpw+QI`>*_&F7-e9c6|SSng3DW!cYCb_QU_@f6M>3v;N=sGyhTi z>c4Y0m)e$oo3)4K{w3pM+(&<D*F4Yp?f-SZ>A%0<Zmj*d_S1K{|L5L`i(kAS^`GUR z`kebl|F-{_e{F`wM|-b7{zv+M+FzTq;r)-xulr@{IR8(5VEtI;(Qm8N=kl%dKkfJV zd-s;!q(A02o<6@ef9b#F&Dlwh?^{iqRbTY)y4c_1$M(nSrQ}a{KRli!yYuJqUCT@U zWd8F$8o%wwf5X3~|6f%H|C@jHUznuW|ET9%{-s4X|GOb)`QLoozwWQ~UH`xQnci*B z`e*lQ<rf<^{a3uB_m=<rl1J|i|9bzmXaBwQ*Yr>RXZ~;ObN_u`!+!g}%fFJPUw&=Z zvwm71FE5?9E&5q}&A;i7_KQvn{<~k>{o(XA`(vKWY0ckSpY?b8SNrx2y&r6@op#-@ z!8h!2e#G>Jx%y`R&i<3XmYR9*>+!tJ|2lrkZ~S-P>i?5J@j3q|eRb!OKizk0|E}`x zztM9-jQ$0B{pp|bf9|ILLI3&BN50*ENIvge^pD?X_OnOVpZTg)`7d<Szl;CkPkz>y zsW<%l{p0=(JV(5a&JQ%t?s@(}e9z85LjSqHp0}$nWRLr=@z?vO`{VU*@(UWzZ=S+> z{M%Q1uRrsh*X`GxcR5#h^Zz%$u0Oqf|5E(p>96{qZ|IrxZ|R@lL;Ej&lYjJM<)3DW zx^w^c+L<N4yZvutp`i5t_6PT;{0}txpYiYVxA>;@U;EG6YyQ6+_224o+};06Ki6Nl z7WJQ_Zsq^ww~tBH|Gsr!?7#c({g%JC|60=WzvTDhy@r38*F>HV{lDAvzv|C&j>1K6 z?c>vX_uu|i-;w<B5$A*KzlA>QuGvfene;RL;{BWd!gGA`D*r7#KEL(7*N26^ULQ)W zqkla6#a__#t^dJdzI|^*{zv?M|K7LP{`CLuv-VQQ=Uey5@IRAZ`rq#7@{1qsr|%8_ zfB5hAUi)Kj>gRurKlg9jnf<Fbn%(%7d$Qi^<@}@N2LGL7zdikb<&W@<|2<pJgHpWZ z|Jz;rm;L#F<Im)-{l~7why7;%ll(&d_5YB6kv#iL{$2mK|Je0k)}`m9>gDP$|B%o5 zbKu`q)0JQQ^S9sl(S3IR&Y$OZGX0)i$T#zUfYE>VxA$A$ia)l$qT9Cq?fob76?M3e z-|tSW5Bq=o*!*vtUVr9`{Ezx0e);OE=#~FdAHM!5{z3mm$Je{*|D1K+{$KtkKl@*5 z=D*s$`t`3TUaa^2z5G{w_V4$?|21RlSs&{+_aD!DTrYn1e?jf3|GU@z<6F1!$NDAz zb^pxx`}$-5_i2w7|9!vM{+9f!{H^up{&{`AU-WwajQ_Dl|08}Ke>Okn|MVB}m;PD) zx&0yk<e&MU_6PsXf0=aFbGvN)w7=02JHP42{*Tx<(>`zO#@6@!Q*Xrc*r)wJC;Dsu zx}N%?f6G(<|9)nF^w|E^<g?tG|BJ8wE0*n^|9^FM(&O{<^*i@Z*|vY_{%7;ECR81{ zWo7z*>a+hxfAGJxKVBd4Z{N4&$6NQSoBm&R?7rPU`M&*g{s$iVAN_3ql>ajy|6luW zzv+LMkLhn#{0rC~^FO<0ZGFz4=x_bCo8JF&zg@rOzvJ<#|Nh;mIlJ)s{pdgM=lo6X zDZToS>+pN`ukvrQ^MwER^!~rQ?a5DX&i`+u|65)8EWgF{^nd<0^&O9=@2Q^~T_5!? z-sgYqz5g5E#6M2|yMFGxwsrESzOomcZOXs$((Ld2C-U33nRd_q_R#)F|IPia^Q-?S z$p1es^S?QEe(3-6*JWS*z3-6keZA+O;eYMx|Cc`I|7z9pf8wV9cQ?NI9bR1hX7>LT zZ}VsVe_rJM=)UUv`LD{({hWW!^;SK{?Gst`-v1@`&HHb<Z|gtq56^$yQTvy5v!17Z z{>SOB{?GZ>exhFc<Nt4x^$~S>|IYrDpZ0G>HdlT2KjUi`zu5N}ZP@pF-~TeE>&Ktf z*ZX(=e|uV^^52rT`&0f3ZmwV5x4rhC;Nk0kqLb=(ovo2RdaYip?#lm88S5**uXp61 z-8wCIO3(k8f4*n_Yt5|BpIpE6r}>5ZH$Shxs<!>){$oX7_g;(Ap88+-^Zx1I<a7R( z?wxb`e0|;Q*#En>{uiHJ@BPO9`hU;Au~q*r|J{H7^Ze<D|8zgiy=wpcU;nY|Z~Lp< zZr4w^zx^oxkMxMg(O*A(^;m1a_MiXW|ACkOAN<1{;dgibCHt5EHU55;>Hod)WB%0o z*?-z+{11NeU#sT%wypc0J#DBDx&7i}f9jeq@`tL6{?^Cdt2u9UT;hl7`}yjB*Z-_q zy~p9dW1afP*nNMQZB6aw9Dl6&Km2_C()06!e?0pUp1b(Bz1e@Yk9Vv7-9BQU_y31C zkNvfOhW~cv{<$dn;(g4W|9-arwJQI`tlFPi-+oE{^|q=1o2+=vZ$J9+Ss<>t^gz|y ze`o%g_vP09zkR6xWqrq=>bL*J&+peW{LlMvW9@x=&!6?nHEYsu&kuDwbMD*L|Fs{2 zzs=A3zxFYIY4^|nepAJ_)`uV1&-%mi-_1?`PCY#SXZ@8Q{a^QY+8;38{r6|N;eV|^ zSALz|wEyZ(rkcCw#QsnJbpF+hL;WA?^Zp7S){m@z_9(ycfBu8Te_wyQFIBHw`JeH& z$L;yw?D?nGOMN(>F#mAb%};+{uKmdV{Mxtlt@ST|*q?o{{+a#K{YUFJe)T`2w(ZRS zUH^l>ZQo=XyZq<(Yd^1NNgpX+cjy0wKiOL&d+LMU&i}mMr{=NT-Ty1<uly6e7BsD~ z|66-%ba9MN{n>iC<kz3~Px{0B_y7JQ&;AGfN%*(@md?WZbN_Ei{a^3(;i1&se+T!v zMMf;w{Udxsa*Fin`Xa&DwcqZ~`u|X=)cH@&zqwh9AN{z#;7fb%g8wzXC%>;Re!Kp4 z!pnH8|Ia_=Z+P?KSNO~MkM2+XpZ{R`kNukeHvYPw&%d<(!G8zIi2Xu;?)(-v{OA8u zUhMz0AM;lnUiRnyLA&-}?tkAa?nygZ&$jQF=i2>S{?FXBU-wUN*ZTLqKkb+PTK^|s zgZZ}lh4`0Q50fR2o|iZN_598Gtl6&r9Zz{Yv@SjOWd6=IwV(9EbC-SD@`wNQzv9RD zqy8WJbN<S&(_8i*TzcKUVBM3S!RP+>{Irk!e_z(@pTq;{h@C&#Py4Oi->>vE^Y`{Q z`YRTnzJ34Hxwa27+q}c-m%pjs_ss0Qz01$z7v#UsZH)i&=jes~H~uO85<MQze$m+U zU#abeHB0{IU&%N9$NN|JkHr6|r_blrvw!3N@%xATu3UlrDStOU{k>oHU%KZ%?{ojV zZqC<R{p7#>#t+5c^0)0@nEmc=`;A}9pn|c!^MB}~`piGR(_<g~U;HnkEabo99`jrO zsvn4dt#|q#^E-JiM_=x#{{g>_U$fu%Q-ABfd%ylAp803<@7nkJjX9@}pRujK_#^uB z{h%Mwx9;EHeg1d2<-e=H+<%I{>ip_^biUUY{mB0o;xFrWn4A57X*K=D{;dC!%a{Bw ze!YLnzl}eayVSGq&lj%0f5?94*ZymY*Z&#*`+nQ^<Nw&$lHU@g8lC@Lf8W2qdfV(z z^&)kr{@?laCx8CG{)f?jrhm5I@MgvT*$2*_t8du<{#Um5@92N^UG>TTzOUS6zx`kD zBmP&jetuW`XZk8^*T2&r<4^rt`|tQ?srxbY4gWTNy+7@n|IhgwzxrqWZBKphQ{=zG z|JPs7Uwa;}`Y-<EpWsLJ5&!z->eUN+pULO^e)>b<s|6bm`}gqO*?S~@82;7$Z@+Z^ z$@*g(*Z!NHSDzzZZ}aVsrsV%7GtW=@F~X1MPx{(^^*?*<|2r2<kMB49cXI82pMS<5 zihnJCcK^no^l$sS*VhOA+`jQ=e)j+0Yc2m>*?H-!__z3rzYbrpKW^{(alWF?`A17{ z{MddizWu{~rr)2xHdihCQ2Iapr+(x6D|@G{J7`)gYreOhvuf$B`$yWVlfR{JmF)Yv z^#AkRoWG&J{wRL?V{Z1}s`Q-Hf6f2N^S`Dq*dMrk#{Z?C|9k(j?|pyT^XLE7-{#-` zIN|;Lr2mn{lY_q>`(eIxzuw_j_1*uy{^q}qSoweN1OA75pZ&NU%Y1$6tN2I%Ev8?; zZ+ZKl*N3%M%YPjIQvWQQPqhB_t$+V&PX79T<>^oJXZ1(k*r(Q8{=R?wfA39j)mr}4 z>E_aZ>8Ji%y8OG%b^k(rt9{GYEgv@a-2Tvi_20Rt*X^U9&A;@Azczo`|7YKB?D|)( zxA=cdp68#PF8>$IU)ZzcFaPUJo9^E?KbKt?G0#fw@Beb%zp{VqzI>i9^nZ8Bzl~qc zud`SFwf@zTFZy5iZ`@P+^>}jhr~R={VixSb74>7<fBDb*zx>$zOYK6vUis_)^)o;H z*Rf6h`+j@-hW#=B-Jjn-cujTc|Hnc<!hXLu{{L*=|H#t5v->^&8vpnw@hJUsJezd< z?RcI4>r(#RH~A;xHtj#}U+;VNr+fd;_#3@1E$`p`r2iFiU;Cfke{FU4dhXf!;=H?N z9rY)Esz0q?^5?&>bNy<6uSfA!e;!*N`F`B{ntl5P`)mJKem#EekGuE(Nng*~{mVc3 z=lPvox!Zd7-}#gMQoj1XNOFRd{j&Z4>_0O7XYQN*XZ5QY(f{9k{$qXp|2d0rtN&|r ziq<{Py=UKd&c5xRq2I2vzw0%BpWe3r()~l%i)MeE|G7Tsul(csEAhYQNB#@{vGn}@ zm?sr^|2F@4zJ;NF^WU9+{v`jjk1)F@{QCcuU&`kHA8piqKR<ute&;pzLEpq5-xvC= zef0cU`{?8M7e4>L_Obr=zpiWduXE<QzjH&*>1*3=-H-cqpQ&E^|M5TbCF)N7+t@SX zpWofxtoC8cbw8?i*Yj=tck|Z&?GN|sK3>1&zc;AWnLqb`(O>zi^&I`Vr~dbE;eECK zb>hqXhtpr=Z{2@P|6{$*pSAzahyB}pZt9KtGw1tn&M&U~|M=I9g3Wiu<A2w8|0~|P z^7;LxKccq%kiYr$xX#h{_7cBlzqNn8_5A043A=6ed4B`n{FnZ5ZU_5D0rj$)_&-9w zwPpWr+-YX|FX&(YQTyoU`|tdTzw~eE=lC<%bbsIa^>1h1?Al-VFV)ZgDgM@8;(znq zY0+=bZ~A=WPrRe_Jd4@?w>`6ee(e9XZ~V8)*S){??f9pXy<X4dSO1+``0v86`&R!i zALu_@ulhsv_5T@vk00Bg$G-fX|IPY!|7GV`mo5Ca^QU>{|K_!a?fz{qF8qC4{lC)l z`dOdfui^i6;gs6*;@S7=14^C$aepwKc02#i^Edn-r~jLO`tGkkhcCsSUU>ii{pUZG zUkT^duXtYcHzB@Q`rpP^p_l$wtWo(V8~uODkM!5S^9_G@znnR%-sjJWjkQ1ZP5%|$ zk1xL<7yZaS`r3T$b%w7(X4Qu`t^c`S<zK_w_c8bWeVDc4U-8f1HT5>j>d*Y&`eE+v z{J)7W=S%(H`SrNsJp0Jk_OE3giGSYTxV}{5p#Hr7ZKXd|AKM?bH~iQA^}pTE@Tx!4 zf9;?4zx?*s@&*4DcCr2V{BiC5`Xfd`KYqWOAN_yovHkps|Igk0HGNHe$=#%H`7h_s z`hW7m{YUaP%PW7fY*T&gzkR>yzePXKud=cIKYfk;wte+49>{EKyk`IWzvYg!%YV32 zkGx6h{-^qQ|CayLtDjp<TeIJ5|No?~|HJ+k|4E-Z@sQ`u|0@^nPn~V?FZo~oyPw@J z>!p5#-F^b9&DQS!9`)b4bN<pV_Cep5eb~P4zxVb15pgAd(!cCK`^(t;|Igf$e>Ru? zbN(|Ub_uAzD}K2?`=9TszrjEGm%cu~=D*?Vsk8oj{rx}X|HRk-Ywy)xNsV}{zy04O zE2U}I^u_=Cto@(#*uHIk^?z;C|I=RVe{kL+djI~gPk+uw{m=YeE*Kkbc(Stk_V@iI z`(6IpKfaO4_|NWd{ICD}r~Z&%`ThNte=q*On_R#Cb<WoMygy5C-Oswe_V4`c`ucrO ze(Ha#=lmZHst-0FpI@4z{ePj|wEs`Hw|Ci3`87Xco8>i=e}8^&`*~jb-^|m#52ydS zf9v0tf7-w0uZW#C`rtip|I%yr8$V9pTwlND$#3hbKZ%uw|4;YNp7Zzass8~_Usikn zSwHEc_~-q>kM+&2FSsB1>EGJcdg*`OAM<(Y<$s?(Yajh=|D6Ai4Oi{7dA{H;`wIOx zzjI&y4qy4RocsUtYw?CPoBy*Py!0dN|MNfV*FKiNeeB@Jgg^Wbzy3ZJzhVC7*Zq>4 zpZr}v>Ery_|2M`h{l`9S|M%a^H@N<p^v8aGZQ#EB#_`j)?q~X2{5Jn%e{tczo6mo` zpZkCP@B8<$XYCi5Z#z8g>-=Ne-zXj3ukH8c$LEa_c8_+P_;}}+{_nrtOn;nv_e<2} z{k!;U`Jee$KCAD)yX)WPXYt<umHub{TYLMz#QR3G?ydhft_t6(U9R_=<=fxC$1Cm~ zIBwYTH{;>&ZJZw>HJbLn;5}aSSpMT*`J(m5kAGVpS<ts{-Tq5=KkW~w=f3}?enHH8 z_R!CN_vv0R`)j%S@5cXYzr~+xl-n1l{r9fbx^=fxR{u@BJK?{0O$PTB(*y0FcmDhS zIHG^E#=hw9`{lc(<iCEezxB^_-+r}>`YYj6_g}idJ#gW_mEX2ktUUFip6AE3e~o_+ zmTvs`e7*ZUSLw_5L%X)_&#Y(LXZruakM+;0YxXDpj-Sq7sxA5d#{Ux&Z~SNc-%!`M zZUJ{uLD3)Wzsxn_-?VT2H@p*maNm6;_1vfR-1jYCrXPua^*H4JVYAG7J;Q%78TJ3> zDXYuKNY_{B9IqGq|KQfC?c$&Q9s0jJ``PPhuRlhx-M=LNlmBo1>i=JV#6QzmQ9Y@C z!Mu$hmmjx(wm;&Zdtbfxt@@+;z3QLHuU~!K-TK5&`;9;4Kc4;g{IzX|A7i|O^pj`u zr~iAs;!lnKaby4Qp8re#EB}f=Z(Fb~XvcBAEe}BrIR5AL+5arte*bwNyyN<H&foX{ zPd&3gutsBh(EjGH=37oq`{r(IyX~3$mwJ<F_oIGI|549(ZegyX-LL=AA2#On3O=rn zcenjl6DwA)($xR<{qz5G{>y%N|Degfp7+=Ps(;;I_y0fr)86v$-n{Pr%&ANMyl;MJ zy}@35W<Bp8iT}U0b)T)j_Gf?3f5(!jWA-oR%hpT&E&R9j*nLi!<8|AAtWWu;8&`jJ z#_F%@^QA53&90yPE&sv$f78D1=RO{v^;h_`{iZ+pZ)Q*ZZz``|+WYPL*FX6$*Z*BF z`k#N%?x+7*eiY<q{oUUBfA8PNdt^TBkM6gh{GToQ|BSy+|C~SYZ0f%o?)qJScYpn} zf0Mn%wC?(6*Y4l=*ZtSJqqkCaf$o~G-|c>M|2Lfe{Pm_E^H=<T&}0!h@vpP(#eXJs zJ^yw8b@tj%{;&K)?0@v1<e&08KZYCqfBi51;qD3b%>SdWuDtPIp2hw4{=EOK+m(M! zKNdgv^L#Dy1N|?m?urTiHD7Q4yg2N)^7FNi>?Qwt{|G;CfACkjo!saBg+4i({yRQ& zmb_8c_<!YxufNJC|6OTy?3TUw|M(yEcKcfYdwu1<X0P|<|AznGfBd!oEjiv6SFiGu zeS_|A?T`Bd>uvR4{9GUOBVxJhe?=YXukP{p9e18MSh4<j*mI?AEI*>^xA-54cxI{o zw|dp7*q1Ms*ng?;J^Fvk-=D8J3Owdsf7>6k`Z)i(`Y%5&|H>|0^-j$0+GCfgTi4k0 z{;j=f@vP*3_W$_LO|9#{`k$>2`@eW@WlfCmtAF1s{HC@(5Bej%x&Da%&H3-@>+eL> z=UlzNv-J1+WAX`#{0Vc#>TWgJbMS53pZPza?!y1wkK=f1>d)-lP-FjZ?YHZ{9+z?d zx7fa|-uB<#dAt4##`kYjF1<VL-*ubaNB^xebU5GeU+LHKAJ_X+ZT|)RU4Oe?`@iD8 z{QCX-J{&Lo!+w1Kw*R3%*Pb4?f0WQ;aP$A?{MCQ^%cGtD-}%%0IR3l+z12VcKj-t@ zpT0KNY{UMa`$Ych|M<LhR(;ZE|D1p4d;h2W-d*}%-u;8FeWmGt(HHVJ?59Xx;q06K ziT{@6yz4)9T>Z9x%|xD>)qj)!EdCn*(ck6&(S1rkqS(3Jf7=`W-6}8BbCUO0vb9C} zdHcZs{x9Dz@caEkUhMy@U*~t+yEd_YgZ#D+{x5$x^VIjh`#<CV+)ws`@we;G{NJ8r zY4|_A`)~fo|IB~W@67l=Rr>$5|IvH?G5GX!PWzOf^QW4pcl}?7HNv-2&;8r;<EOp( z|3q7ze;*&8-~LeS|LOSTpHDlgd9Ei$>y>uBsPFjd|KR^ZJ>xXJe*)S6XZ)YM$v*eA z=D+fDdG$y1CI8R(zWRCn^V^C|>%VTlwm<rr{95m>|8q7U-=CMB{a1c=z3!jamMV+t z&;9>&eaW@>UH`xSV!!QpJHGG#+|9r1pVpuI75VyowzT1w`$qq#9+N-w^?IK4!R=qJ z|9KuW>wnPa^N%LGe+Yi9e}CGvzd!fi{xHA$LGthQkMf)EJU@N7YR$9zKWEgNZD0C7 z_|NLE{oDS#{Z0O%`g;G2e;YrxTdnyb|74F`{mvi!j~;JcIq8Rc&OhyK^(&5l-Ff%@ z&l#rwqkqkR^*i>~|Gsne0n@p@ZC_Xa<&MPc`cm71|J(k>m;E)&U;J-<^!EC`|7J($ z$K3gUuxd(Az5cxVt`D_8og>1|{r5au&;5UuQS!I;pEok>pPc#`|9XGwpM~oe|5R^_ z7y7U9X#UUbt@6?TEt%!tt~dE7^FjZ?gIOlWKtr@r^|$^XUi-}c{O;&C|6T6ZKe@Ii z<W>EH8yXkoKL-DK|G&h~N#|dP>lgc$zsc4tANwUgTP&*YtLOZpv+=*izT0B;j(_jE z{ayQSec4xuC%bh2zJI&t$UmEX&9C>|`)yNytZ?(u|F;j>YySOPX!~!{XZZ&|tlqw= z<2jz6{a;G{wPRjxee~o19r?EP_mk?+os@|X`mY&Tf9z5Gbt}Fio9X{UAJzZ({jJNs z>Q3~7`0p1t*yaDK|6J-9TmC5LfARO}-St0yF2C~sLaOc@iHr88{|f%DecfL5$V{r< z<ilD^nUCVXXTP^?{3ICvx*=8fygm2V>mT*I>wW(-8wk{A|6%;<eorr|{_J(O$M>(F zJaBwh(%i`(*|*EpJN`K?`G-GuvUI)n|N03<g7Me?R&dnMUzL1S?#HgVUw1wEwc}Xp z{p-mmUgte{eCyihmSg3|EdTzAUvDiIZFcL)k)yxmkMCV-e`~?M>oxzLUHaF4?S5AM z5yRW}r}I92{ZoJ0e!2RM-^wd$3J%V{`rh&1Z-KbkQuUgDE*~kp+h+CupTw($Z{?rQ z7vBEu>fiWPaoopOR;@YK*0}!sYqNrL+jx(x50Sj}Z>#^DSM}V-Kk*9Q-@bjFdyjO` zTlv?|W&X4Nd|t5DRQ=d3`=$Hs#2@gVlc|s1zrXy^qrd$>#QsM{e_>7j^}2dZX<uA@ z%JKMx|5X3A|NmUI{+GNVoA?)J&i@(zjklib{%HSf?(;ADng3P(XVvtszLgLud~oBG zzq-FvK5YH_+GyIQv@=&e_h0?d{@Ucf%waRo+~l?o>(^DEI^XljJ}L3<#~BOjbN+38 zqkr|^>au_;`L6$Gf8M_k{ks20)t}c@%kJ5qd0D@9d*T1wU-x&G-dA}pukkq~Hh57z z=l_Vu`46j>)<1pp_I>H)Qa8Rs*^3_Y7c$y^|7-j_=U?x`=^xJL-oF1yep>OW7;kgi zYqry){zOaus+PX-=zrqB?xXc9Kc@eTmy-W#{#Ran=Ih?%?tjiJX3sNTQ|5QBJ}$h} zo;OjF{djr8|F}P;AF5m5Kl&q|{a?rKU)a+{d^?}Vi`51Fm#jW&zhVEO<JD9CU9-^s zRv%*#x9G<@oqvs0ZrA@8%Nkr~{Aq6bfBKsGFF&sTslIFH{Nr}~`s*nx>U-)-ev3bp zj<Eb!c+RfB^h2`C=kxDo^uLtf`9X4e-~Bm<O#6lHj)l$TzI|uYW{Lm5o=cyOd3^2t z{zZKK-1|)bZCUas|7>oR>^7O=g}nP${8+VSy6wLYj~?Ye{6FQtbivv~_c#6f8SuzD zeNE&-zIo|3*Aw=xdEW16yWrpTJ3L>#*6ru1d48zuV@&_|teP+KNw5D){I7_eUM6?w z^Q`G6|AOKV9Q}6I_W!}H9sdto9sP0si)vuZuD^f(Y`Ap4wKnM?dl^H7_iTwDQT6?e z+uF`+6xhWVvQO!cs9ODT^K1VDJs*{>X=c>#sBc~0_uHuH>-NI@tUuZ7y0@NO_I&<l z`IW^7f6P9;?MM3ea+@O`xBff$OZG!)jL`e9pKH(j(^zCbt+({pw=2JXS6S?EfBxdd zkJKsu*(~>;@n7+O&;B3A&D?KZa;&o~dr<EnsrsY+!T*pyD^30@Z2$N5?Rf8N($ z-eA`zGvkwe(#Q9~s(I`7Z<l@Joc&L`_Fvw^<myGei~imCxAez~+56u~tc(64|76e6 z|J?r`9NH;Vmpn82ll|6zhJQk3j<s%^amZcz(?|Q&b4~Zz_q|@9@W(m&$fJDKU)H5Q z*H7mco-K~`&k^R9f4%%QUuxvH=Q20;Yu$e|zxFZz4fkt%N>-iXxK(xU(VYpK>fhXF zO&6$HGh42HUVZWX`F3nI_WKq5*FAb4FfH%@f_Kfocz+yU<)``kdbR-j^#!$h+g6uc z&5hKo(f+sa{l+iXi=BI{*XKsWZojeO(*Be`ihPfvzeoM+e=2<J!}{Oy@2>yber~x# z%$)jw>!;S{8a=O%er#|0Pv*tmP5&$HTi0KYmpk4c-SfO+p7S5&BjtH-|BJp_|IzgP z@mohNAJiZC^tbija?$@6{w?qOob`9@SN><)v;JMq4chh3_hbEo`sQ=X{;%vidd>dc zcg`>Ezc=K6xOd?Hmx$&owHNE{#4g|8R3D>!^vn0#5q~6WkHq|cY|-~|{ZjjLvR4_N zKdYS+|Mi!7M7@SiJ+EE$);Ii*&+qzY`9Jvf{Y(FySEO(HfA^Q|pE-Yz|MAcMlUU32 zueoUbdESNVBo=SKb^p-zKX0bS3mj&T-FVmg_Wh{z=#MPfZ}V)kqvoAG__6kxe2(;) z?&H<lkJ;DUyp^{0`Td<Am(Q<f{k-*h?92S4@muCU{n&rCQeHgzS6;*01${ene_ofm zzw^SsH6PbskFWX@{bL&c)<^Xg>3#M$)+I$cSM<u&Z~U?O>iJ!Nb^k@%{=fP5{C?fP z8*`3C6tFGb&;8bDh4rgf-)-%#9G+Eg^8d)Ocpbe*r(=^s&auATuli5m$o>aXUrsXp z=l@#v@!$PT|M=!@Zs1$_G5?%t!5X6vb2tAy{wV(J{~P5m%2oE)zd7`;|7ZOV9l6&X z#lo-Td;h;S+xfG$=5XxscT;y4``@pBb@F-T^Tj*o@SkD#pTFz>9dVnVbN+w5mOiiU zVeI=E^|JQ&<6?Av*H&{LKmW8s`S|&H843Ga|MGpS?*H~DvR3fULvP#4f1loTZhpGy z*`Ir#UjGW|wV(CCs(w{f+_zb&>tny|N?jlS?cDUe-Kpob-OtaTdooXVcki2Rn{J=X z)8GAkQ?=r5roj7?V;xtst`*x7bs-DE$Yxn9xW(;)lp`u5>_U`dHtSl^EhvoFENg|g z_+7AaL}kQXsB(PGx>kG(3d34_OR*z1Vz<B+Zbz(y$QJL$9U>hl1k0pd<^12yC+u&l zU9hy;A?16+iFaQ&)bsro-@v~ky`h>7jfhBZ2xSiAjWA8P3TG@x=8fP@Fm1TX5XOwe zc+oN0mwSiqgKZ7Bnevbr20!PPHr!^+W8WeBpbUk<zr*&yw}#uyc_@r|%<n88U?buf z-myMFCiE=y(jVNJ6U*>U^+6mHCc&hC^?<i&K_Ks+VCMf<4%nN1@W;uV&!|@r$Qu*F zEPnNXD@adF2ux2bvlxaHlB%J>G(F(T`@@6xLlCoF5VOox26xi~fxHDFBr*R5G1pvW zh&MeD&if%0$|TtkGVR`Tg+br+fH%kk5T+|{K_GL?6$WvT&q!l($6R3&-+I7Rw;+%z z%%=V;O#inWu-E<I&;92zgjwLnJ?Aph^DPG~bwBuU+gxTkzvY0j?gyW4N6!Z{#VVGu z&dY2}k3wbcSjw80*%%%5V44_$xy@bYg5aO|7+Mbg&ulD?dN4=KVma$MbSAO^NM?bV zFB_t*9!%rgv6MNFI?NBnRv(J39?aphSk8RzWdnkF;Q7mjdaDQX`F<>C{`V5Z>xT1? zBruqN=J4%U!L0YHq1)=gA}AAK4u*2D>DWaNJO^7l-Ri+ozKWI1ajzOMne1_|8mCu1 zSSnX5ClPP^OWJ^~x<}QnknQ)rGqc%?(huryQPkXiT!Q^qitf4fEBv{C2k;f7^2gj{ z4*!<ELV61;jB{YB?T2LkKR1}|za8kuWU7C=A%7$NK&Nd%691eV%<A9Z%<o?hl-qs@ z<gdBj{Qm0p8`mS$UNF^MXTA>-{t(L#VHVmJ#PILA#=QRPnaEcszeV4#{r~gl$!|p; zLi2b3KlImp-`}0T{cTt3)jushzvp}Jzr-K!IBQKm*vtQU!2X{zf1hKl&w0C_X8*pM zTECcmdbNJ}JJIiRs=uaNeK)MXxwF5P^|bYz-#g|XFxvRX__^J`@7CWrw-(R;(H-$` zM)CT;Un2iq=&iYAJ-1r+{`Xz?kAM1luGZ-O?_c@nKW#m?w=w&3+x>@kmjCg3ZnrgY z=lM^eI{VM<eCB&@YyHKY?;g*s{;Y2OJ<@;UpUmfW&y(j=t55&<Q@-S#<~_T=@^h=7 z<y(D!Q(pYe_WtFa`hVQc?R|gV`uo52#qYfB=C_{yv1$9Xli_>Hb>n}|&VRCc-_PC} z)7M{2_xHAbt^aU1vX13+`o=#V)8vnSx~XG7>*?_ge=MfSpZ~;i`uU~}b^LbytsCVE zqU|62tNKtTmbaKa{=g@b)5bdIH|=SEYAN<^7rXw34eR)K*zxb1@U%OkPUy6MTBZEy z-;w)fwBG)F&_DHO<~iFxUen^we#(xh)8*d&+Bxrf`~G=P%{TmUo3=jfr?1YM`-krz z`V<`*vAuo&+}7VuAHGld8LP8@_S5eh{=|vBJKlchIsg8)r~DiKIEv*>Zm*y5w12~& zK(TGj?e%k>`dfVO@cj{ZzUW-h!J?C&Vk39#m#ZzG_Wt0Zzu#U|Z>-6m_Wtmv+DN<J z*0(P%eoz0&Gfn=$C+5gHfz$kHKNWTCr?lpNzQ~_kY0UlosPemR_dDYKJM`rW<87+A zxxXJ@d_M7^UFDbLxw{`Fhu6I6>W}@narIrpcdNDWpl$!P->qL=zHvisY~lXcy|Y{2 ze(w7H4u;-#{l<b`E#FXZKeiAR{Vka`|KKO%$T~&t?^i{?Uk0PYqTBzAZvS|7eq!Z; zPaRL~H~dL|eoR?R?waz(4;FI%5q^;sF+DbX?sBglua4Mf$ld?(<Kl>YlHA6pe!Pmj zaqjr_GoN%L_NkuUp7isYj?KSg`G-H{M(&g4?tl8xdP9xQ>GG7HJ0dIY^vyr;X>Nqw z#HU+z<{$ZFB_?<Nad}#$?P=)<yVj?{k@rq~ijA02e9U-SmDsbkjj4&zQa7i$ZCg4q zH!}r4^Jhv*wbXuj?%P*>NN(N`+nW=veEaH&GSf{6<|hgHo7>#Bt(};gopM_W!hD;N zVl8#E&u!b{i3sM$;`E*N+_x{E_-3-nwl~LL89Vdy!4qbtn|yn7f|WlXgjx02KP3@j z`09yovr=|T-CXCk4U@UQr8PJEPo$3h?5EE+{E3-1-~9CX^q)b~^pk(G>f|&lfByZW zJF<@Rw0`Qtor!-^b>tI%KGlgo_(?lr#_8knM-TlmHu~Rl#$NgJ`4dP?Gh3toJvrBv zZ+}G=MN)9b*ywvt&Tr-7^C!T}Gq!N1jh@@Hc@uZ0|Cy{)-_?5j<d5{oiZwm_k#c8? zjv8$;nBHIC*ZREoqyB~ucT7I*G5n`0Ht+kf{f7>HKKUa&@}Jb{`1GIBIyUcn>bsxj zZ~h}YZU5;{-y{CnowiT-*{<`y<Ei}S4^yoF1&hryK3;$BlYYcM)zkZveqPtHG4K1| z_VoUyKi1Rg&mT&zdh#>(kH60Djep}K{%N1CPyYE{=YR9l`b~fIr~N<ssXpSL{^|e8 z4`0?kv78iIEL?o($Q%Phb)PewNX*OH>4|YOW{5A#H{$+$_{bjvLwlby{KCbDkK8dZ z)b}~VE&Tb=kv({r$c7=Ai<?<vU?}f%hF$pc!6T$IS%r%a9*Hq96!$qpCX-pX_`sLS z%?9i~XBdTx4;+y(FeHs>v)|_ov+(BwM{EoX`H5nZVoU%3@{bV}{NJA!+myvU`1mDG z?|*H4m9qK1r{_fPo;=sT=jOg&#t&~i`Tq3AIsU2Hc2_oiPCR^n{r_9M-|O~@S#JLv zdjEgv!>{-5*T@|G{Y`&sMRnDjCrk4t2i@HM-urq`d67B<wNKrezqOnDT~fa_XYJ|Z zo@HOB{JmaRwWIc~_JQ!4*3$YtcVf>Ni&$>&p7`#_aYOm?$VbQZH%4rHFg+^nm6_($ z+TZ_{=H0j6IT?h0o|rn@Qs>Lrvh#Dwi~idEoc@09qlTBtj#cc-dJ>`>ChTf_X)s|^ z!^<t=-*&%x`1_4`nHAHrB?(gO%S;nau`XMfkj1?$J0Xf~nMJ}YPtPv4Wx)x%*p`JQ z{PGUu;$AlCfoX)=qoh}C-&mK)cE&k+2`syw&}F!9)5=K`lj6l2#NW=dX#cCXLppfg zh9w4nJ?kE(vmff`K4R}AP_t9v%ucSNjRx-A?3~YDektoaAA4@w{rj^v_g3C&_-*kd z=l)Zt^S}Ee^}fXVz1j2s)9KE=r<1?`mXEA_!(RQL^?s=3kKG2}zIVp;tQUIwSySmx zQ`6r_r5gXAg+E*b_r?E7{JvA^55s|D{^EOM_J^17yw|x`yE{X2>W^LlnfE_B7wz_! zy8Gt1UFMg>FOtfCR4#P?^IYs)d+f!#%dY0XH~Nd${-|HaVXs}b+h6={?Yb9f{|kS| zT)eB^mG_PRbEnduId`_57d@%&Z_gsH_iMwih>LgGyViYwZx>+le*YZ%0G9b1_Wll= z_~w4?YOvNYLAwW2KQ))1`Dj~GV=i<5<8cd3{)w-@?^?~jYxeac{m+ZnR~(=0Z~ys_ zxb5#Je|~>inI2cMyhh;f+wTvrYtP^R|H|g~b)Pzy&)=>2ra~{L@>lH>!TvqJ+~dA0 zum3Ue`mYnKYo5HWc{0`NlWvUeuW;}6Vczq@y!*qv>%+YB!@T3eyjO>K+lP7Uhk46~ zdGm*P{}1(kAL@NS)cbtsX^|k^y<xhS-M9M4U43*X>~u<?Zm{cCACap@tRdd-L%IKk zbN>wI{u<7GFudD0gnMT=_tx<4uOZy2;oLjJxI@FZt;4#N!*z8cb!8%Tc_MZHMCiVW z(7h9(`z~s0LCRKx6xF_4hx)D_>b-iX=jx&EsV)=5{nm)Ntr4?ZBWAWnOmB^t+8Qyr zHDY3G#Q4^Tv8@sNw_5DmYO!~##hxj1`b=b5;PJ>`<)Qkd7s8$&I#s^82vsR{+Dv4b z=kdr-B~g9S3SrL+ohr{<go>0pEk3SW;HeYi<gYSOebND8&pVweZ(M{*l{l>?t||9C z<iAHdL+JA~3lUF=E)_0Up%$f1$B8WqJd^@d7&RvS5cd4hsq)K3s79&Nc4Ess4<&z< zgKCo^ggsYus$6mr%24Vwp4c+eW08*vx9p?}Vb2|%Dz{vOa+EqvC$`M;Sd^^ds6J_i zu;+<Rl}9c@1xlUf6I*6`Eb_%=h@;vh31QEUPL)G0LJ3Np`V(8Gdo1#1@h??5=OUD* z)M+%4Wu`}xkBXw&Bo1NEhEA1zE<$lioq7{lrh6oLt1MKVbVA6pNW!zHvt0USZtC{Q zcjL;=r+#-TWA)wP;^fV8u$0qi;*l91PChDvYLgg*J!?8u_P7YeDsk#fJTlG0$$O7> z#vxUqWF=0+i7qodn0!<|s!sYK<XO_Gvcp9vT8UG4qRTW7CU2EO)kzzKJVQEFR=5ZS zD{*R0beZD8<fZaaWm19A!Oi-dIunmf^*H3E5~w<9fsm(9r^*r+p+F^0jfqDldmQpq zxu`NJLC905Q)P;akf#!-%ETiRJq~%O1gcC@5c1^dRB3S$a#Z3}n0Tb$<B+?`Mde8i zf}VRiRAO9&bd@*-C%SZaFuACFRGjodz_X-7#ll5MTB#yTCs1jUf}m$lhl-1fkfIW& zz{Df%9*3M&E-FrXAmEwPp<?19B&o#7Gx12P#~~+`K*c8YGeYu8oct4A+C7?_RW>S4 zx**`0(xGDDA|$TF$vx4f)uYKt#ZYn51OZQ%4iyC#A$}!J_K7ad9!-ua8x<x62zaV= zl!JrTNkvd`QiFh}O^1q%ix968C)>m$O&(5;DiakZIS6>lbg1yS2(c=0GEF?v;Nj$; zBB(HlLBR7*yUHJDp}&fp|0W!%^Ki0PnJ7Q$0Key*c9l2ILSGd*e@!@2<KbkdA}Bwp zf!}eDdy>r+j_Dp-+Es2i3%yq4{5rv<%7e)k5@#P3Ie$zzQtffbR^_7Hqy&D?HSH=_ zoP}O0a(<a`q|)P%jY^>0Bn5uYo_3WZ&O#3rIX_G|QtolcTIHhbqz8PSIqfQ2oP};G za=w{xq}1b(l}e!Oqy>DQKJ6+?oP{nba=w^wq}bz-rOHK_NeO(OI_)Y`oP|y*az2@G zq|oD#g-W2zBn3WCo_3WM=LtI%56VpXlRjxm`lOt+NhxWQV$vpsq)qZko8*!<$tLZd zsL4Yi&kVJb>1v+oYMz$ro;hkKv(-Ga)lO!qd1k4d%vAHtR6CiW23K=3P0ce+?PRK& zXR4a#Vs+0HwUhby)SOIF1F6}o?73I@<Q`?uJ<2C{D|_x%KDkBNbBprH&B~sel}~O` z_S~d=a-*{6L*+`|mM6LVllEj!+LAqKP4=WE*^}mEPnwcFsV93<OZKFi>`5irlX9{r zrDRWv$(|IFJ;^6~l1ug^o9sy@*^_j#C#hsllF6PVl0AthdlF0bq(51czGO{$lQro{ z)}%XGldfb<I+Hc&NY<o1S(CP8O*#`Z=}63^Ju#EE#7tTfGigc8q&YE@ro>F@iJ8<A zGpQzKQc29DoR~=|F_U6qCWXXI@`;(`5;MsrW|B$FB%PQ^Dly>jJ9$ji^O)+%qpF@q zRZkvK^*o|_^02DsVbzm|R6P%=o;;}Pc~JG_0aecfswelWdhS;}xlh$|pX$lIsy|%} zJfGQkKC<z=XXAOx#`Btu=Or7@b2gr*Y&?(IcpkFx+-Kvt%f@q?jprsC&viDQt86@% z*?2Cp@tkMlIm^a#nvLfq8_zx)&n_F!HXF|-8_zl$&ng?wG8@k#8_zr&&nz3yG#k$( z8_zf!&nO$uFq@OnrF#$q*-dIOO6y-w+|%j)Nl518?j)7<ize>*<ML@w+mpR^ldgaF zxYwmznb`9r)@)MvO^<si%9V+|Pv)9|JzbeN@ky=gr0b%d_rg>vHT#~-HJx<*w@2I+ zrAosI;Cy(GMWr&#^%IxG$?OJ|e8x$8SUf)6lRvq-LWTdhM_iFgC4c`DPpOm9)hha? zllDY=e6mt}&o!%{bI)wgPurwV&hAw?|GXmnLW_6RL!rv&y&yA}i0i#<^{#q2DQ*Wc zb2_`3_v|q5=8*1PR~LnEeG#Plb^V`Yw@>|&{m+aKUSClzKeO!qhyRSa4Ba6!PB1b6 E0G!=-5dZ)H literal 0 HcmV?d00001 diff --git a/data/demo.RData b/data/demo.RData deleted file mode 100644 index 0deeba01a92e59aa76a5bfe51fed67a7695c1f1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14821 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 diff --git a/vignettes/PolyHaplotyper-vignette.Rmd b/vignettes/PolyHaplotyper.Rmd similarity index 77% rename from vignettes/PolyHaplotyper-vignette.Rmd rename to vignettes/PolyHaplotyper.Rmd index d65b86f..045afc5 100644 --- a/vignettes/PolyHaplotyper-vignette.Rmd +++ b/vignettes/PolyHaplotyper.Rmd @@ -9,8 +9,11 @@ vignette: > \usepackage[utf8]{inputenc} --- -This vignette shows how to use the main functions provided in PolyHaplotyper and -explains their output. +# PolyHaplotyper vignette +### Roeland E. Voorrips, `r Sys.Date()` + +This vignette shows how to use the main functions provided in R package +PolyHaplotyper and explains their output. The PolyHaplotyper package contains tools to derive phased haplotypes from unphased bi-allelic marker (SNP) data in collections of individuals of @@ -34,7 +37,8 @@ are available, or if the parents have missing data. Load the package and demo data: ```{r} library(PolyHaplotyper) -data(demo) # contains data.frames snpdos (SNP dosages) and ped (pedigree) +data(PolyHaplotyper_demo) +# PolyHaplotyper_demo contains data.frames snpdos (SNP dosages) and ped (pedigree) ``` Two inputs are necessary for the assignment of haplotypes: a matrix or data.frame with the dosages of the markers, and a list indicating which markers @@ -76,7 +80,7 @@ hblist <- haploblock_df2list(snpdos, mrkcol=1, hbcol=2) # number of markers in each haploblock: sapply(hblist, length) ``` -This small example has data for 7 haploblocks, each with 4 markers. +This small example has data for 7 haploblocks, each with 4 or 5 markers. ### Marker data The marker data must be supplied as a matrix or data.frame, with markers in @@ -141,7 +145,8 @@ dim(snpdos) ``` Function mergeReplicates has converted the snpdos data.frame to a matrix, with the rownames taken from the column "marker". For each individual present -in multiple replicates only the first column name is retained. We see that +in multiple replicates only the first column name is retained, and the +merged data are the consensus scores over all replicates. We see that snpdos now has 31 less columns: 1 was the removed "marker" column and 30 were columns for the duplicated samples, corresponding to the 30 duplicates removed from the pedigree. @@ -183,8 +188,16 @@ results <- inferHaplotypes(mrkDosage=snpdos, ploidy=6, haploblock=hblist, parents=parents, FS=FS) ``` -(The first output line mentions ahccompletelist. This can be ignored for now. -More information on ahccompletelist is mentioned in section Remarks.) +We assume here that you run this command in a directory +that does not contain file ahclist_6x.RData or ahccompletelist_6x.RData (more +about these files in section Remarks). +The first output line says that ahccompletelist cannot be loaded. Then you +will get messages about sets of haplotype combinations that need to be +calculated. These would normally be available from the ahccompletelist file, +but failing that they are calculated as needed. For this small example this +will take about 1 min. When this is done the actual haplotyping is +done, and again progress messages are shown. + This function call returns a list with one item for each haploblock. Each of these items is itself a list with several items. We'll take a look at the results for the first haploblock which are in results[[1]]. @@ -195,7 +208,7 @@ allhap function. ```{r} names(results[[1]]) # show part of hapdos: the dosages of the haplotypes, for the first haploblock: -results[[1]]$hapdos[, 11:18] +results[[1]]$hapdos[, 1:8] # show the composition of the used haplotypes: usedhap(results[[1]]) # show the composition of all possible haplotypes: @@ -203,16 +216,16 @@ allhap(results[[1]]) ``` Haploblock 1 has 4 SNPs, so there are 16 (2^4) possible haplotypes. These are all listed in the matrix returned by allhap, with a 0 indicating the non-counted -(reference) SNP allele and a 1 the dosage-counted (alternative) allele. With -more markers there will be many more columns, so mostly function usedhap is -more useful; this shows only the haplotypes that are present in at least one -individual. +(reference) SNP allele and a 1 the dosage-counted (alternative) allele. In +haploblocks with more markers there will be many more columns, so +function usedhap is often more useful; this shows only the haplotypes that +are present in at least one individual. Matrix hapdos has the dosages of each haplotype in each individual, in the same layout as the marker dosages in snpdos. Only the haplotypes that occur in the population are shown: in this case haplotypes 1, 3, 7, 8 and 11 (appended to the haploblock name). For each individual the haplotype dosages sum to the -ploidy (6). +ploidy (6). Non-haplotyped individuals have NA dosages for all haplotypes. Another interesting item in the results is imputedGenotypes. For some FS individuals that have missing data in the marker genotypes it is still possible @@ -221,8 +234,8 @@ dosages. Below we see these imputed marker dosages compared with the original dosages: ```{r} -results[[1]]$imputedGeno -snpdos[hblist[[1]], colnames(results[[1]]$imputedGeno)] +results[[1]]$imputedGeno[, 1:8] +snpdos[hblist[[1]], colnames(results[[1]]$imputedGeno)[1:8]] ``` The other items in the results for each haploblock are mostly intended for @@ -245,8 +258,8 @@ knitr::kable(ovwFS$ovw[, c(1:2, 27:31)]) # ``` overviewByFS returns a list with two items: ovw and messages. Both are matrices -with one row per haploblock. Matrix ovw first gives the number of markers and -of assigned haplotypes for the haploblock over all individuals, and then +with one row per haploblock. Matrix ovw first gives the number of markers (nmrk) +and of assigned haplotypes (nhap) for the haploblock over all individuals, and then for each FS family specific information: parmrk (0, 1 or 2: the number of parents with complete marker dosages), fit (1 if a polysomic segregation could be fitted, else 0), P (the chi-squared P-value of the best fitting segregation @@ -283,14 +296,18 @@ and parents_arr. In both cases the first dimension are individuals and the third dimension are haploblocks; the second dimension are the columns as shown above. Array ped_arr shows for each individual in the pedigree whether it -has complete marker data and an assigned haplotype combination, and if its -haplotype combination is compatible with that of its parents assuming Double -Reduction to be impossible or possible (NA indicates that the individual itself -or both its parents have no haplotypes assigned). +has complete marker data (mrk), whether some of its marker dosages were +imputed (imp) and whether it has an assigned haplotype combination (hap), +and if its haplotype combination is compatible with that of its parents +assuming Double Reduction to be impossible (noDR) or possible (withDR); +NA indicates that the individual itself or both its parents have no +haplotypes assigned. Array parents_arr gives information for each individual that is a parent, -whether it has complete marker data and an assigned haplotype combination, -and how many of its progeny are compatible with it. For -details see the help (?pedigreeHapdosCheck). +whether it has complete marker data (par_mrk) and an assigned haplotype +combination (par_hap), how many of its progeny have complete marker data (mrk) +and haplotype data (hap), and how many of its progeny are compatible +with it, assuming Double Reduction to be impossible(nonDRmatch) or +possible (DRmatch). For details see the help (?pedigreeHapdosCheck). ### Summary statistics The results of overviewByFS and pedigreeHapdosCheck can be summarized using @@ -298,13 +315,15 @@ function calcStatistics: ```{r} cst <- calcStatistics(pedchk=pedchk, ovwFS=ovwFS) knitr::kable(cst$pedstats) -knitr::kable(cst$FSstats, digits=c(0,0,0,2,2)) +knitr::kable(cst$FSstats, digits=c(0,0,0,2,2,2)) # ``` calcStatistics returns a list with two matrices. Matrix pedstats gives per -haploblock the totals over all individuals from pedchk\$ped_arr. Matrix FSstats -gives the totals or means per FS family over all haploblocks from ovwFS\$ovw. -For details see the help (?calcStatistics). +haploblock the totals over all individuals from pedchk\$ped_arr. +The total of columns match.NA + noDR.TRUE + noDR.FALSE and +of match.NA + withDR.TRUE + withDR.FALSE is the total number of individuals. +Matrix FSstats gives the totals or means per FS family over all haploblocks +from ovwFS\$ovw. For details see the help (?calcStatistics). ### Number of markers vs number of haplotypes Finally we can get a table of the number of haplotypes vs the number of markers @@ -312,13 +331,13 @@ per haploblock: ```{r} calcMrkHaptable(ovwFS=ovwFS) ``` -In this small example all haploblocks have 4 markers, so the table contains -only one row. +In this example there are 5 haploblocks with 4 markers and 2 haploblocks with 5 +markers, so the table contains two rows. ### Segregation in one FS, one haploblock With function showOneFS it is possible to study the segregation of one haploblock in one FS family. Both the segregation of markers and of -haplotypes is computed. +haplotypes is shown. ```{r} # show the segregation for FS number 1 (FSnr=1) and @@ -334,70 +353,65 @@ first two columns represent the parents. Their column names are the names of the parents in (brackets), and their first row does not contain a frequency but the marker dosage IDs of the parents. All following columns have as names one of the marker dosage IDs occurring in the FS, and the first row has the -number of FS individuals with that marker dosage ID. In all columns, the next -rows contain the dosages of the markers or the haplotypes. +number of FS individuals with that marker dosage ID. (A marker dosage ID or +mrkdid is a number encoding the dosages of all the SNP markers in an +individual). In all columns, the next rows contain the dosages of the markers +or the haplotypes. The third element of the result is a matrix listing the composition of the haplotypes present, in terms of their marker alleles (with a 0 being the reference allele and a 1 the alternative allele). # Remarks -PolyHaplotyper creates and uses -two or three variables in the global environment (.GlobalEnv): ahcinfo, ahclist -and possibly ahccompletelist. These variables are used to manage calculated -lists with all possible haplotype combinations that result in the same marker +PolyHaplotyper uses one or two pre-calculated lists, ahclist and +ahccompletelist, that contain +all possible haplotype combinations that result in the same marker dosage combination. These may take a lot of time to calculate and are completely -reusable between haploblocks and runs of inferHaplotypes, so it would be a -waste to discard them. +reusable between haploblocks and runs of inferHaplotypes. -The ahclist and ahccompletelist lists are also stored +The ahclist and ahccompletelist lists are stored as files with names like ahclist_6x.RData and ahccompletelist_6x.RData (where -6x indicates the ploidy). - -The ahclist lists store the haplotype combinations for any marker dosage -combination that is encountered in the data. They are modified "on the fly" -and store only results for haploblock sizes and ploidies for which no -ahccompletelist is available. - -The ahccompletelist lists store the possible haplotype -combinations for ALL marker dosage combinations, for haplotypes from 1 marker -up to some maximum. These lists must be pre-calculated. They -are only used but not changed by inferHaplotypes, and speed up the +6x indicates the ploidy). These files must be either in the working +directory, or their location must be specified in the call to inferHaplotypes +by setting parameter ahcdir. If these files are not present the haplotyping +will proceed normally, but the computation of the ahc data will add very +much to the processing time; a message to that effect will be shown. + +An ahccompletelist stores the possible haplotype +combinations for ALL marker dosage combinations, for haploblocks from 1 marker +up to some maximum. These lists are pre-calculated: they +are only used but not changed by inferHaplotypes. They speed up the calculations enormously, but it takes considerable time to calculate them and above 7 or 8 markers (in tetraploids) or 6 markers (in hexaploids) they become too large. They are useful if haplotyping is a recurrent activity. -The package does not include ahccompletelist files because of their size. +An ahclist stores the haplotype combinations for any marker dosage +combination that is encountered in the data, and that cannot be found in the +ahccompletelist (because that list is not available, or is limited to haploblocks +with fewer markers). These lists are created or extended "on the fly" and +then (re-)saved to file. + +The package does not include ahclist or ahccompletelist files because of their size. The demo in this vignette doesn't need them as the number of haploblocks -is small and the calculation of the ahclist data requires very little time. -After running inferHaplotypes you will find file ahclist_6x.RData in the +is small and the calculation of the ahclist data requires little time. +After running the vignette example you will find file ahclist_6x.RData in the working directory. -The ahccompletelist files can be computed using function build_ahccompletelist -completeAllHaploComb: +The ahccompletelist files can be computed using function build_ahccompletelist: ```{r eval=FALSE} build_ahccompletelist(ploidy=6, maxmrk=5, overwrite=FALSE) ``` -Apart from the file ahccompletelist_4x.RData this will also generate -separate files for each number of markers, with names ahc_4x_nmrk1.RData ... -ahc_4x_nmrk5.RData; these can be deleted. +Apart from the file ahccompletelist_6x.RData this will also generate +separate files for each number of markers, with names ahc_6x_nmrk1.RData ... +ahc_6x_nmrk5.RData; these can be deleted. Alternatively the following ahccompletelists are available upon request -from roeland.voorrips@wur.nl +from roeland.voorrips@wur.nl. ploidy 4, up to 7 markers: ahccompletelist_4x.RData, 41 MB ploidy 4, up to 8 markers: ahccompletelist_4x.RData, 769 MB ploidy 6, up to 5 markers: ahccompletelist_6x.RData, 9 MB ploidy 6, up to 6 markers: ahccompletelist_6x.RData, 515 MB -The ahccompletelist files and ahclist files must be copied to the working -directory to be accessible by inferHaplotypes and other functions in -the PolyHaplotyper package. Alternatively they can be stored in any other -directory, say "MyPath/MyAhcdir", and an option must be set before -calling inferHaplotypes: -```{r eval=FALSE} -options(PolyHaplotyper_ahcdir = "MyPath/MyAhcdir") -``` - For those interested in some background on these lists: An ahclist for a given ploidy has one item for each number of markers per @@ -407,7 +421,7 @@ for which the set of haplotype combinations has been calculated. The name of such an item is the mrkdid (marker dosage ID) and the item contains a matrix with (ploidy) rows and one column per possible haplotype combination, with the numbers of the haplotypes that are present (e.g. a column with numbers -0, 17, 17, 24 means that haplotypes 0 and 24 are present in simplex and +1, 17, 17, 24 means that haplotypes 1 and 24 are present in simplex and haplotype 17 is present in duplex, in this tetraploid combination). The order of the mrkdid items in the sublist for a certain number of markers is the order in which they are calculated, and they are accessed by diff --git a/vignettes/ahclist_6x.RData b/vignettes/ahclist_6x.RData deleted file mode 100644 index 33c9e61a1cd7e9a06b693a16045d316326d877b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4540 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 -- GitLab