R/XYplot.R

Defines functions `XYplot`

# Copyright (c) 2019, Adrian Dusa
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, in whole or in part, are permitted provided that the
# following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * The names of its contributors may NOT be used to endorse or promote products
#       derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL ADRIAN DUSA BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

`XYplot` <- function(x, y, data, relation = "sufficiency", mguides = TRUE,
                     jitter = FALSE, clabels = NULL, enhance = FALSE, model = FALSE, ...) {
    other.args <- list(...)
    funargs <- unlist(lapply(match.call(), deparse)[-1])
    if (missing(x)) {
        cat("\n")
        stop(simpleError("Argument x is mandatory.\n\n"))
    }
    via.web <- FALSE
    if (length(testarg <- which(names(other.args) == "via.web")) > 0) {
        via.web <- other.args$via.web
        other.args <- other.args[-testarg]
    }
    negated <- logical(2)
    xname <- yname <- ""
    minus <- rawToChar(as.raw(c(226, 128, 147)))
    testit <- capture.output(tryCatch(eval(x), error = function(e) e))
    if (length(testit) == 1 & is.character(testit)) {
        if (grepl("Error", testit)) {
            x <- as.vector(funargs["x"])
        }
    }
    if (is.vector(x) & is.character(x) & any(grepl("\\$solution", funargs["x"]))) {
        x <- list(x)
    }
    if (is.character(x)) {
        if (x == tolower(x) & x != toupper(x)) {
            if (eval.parent(parse(text = sprintf("is.element(\"%s\", ls())", toupper(x))), n = 1)) {
                conds <- toupper(x)
                x <- 1 - eval.parent(parse(text = sprintf("get(\"%s\")", toupper(x))), n = 1)
                negated[1] <- TRUE
            }
        }
    }
    else {
        testit <- capture.output(tryCatch(eval(x), error = function(e) e))
        if (length(testit) == 1 & is.character(testit)) {
            if (grepl("Error", testit)) {
                x <- as.vector(deparse(funargs["x"]))
            }
            else if (hastilde(testit)) {
                negated[1] <- TRUE
                if (eval.parent(parse(text = sprintf("is.element(\"%s\", ls())", notilde(testit))), n = 1)) {
                    x <- 1 - eval.parent(parse(text = sprintf("get(\"%s\")", notilde(testit))), n = 1)
                }
                else {
                    x <- testit
                }
            }
        }
    }
    if (is.list(x)) {
        if (any(grepl("\\$solution", funargs["x"]))) {
            model <- TRUE
            obj <- get(unlist(strsplit(funargs["x"], split = "[$]"))[1])
            data <- obj$tt$initial.data
            y <- obj$tt$options$outcome
            if (obj$tt$options$neg.out) {
                y <- paste("~", y, sep = "")
            }
            x <- paste(unlist(x), collapse = " + ") 
        }
    }
    if (!is.data.frame(x) & !is.matrix(x) & !missing(y)) {
        testit <- capture.output(tryCatch(eval(y), error = function(e) e))
        if (length(testit) == 1 & is.character(testit)) {
            if (grepl("Error", testit)) {
                y <- as.vector(funargs["y"])
            }
        }
        if (!is.character(y)) {
            testit <- capture.output(tryCatch(eval(y), error = function(e) e))
            if (length(testit) == 1 & is.character(testit)) {
                if (grepl("Error", testit)) {
                    y <- deparse(funargs["y"])
                }
                else if (hastilde(testit)) {
                    negated[2] <- TRUE
                    if (eval.parent(parse(text = sprintf("is.element(\"%s\", ls())", notilde(testit))), n = 1)) {
                        y <- 1 - eval.parent(parse(text = sprintf("get(\"%s\")", notilde(testit))), n = 1)
                    }
                    else {
                        y <- testit
                    }
                }
            }
        }
        else {
            if (y == tolower(y) & y != toupper(y)) {
                if (eval.parent(parse(text = sprintf("is.element(\"%s\", ls())", toupper(y))), n = 1)) {
                    conds <- toupper(y)
                    y <- 1 - eval.parent(parse(text = sprintf("get(\"%s\")", toupper(y))), n = 1)
                    negated[2] <- TRUE
                }
            }
        }
    }
    if (is.character(x)) {
        if (length(x) == 1) {
            x <- splitstr(x)
        }
        if (length(x) == 1) {
            x <- unlist(strsplit(x, split = "=>"))
            if (length(x) == 1) {
                x <- unlist(strsplit(x, split = "<="))
                if (length(x) > 1) {
                    relation <- "necessity"
                    y <- trimstr(x[2])
                    x <- trimstr(x[1])
                }
            }
            else {
                y <- trimstr(x[2])
                x <- trimstr(x[1])
            }
            if (missing(y)) {
                cat("\n")
                stop(simpleError("The outcome's name is missing.\n\n"))
            }
            else if (!is.character(y)) {
                cat("\n")
                stop(simpleError("x and y should be both column names from the data.\n\n"))
            }
        }
        else {
            if (!missing(y)) {
                if (is.data.frame(y)) {
                    data <- y
                }
            }
            y <- x[2]
            x <- x[1]
        }
        if (missing(data)) {
            cat("\n")
            stop(simpleError("Data is missing.\n\n"))
        }
        else {
            verify.qca(data)
        }
        xname <- as.character(parse(text = x))
        yname <- as.character(parse(text = y))
        x <- gsub(minus, "-", gsub("[[:space:]]", "", x))
        y <- gsub(minus, "-", gsub("[[:space:]]", "", y))
        negated <- logical(2)
        negated[1] <- identical(unname(substring(x, 1, 2)), "1-")
        negated[2] <- identical(unname(substring(y, 1, 2)), "1-")
        if (any(checks <- grepl("1-", c(x, y)) & !negated)) {
            cat("\n")
            stop(simpleError(paste("Incorrect expression in \"", paste(c(x, y)[checks], collapse = "\" and \""), "\".\n\n", sep = "")))
        }
        x <- compute(x, data = data)
        y <- compute(y, data = data)
        negated <- logical(2)
    }
    else if (is.data.frame(x) | is.matrix(x)) {
        verify.qca(as.data.frame(x))
        if (ncol(x) < 2) {
            cat("\n")
            stop(simpleError("At least two columns are needed.\n\n"))
        }
        xname <- colnames(x)[1]
        yname <- colnames(x)[2]
        y <- x[, 2]
        x <- x[, 1]
    }
    else if (!missing(y)) {
        if (length(x) > 1 & is.numeric(x)) { 
            oneminus <- identical(unname(substring(gsub("[[:space:]]", "", funargs[1]), 1, 2)), "1-")
            if (any((hastilde(funargs[1])    & !tilde1st(funargs[1])) | 
                    (grepl("1-", funargs[1]) & !oneminus)
                   )) {
                cat("\n")
                stop(simpleError(paste("Incorrect expression in \"", funargs[1], "\".\n\n", sep = "")))
            }
            negated[1] <- oneminus | tilde1st(funargs[1])
            xname <- "X"
            tc <- capture.output(tryCatch(getName(funargs[1]), error = function(e) e, warning = function(w) w))
            if (!grepl("simpleError", tc)) {
                xname <- notilde(getName(funargs[1]))
            }
        }
        if (length(y) > 1 & is.numeric(y)) { 
            oneminus <- identical(unname(substring(gsub("[[:space:]]", "", funargs[2]), 1, 2)), "1-")
            if (any((hastilde(funargs[2])    & !tilde1st(funargs[2])) | 
                    (grepl("1-", funargs[2]) & !oneminus)
                   )) {
                cat("\n")
                stop(simpleError(paste("Incorrect expression in \"", funargs[2], "\".\n\n", sep = "")))
            }
            negated[2] <- oneminus | tilde1st(funargs[2])
            yname <- "Y"
            tc <- capture.output(tryCatch(getName(funargs[2]), error = function(e) e, warning = function(w) w))
            if (!grepl("simpleError", tc)) {
                yname <- notilde(getName(funargs[2]))
            }
        }
        if (length(y) == 1 & is.character(y)) {
            if (missing(data)) {
                cat("\n")
                stop(simpleError("Data is missing.\n\n"))
            }
            else {
                verify.qca(data)
            }
            yname <- as.character(parse(text = y))
            y <- gsub(minus, "-", gsub("[[:space:]]", "", y))
            negated[2] <- identical(unname(substring(y, 1, 2)), "1-")
            if (grepl("1-", y) & !negated[2]) {
                cat("\n")
                stop(simpleError(paste("Incorrect expression in \"", y, "\".\n\n", sep = "")))
            }
            y <- compute(y, data = data)
            negated[2] <- FALSE
        }
    }
    else {
        cat("\n")
        stop(simpleError("Either a dataframe with two columns or two vectors are needed.\n\n"))
    }
    if (any(x > 1) | any(y > 1)) {
        cat("\n")
        stop(simpleError("Values should be bound between 0 and 1.\n\n"))
    }
    xcopy <- x
    ycopy <- y
    jitfactor <- 0.01
    jitamount <- 0.01
    cexaxis <- 0.8
    hadj <- 1.1
    padj <- 0
    linex <- 1.75
    liney <- 2
    linet <- 1.5
    pch <- rep(21, length(x))
    cexpoints <- rep(0.8, length(x))
    bgpoints <- rep("#707070", length(x)) # "#ababab"
    if (length(testarg <- which(names(other.args) == "pch")) > 0) {
        pch <- other.args$pch
        if (length(pch) == 1) {
            pch <- rep(pch, length(x))
        }
        else {
            if (length(pch) != length(x)) {
                cat("\n")
                stop(simpleError(sprintf("Length of argument \"pch\" different from the %s.\n\n", ifelse(missing(data), "length of \"x\"", "number of rows in the data"))))
            }
        }
        other.args <- other.args[-testarg]
    }
    if (length(testarg <- which(names(other.args) == "cex")) > 0) {
        cexpoints <- other.args$cex
        if (length(cexpoints) == 1) {
            cexpoints <- rep(cexpoints, length(x))
        }
        else {
            if (length(cexpoints) != length(x)) {
                cat("\n")
                stop(simpleError(sprintf("Length of argument \"cex\" different from the %s.\n\n", ifelse(missing(data), "length of \"x\"", "number of rows in the data"))))
            }
        }
        other.args <- other.args[-testarg]
    }
    bginput <- is.element("bg", names(other.args))
    if (length(testarg <- which(names(other.args) == "bg")) > 0) {
        bgpoints <- other.args$bg
        if (length(bgpoints) == 1) {
            bgpoints <- rep(bgpoints, length(x))
        }
        else {
            if (length(bgpoints) != length(x)) {
                cat("\n")
                stop(simpleError(sprintf("Length of argument \"bg\" different from the %s.\n\n", ifelse(missing(data), "length of \"x\"", "number of rows in the data"))))
            }
        }
        other.args <- other.args[-testarg]
    }
    if (length(testarg <- which(names(other.args) == "factor")) > 0) {
        jitfactor <- other.args$factor
        other.args <- other.args[-testarg]
    }
    if (length(testarg <- which(names(other.args) == "amount")) > 0) {
        jitamount <- other.args$amount
        other.args <- other.args[-testarg]
    }
    if (length(testarg <- which(names(other.args) == "hadj")) > 0) {
        hadj <- other.args$hadj
        other.args <- other.args[-testarg]
    }
    if (length(testarg <- which(names(other.args) == "padj")) > 0) {
        padj <- other.args$padj
        other.args <- other.args[-testarg]
    }
    if (length(testarg <- which(names(other.args) == "line")) > 0) {
        linex <- other.args$line[1]
        liney <- ifelse(is.na(other.args$line[2]), other.args$line[1], other.args$line[2])
        linet <- ifelse(is.na(other.args$line[3]), other.args$line[1], other.args$line[3])
        other.args <- other.args[-testarg]
    }
    if (!is.null(clabels)) {
        if (is.numeric(clabels)) {
            if (length(clabels) < length(x)) {
                if (all(clabels <= length(x))) {
                    rownms <- rep("", length(x))
                    rownms[clabels] <- clabels
                    clabels <- rownms
                }
                else {
                    cat("\n")
                    stop(simpleError("Values in the argument \"clabels\" outside the rows of the data.\n\n"))
                }
            }
            clabels <- as.character(clabels)
        }
        if (length(clabels) != length(x)) {
            cat("\n")
            stop(simpleError(sprintf("Length of argument \"clabels\" larger than %s.\n\n", ifelse(missing(data), "length of \"x\"", "number of rows in the data"))))
        }
        if (is.logical(clabels)) {
            if (missing(data)) {
                rownms <- seq(length(x))
            }
            else {
                rownms <- rownames(data)
            }
            rownms[!clabels] <- ""
            clabels <- rownms
        }
    }
    cexlabels <- cexpoints
    if (enhance) {
        if (is.null(clabels)) {
            caselabels <- rep("", length(x))
        }
        if (relation == "sufficiency") {
            if (any(selection <- x >= 0.5 & y >= 0.5 & x <= y)) {
                if (is.null(clabels) & !model) {
                    if (missing(data)) {
                        caselabels[selection] <- which(selection)
                    }
                    else {
                        caselabels[selection] <- rownames(data)[selection]
                    }
                }
                xs <- x[selection]
                ys <- y[selection]
                pch[which(selection)][which.min((ys - xs)/xs)] <- 3
            }
            if (any(selection <- x >= 0.5 & y >= 0.5 & x > y)) {
                if (is.null(clabels) & !model) {
                    if (missing(data)) {
                        caselabels[selection] <- which(selection)
                    }
                    else {
                        caselabels[selection] <- rownames(data)[selection]
                    }
                }
                if (!bginput) {
                    bgpoints[selection] <- "#cccccc"
                }
            }
            if (any(selection <- x >= 0.5 & y < 0.5)) {
                xs <- x[selection]
                ys <- y[selection]
                pch[selection] <- 23
                if (!bginput) {
                    bgpoints[which(selection)][which.min(1 - (ys - xs)/xs)] <- "#cccccc"
                }
            }
            if (any(selection <- x < 0.5 & y < 0.5)) {
                if (is.null(clabels) & model) {
                    caselabels[selection] <- rownames(data)[selection]
                }
                cexpoints[selection] <- 0.875 * cexpoints[selection]
                pch[selection] <- 24
                if (!bginput) {
                    bgpoints[selection] <- "#cccccc"
                }
            }
            if (any(selection <- x < 0.5 & y >= 0.5)) {
                if (is.null(clabels) & model) {
                    caselabels[selection] <- rownames(data)[selection]
                }
                pch[selection] <- 22
                if (!bginput) {
                    bgpoints[selection] <- "#cccccc"
                }
            }
        }
        if (is.null(clabels)) {
            clabels <- caselabels
        }
    }
    if (jitter) {
        x <- jitter(x, jitfactor, jitamount)
        y <- jitter(y, jitfactor, jitamount)
    }
    toplot <- list(x = x, y = y)
    xlabel <- paste0(ifelse(negated[1], "~", ""), xname)
    ylabel <- paste0(ifelse(negated[2], "~", ""), yname)
    if (model) xlabel <- "MODEL"
    if (length(testarg <- which(names(other.args) == "xlab")) > 0) {
        xlabel <- other.args$xlab
        other.args <- other.args[-testarg]
    }
    if (length(testarg <- which(names(other.args) == "ylab")) > 0) {
        ylabel <- other.args$ylab
        other.args <- other.args[-testarg]
    }
    if (length(testarg <- which(names(other.args) == "cex.axis")) > 0) {
        cexaxis <- other.args$cex.axis
        other.args <- other.args[-testarg]
    }
    toplot$type <- "n"
    toplot$xlim <- c(0, 1)
    toplot$ylim <- c(0, 1)
    toplot$xlab <- ""
    toplot$ylab <- ""
    toplot$axes <- FALSE
    if (length(other.args) > 0) {
        toplot <- c(toplot, other.args)
    }
    par(mar = c(3, 3.1, 2.5, 0.5), cex.axis = cexaxis, tck = -.015,
        las = 1, xpd = FALSE, mgp = c(1.5, 0.5, 0))
    suppressWarnings(do.call("plot", toplot))
    box()
    axis(1, xaxp = c(0, 1, 10), padj = padj)
    axis(2, yaxp = c(0, 1, 10), hadj = hadj)
    title(xlab = xlabel, cex.lab = cexaxis + 0.1, font.lab = 2, line = linex)
	
    title(ylab = ylabel, cex.lab = cexaxis + 0.1, font.lab = 2, line = liney)
	title(main = paste(ifelse(nec(relation), "Necessity", "Sufficiency"), "relation"),
          cex.main = cexaxis/0.8, font.main = 2, line = linet)
    if (mguides) {
        abline(v = .5, lty = 2, col = "gray")
        abline(h = .5, lty = 2, col = "gray")
    }
    abline(0, 1, col = "gray")
    plotpoints <- list(x, y, pch = pch, cex = cexpoints, bg = bgpoints) 
    suppressWarnings(do.call("points", c(plotpoints, other.args)))
    inclcov <- round(pof(setms = xcopy, outcome = ycopy, relation = relation)$incl.cov[1, 1:3], 3)
        inclcov[is.na(inclcov)] <- 0
    inclcov <- sprintf("%.3f", inclcov)
    mtext(paste(c("Inclusion:", "Coverage:", ifelse(nec(relation), "Relevance:", "PRI:")),
                inclcov[c(1, 3, 2)], collapse = "   "), at = 0, adj = 0, cex = cexaxis)
    cexl <- ifelse(any(names(other.args) == "cex"), other.args$cex, 1)
    srtl <- ifelse(any(names(other.args) == "srt"), other.args$srt, 0)
    if (!is.null(clabels)) {
        text(x, y + 0.02, labels = clabels, srt = srtl, cex = cexlabels*cexl)
    }
}

Try the QCA package in your browser

Any scripts or data that you put into this service are public.

QCA documentation built on May 2, 2019, 4:47 p.m.