|
| 1 | +#' |
| 2 | +#' @title Calculates the kurtosis of a numeric variable |
| 3 | +#' @description This function calculates the kurtosis of a numeric variable. |
| 4 | +#' @details The function calculates the kurtosis of an input variable x with three different methods. |
| 5 | +#' The method is specified by the argument \code{method}. If x contains any missings, the function removes those before |
| 6 | +#' the calculation of the kurtosis. If \code{method} is set to 1 the following formula is used |
| 7 | +#' \eqn{ kurtosis= \frac{\sum_{i=1}^{N} (x_i - \bar(x))^4 /N}{(\sum_{i=1}^{N} ((x_i - \bar(x))^2) /N)^(2) } - 3}, |
| 8 | +#' where \eqn{ \bar{x} } is the mean of x and \eqn{N} is the number of observations. If \code{method} is set to 2 |
| 9 | +#' the following formula is used \eqn{ kurtosis= ((N+1)*(\frac{\sum_{i=1}^{N} (x_i - \bar(x))^4 /N}{(\sum_{i=1}^{N} ((x_i - \bar(x))^2) /N)^(2) } - 3) + 6)*((N-1)/((N-2)*(N-3)))}. |
| 10 | +#' If \code{method} is set to 3 the following formula is used \eqn{ kurtosis= (\frac{\sum_{i=1}^{N} (x_i - \bar(x))^4 /N}{(\sum_{i=1}^{N} ((x_i - \bar(x))^2) /N)^(2) })*(1-1/N)^2 - 3}. |
| 11 | +#' This function is similar to the function \code{kurtosis} in R package \code{e1071}. |
| 12 | +#' @param x a string character, the name of a numeric variable. |
| 13 | +#' @param method an integer between 1 and 3 selecting one of the algorithms for computing kurtosis |
| 14 | +#' detailed below. The default value is set to 1. |
| 15 | +#' @param type a character which represents the type of analysis to carry out. |
| 16 | +#' If \code{type} is set to 'combine', 'combined', 'combines' or 'c', the global kurtosis is returned |
| 17 | +#' if \code{type} is set to 'split', 'splits' or 's', the kurtosis is returned separately for each study. |
| 18 | +#' if \code{type} is set to 'both' or 'b', both sets of outputs are produced. |
| 19 | +#' The default value is set to 'both'. |
| 20 | +#' @param datasources a list of \code{\link{DSConnection-class}} objects obtained after login. |
| 21 | +#' If the \code{datasources} argument is not specified |
| 22 | +#' the default set of connections will be used: see \code{\link{datashield.connections_default}}. |
| 23 | +#' @return a matrix showing the kurtosis of the input numeric variable, the number of valid observations and |
| 24 | +#' the validity message. |
| 25 | +#' @author Demetris Avraam, for DataSHIELD Development Team |
| 26 | +#' @export |
| 27 | +#' |
| 28 | +ds.kurtosis <- function(x=NULL, method=1, type='both', datasources=NULL){ |
| 29 | + |
| 30 | + # if no opal login details are provided look for 'opal' objects in the environment |
| 31 | + if(is.null(datasources)){ |
| 32 | + datasources <- datashield.connections_find() |
| 33 | + } |
| 34 | + |
| 35 | + if(is.null(x)){ |
| 36 | + stop("Please provide the name of the input vector!", call.=FALSE) |
| 37 | + } |
| 38 | + |
| 39 | + if(!(method %in% c(1,2,3))){ |
| 40 | + stop("method must be an integer between 1 and 3", call.=FALSE) |
| 41 | + } |
| 42 | + |
| 43 | + # enable valid aliases for "type" argument |
| 44 | + if(type == 'combine' | type == 'combined' | type == 'combines' | type == 'c') type <- 'combine' |
| 45 | + if(type == 'split' | type == 'splits' | type == 's') type <- 'split' |
| 46 | + if(type == 'both' | type == 'b' ) type <- 'both' |
| 47 | + |
| 48 | + # the input variable might be given as column table (i.e. D$x) |
| 49 | + # or just as a vector not attached to a table (i.e. x) |
| 50 | + # we have to make sure the function deals with each case |
| 51 | + xnames <- extract(x) |
| 52 | + varname <- xnames$elements |
| 53 | + obj2lookfor <- xnames$holders |
| 54 | + |
| 55 | + # check if the input object(s) is(are) defined in all the studies |
| 56 | + if(is.na(obj2lookfor)){ |
| 57 | + defined <- isDefined(datasources, varname) |
| 58 | + }else{ |
| 59 | + defined <- isDefined(datasources, obj2lookfor) |
| 60 | + } |
| 61 | + |
| 62 | + # call the internal function that checks the input object is of the same class in all studies. |
| 63 | + typ <- checkClass(datasources, x) |
| 64 | + |
| 65 | + # the input object must be a numeric or an integer vector |
| 66 | + if(typ != 'integer' & typ != 'numeric'){ |
| 67 | + message(paste0(x, " is of type ", typ, "!")) |
| 68 | + stop("The input object must be an integer or numeric vector.", call.=FALSE) |
| 69 | + } |
| 70 | + |
| 71 | + if (type=='split' | type=='both'){ |
| 72 | + calltext.split <- call("kurtosisDS1", x, method) |
| 73 | + output.split <- DSI::datashield.aggregate(datasources, calltext.split) |
| 74 | + mat.split <- matrix(as.numeric(matrix(unlist(output.split), nrow=length(datasources), byrow=TRUE)[,1:2]),nrow=length(datasources)) |
| 75 | + validity <- matrix(unlist(output.split), nrow=length(datasources), byrow=TRUE)[,3] |
| 76 | + mat.split <- data.frame(cbind(mat.split, validity)) |
| 77 | + rownames(mat.split) <- names(output.split) |
| 78 | + colnames(mat.split) <- c('Kurtosis', 'Nvalid', 'ValidityMessage') |
| 79 | + } |
| 80 | + |
| 81 | + if (type=='combine' | type=='both'){ |
| 82 | + global.mean <- ds.mean(x, type='combine')$Global.Mean[,'EstimatedMean'] |
| 83 | + #global.var <- ds.var(x, type='combine')$Global.Variance[,'EstimatedVar'] |
| 84 | + |
| 85 | + if (is.na(global.mean)){ |
| 86 | + stop("FAILED: The number of valid observations in one or more studies is less than nfilter.tab. \n Check that by using the argument type=='split'", call.=FALSE) |
| 87 | + }else{ |
| 88 | + calltext.combined <- call("kurtosisDS2", x, global.mean) |
| 89 | + output.combined <- DSI::datashield.aggregate(datasources, calltext.combined) |
| 90 | + |
| 91 | + Global.sum.quartics <- 0 |
| 92 | + Global.sum.squares <- 0 |
| 93 | + Global.Nvalid <- 0 |
| 94 | + for (s in 1:length(datasources)){ |
| 95 | + Global.sum.quartics <- Global.sum.quartics + output.combined[[s]]$Sum.quartics |
| 96 | + Global.sum.squares <- Global.sum.squares + output.combined[[s]]$Sum.squares |
| 97 | + Global.Nvalid <- Global.Nvalid + output.combined[[s]]$Nvalid |
| 98 | + } |
| 99 | + |
| 100 | + g2.global <- ((Global.sum.quartics/Global.Nvalid)/(Global.sum.squares/Global.Nvalid)^(2))-3 |
| 101 | + |
| 102 | + if(method==1){ |
| 103 | + Global.kurtosis <- g2.global |
| 104 | + combinedMessage <- "VALID ANALYSIS" |
| 105 | + } |
| 106 | + if(method==2){ |
| 107 | + Global.kurtosis <- ((Global.Nvalid + 1) * g2.global + 6) * (Global.Nvalid - 1)/((Global.Nvalid - 2) * (Global.Nvalid - 3)) |
| 108 | + combinedMessage <- "VALID ANALYSIS" |
| 109 | + } |
| 110 | + if(method==3){ |
| 111 | + Global.kurtosis <- (g2.global + 3) * (1 - 1/Global.Nvalid)^2 - 3 |
| 112 | + combinedMessage <- "VALID ANALYSIS" |
| 113 | + } |
| 114 | + mat.combined <- data.frame(cbind(Global.kurtosis, Global.Nvalid, combinedMessage)) |
| 115 | + rownames(mat.combined) <- 'studiesCombined' |
| 116 | + colnames(mat.combined) <- c('Kurtosis', 'Nvalid', 'ValidityMessage') |
| 117 | + |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + if (type=='split'){ |
| 122 | + return(Kurtosis.by.Study=mat.split) |
| 123 | + } |
| 124 | + |
| 125 | + if (type=="combine") { |
| 126 | + return(Global.Kurtosis=mat.combined) |
| 127 | + } |
| 128 | + |
| 129 | + if (type=="both") { |
| 130 | + return(list(Kurtosis.by.Study=mat.split, Global.Kurtosis=mat.combined)) |
| 131 | + } |
| 132 | + |
| 133 | +} |
0 commit comments