R/rapt_file.R

Defines functions writePOS createSpec createDet createSpat readRRNG readATO readPOS

Documented in createDet createSpat createSpec readATO readPOS readRRNG writePOS

#
# This file contains methods for importing and conditioning APT data
#

#### Load Data ####

### readPOS ###
#' Read a POS File
#'
#' \code{readPOS} reads a POS file (from IVAS) into a `data.frame`.
#'
#' @param filepath Character. The file path to the POS file.
#' @return A `data.frame` with columns corresponding to the x, y, and z
#'   positions of the reconstruction and the mass-to-charge ratio.
#'
#' @references Larson *et al.*,
#'   *Local Electrode Atom Probe Tomography: A User's Guide* (2009)
#' @family APT import functions
#'
#' @export
readPOS <- function(filepath) {
  pos.len <- file.info(filepath)["size"] / 4
  pos.len <- as.numeric(pos.len)
  pos.raw <- readBin(
    filepath,
    what = "numeric",
    size = 4, endian = "big", n = pos.len
  )
  pos.mat <- matrix(pos.raw, ncol = 4, byrow = T)
  pos.dat <- as.data.frame(pos.mat)
  names(pos.dat) <- c("x", "y", "z", "mass")
  pos.name <- sub("\\.pos$", "", basename(filepath), ignore.case = TRUE)
  attr(pos.dat, "metaData") <- list(
    name = pos.name
  )
  return(pos.dat)
}

### readATO ###
#' Read an ATO File
#'
#' \code{readATO} reads an ATO file (from IVAS) into a `data.frame`.
#'
#' @param filepath Character. The file path to the ATO file.
#' @return A `data.frame` with columns corresponding to the ATO structure
#'   definition.
#'
#' @references Larson *et al.*,
#'   *Local Electrode Atom Probe Tomography: A User's Guide* (2009)
#' @family APT import functions
#'
#' @export
readATO <- function(filepath) {
  ato.len <- file.info(filepath)["size"] / 4
  ato.len <- as.numeric(ato.len)
  ato.file <- file(filepath, open = "rb")
  seek(ato.file, where = 8)
  ato.raw <- readBin(
    ato.file,
    what = "numeric",
    size = 4, endian = "little", n = ato.len
  )
  ato.mat <- matrix(ato.raw, ncol = 14, byrow = T)
  ato.dat <- as.data.frame(ato.mat)
  names(ato.dat) <- c(
    "x", "y", "z", "mass", "clusID", "pIndex", "Vdc",
    "TOF", "dx", "dy", "Vp", "shank", "FouR", "FouI"
  )
  ato.name <- sub("\\.ato$", "", basename(filepath), ignore.case = TRUE)
  attr(ato.dat, "metaData") <- list(
    name = ato.name
  )
  close(ato.file)
  return(ato.dat)
}

### readRRNG ###
#' Read an RRNG File
#'
#' \code{readRRNG} reads an RRNG file from IVAS into an RNG object for ranging
#' a \code{\link[MALDIquant:MassSpectrum-class]{MassSpectrum}} generated by
#' \code{\link{createSpec}}
#'
#' @param filepath Character. A filepath to the RRNG file.
#' @return A `data.frame` with columns corresponding to the mass ranges,
#'   ion volume, name, molecular formulae (`NA` if not present), and color
#'   for display.
#'
#' @references Larson *et al.*,
#'   *Local Electrode Atom Probe Tomography: A User's Guide* (2009)
#' @family APT import functions
#'
#' @export
readRRNG <- function(filepath) {
  text <- readLines(filepath, warn = FALSE)
  n <- grep("Number=", text, value = TRUE)
  n <- strsplit(n, "=")
  n <- sapply(n, function(m) {
    as.numeric(m[2])
  })
  elem <- strsplit(text[1:n[1] + 2], "=")
  elem <- sapply(elem, function(m) {
    m[2]
  })
  r.pos <- grep("[Ranges]", text, fixed = TRUE)
  entries <- sub("^Range[[:digit:]]+=", "", text[-(1:(r.pos + 1))])
  entries <- strsplit(entries, " ")
  dat <- lapply(entries, function(X) {
    mass.start <- as.numeric(X[1])
    mass.end <- as.numeric(X[2])
    mass.volume <- as.numeric(sub("Vol:", "", X[3]))
    mass.name <- sapply(elem, function(el) {
      m <- paste0(el, ":")
      w <- grepl(m, X)
      if (any(w)) {
        n <- sub(":", "", X[w])
      } else {
        n <- NA
      }
      return(n)
    })
    mass.name <- na.omit(mass.name)
    mass.name <- paste0(mass.name, collapse = "")
    data("isotopes", package = "enviPat", envir = environment())
    form.warn <- enviPat::check_chemform(isotopes, mass.name)[, 1]
    mass.formula <- mapply(function(name, warn) {
      if (warn) {
        NA
      } else {
        name
      }
    }, mass.name, form.warn)
    mass.color <- sub("Color:", "#", tail(X, n = 1))
    data.frame(
      start = mass.start,
      end = mass.end,
      volume = mass.volume,
      name = mass.name,
      formula = mass.formula,
      color = mass.color,
      stringsAsFactors = FALSE
    )
  })
  dat <- do.call(rbind, dat)
  rownames(dat) <- NULL
  rng.name <- sub("\\.rrng$", "", basename(filepath), ignore.case = TRUE)
  attr(dat, "metaData") <- list(
    name = rng.name
  )
  return(dat)
}

#### Transform Data ####

### createSpat ###
#' Create a pp3 from a POS or ATO
#'
#' \code{createSpat} creates a \code{\link[spatstat.geom]{pp3}} from a POS or ATO
#' data frame.
#'
#' @param pos A POS or ATO data frame.
#' @param win The domain of the data.
#' @return A \code{\link[spatstat.geom]{pp3}} with the x,y,z positions of the hits in
#'   the supplied POS or ATO.
#'
#' @family APT data transformation functions
#'
#' @seealso \code{\link{readPOS}}, \code{\link{readATO}},
#'   \code{\link[spatstat.geom]{pp3}}
#'
#' @export
createSpat <- function(pos, win = NULL, marks = NULL) {
  pp3.box <- win
  if (is.null(win)) {
    pp3.box <- sapply(pos[1:3], range)
  }
  pp3.dat <- spatstat.geom::pp3(pos$x, pos$y, pos$z, pp3.box, marks = marks)
  pp3.dat <- pp3.dat[spatstat.geom::inside.boxx(pp3.dat, w = pp3.box)]
  attr(pp3.dat, "metaData") <- attr(pos, "metaData")
  return(pp3.dat)
}

### createDet ###
#' Create a "ppp" from an ATO.
#'
#' `createDet` generates a \code{\link[spatstat.geom]{ppp}} of detector hits from an
#' ATO.
#'
#' @param ato An ATO data frame.
#' @param window An object of class \code{\link[spatstat.geom]{owin}}. If `NULL` (the
#' default), a window will be calculated from the data using
#'   \code{\link[spatstat.geom]{ripras}}.
#' @return A \code{\link[spatstat.geom]{ppp}} with the positions of the detector hits
#'   from the ATO.
#'
#' @family APT data transformation functions
#'
#' @seealso \code{\link{readATO}}, \code{\link[spatstat.geom]{ppp}},
#'   \code{\link[spatstat.geom]{ripras}}
#'
#' @export
createDet <- function(ato, win = NULL, marks = NULL) {
  if (is.null(win)) {
    win <- spatstat.geom::ripras(ato$dx, ato$dy)
    unitname(win) <- "cm"
  }
  det.dat <- spatstat.geom::ppp(ato$dx, ato$dy, window = win, marks = marks)
  attr(det.dat, "metaData") <- attr(ato, "metaData")
  return(det.dat)
}

### createSpec ###
#' Create a \code{\link[MALDIquant:MassSpectrum-class]{MassSpectrum}} from a POS
#' or ATO
#'
#' `createSpec` generates a
#' \code{\link[MALDIquant:MassSpectrum-class]{MassSpectrum}} object with a
#' specified resolution from an ATO or POS data frame (like that created by
#' \code{\link{readPOS}}).
#'
#' @param pos A POS or ATO data frame.
#' @param res numeric. The bin width of the mass spectrum.
#' @param clip numeric of length two. The minimum and maximum mass values to be
#'   included in the mass spectrum. If `NULL` (the default), all values are
#'   included.
#'
#' @return A \code{\link[MALDIquant:MassSpectrum-class]{MassSpectrum}} from the
#'   `mass` field of the POS or ATO, with the resolution set by `res`.
#'
#' @details
#' The input POS or ATO is binned by mass values; the `res` parameter sets
#' the width of the mass bins used in \code{\link[graphics]{hist}} to create the
#' input to the \code{\link[MALDIquant]{createMassSpectrum}} call, and also acts
#' as a tolerance around the spectrum minimum and maximum mass. The minimum of
#' the mass value is zero unless `clip` is set.
#'
#' @family APT data transformation functions
#'
#' @seealso \code{\link{readPOS}}, \code{\link{readATO}},
#'   \code{\link[MALDIquant:MassSpectrum-class]{MassSpectrum}}
#'
#' @export
createSpec <- function(pos, res = 0.05, clip = NULL) {
  m <- pos$mass
  if (is.numeric(clip) & length(clip) == 2) {
    m <- m[m >= clip[1] & m <= clip[2]]
  }
  ms.max <- ceiling(max(m) / res) * res
  ms.min <- floor(min(m) / res) * res
  ms.breaks <- seq(ms.min, ms.max, res)
  ms.hist <- hist(m, ms.breaks, plot = F)
  ms.dat <- MALDIquant::createMassSpectrum(
    ms.hist$mids[-1], ms.hist$counts[-1],
    metaData = attr(pos, "metaData")
  )
  return(ms.dat)
}

#### Write Data ####

### writePOS ###
#' Write a POS File
#'
#' `writePOS` writes a POS `data.frame` into a POS file
#'
#' @param pos A `data.frame` with columns of x, y, z, and mass, following the
#'   structure of the `data.frame` returned by \code{\link{readPOS}}.
#' @param filepath A string. The file path to the `POS` file.
#' @return `NULL`
#'
#' @references Larson `et al.`,
#'   `Local Electrode Atom Probe Tomography: A User's Guide` (2009)
#' @seealso \code{\link{readPOS}}
#'
#' @export
writePOS <- function(pos, filepath) {
  p.mat <- as.matrix(pos)
  p.vec <- as.numeric(t(p.mat))
  writeBin(p.vec, filepath, size = 4, endian = "big")
}
aproudian2/rapt documentation built on Dec. 15, 2022, 4:24 a.m.