|
| 1 | +#ds.glmSummary |
| 2 | +#' @title summarize a glm object on the serverside |
| 3 | +#' @description summarize a glm object on the serverside to create a |
| 4 | +#' summary_glm object. Also identify and return components of |
| 5 | +#' both the glm object and the summary_glm object |
| 6 | +#' that can safely be sent to the clientside without a risk of disclosure |
| 7 | +#' @details Clientside function calling a single assign function (glmSummaryDS.as) |
| 8 | +#' and a single aggregate function (glmSummaryDS.as). ds.glmSummary summarises a |
| 9 | +#' glm object that has already been created on the serverside by fitting ds.glmSLMA |
| 10 | +#' which is precisely the same as the glm object created by fitting a glm |
| 11 | +#' using the glm function in native R. Similarly the summary_glm object |
| 12 | +#' saved to the serverside is precisely equivalent to the object created using |
| 13 | +#' summary(glm object) in R. The glm object produced from a standard |
| 14 | +#' call to glm in R has 32 components. Amongst these, all of the following |
| 15 | +#' thirteen contain information about every records in the data set and so are |
| 16 | +#' disclosive. They are all therefore set to NA and so convey |
| 17 | +#' no information when returned to the clientside: |
| 18 | +#' 1.residuals, 2.fitted.values, 3.effects, 4.R, 5.qr, 6.linear.predictors, |
| 19 | +#' 7.weights, 8.prior.weights, 9.y, 10.model, 11. na.action, 12.x, 13. offset. |
| 20 | +#' In addition the list element "data" which identifies a data.frame |
| 21 | +#' that was identified as containing all of the variables required |
| 22 | +#' for the model is also disclosive because it doesn't list |
| 23 | +#' the name of the data.frame but rather prints it out in full. However, |
| 24 | +#' a user can benefit from knowing what source of data were used in creating |
| 25 | +#' the glm model and so the element "data" that is returned to the clientside |
| 26 | +#' simply lists the names of all of the columns in the originating data.frame. |
| 27 | +#' Having removed all disclosive elements of the glm object, ds.glmSummary |
| 28 | +#' returns the remaining 19 elements to the clientside. The object |
| 29 | +#' created from a standard call to summary(glm object) in R contains 18 |
| 30 | +#' list elements. Only two of these are disclosive - na.action and |
| 31 | +#' deviance.resid and these are therefore set to NA before ds.glmSummary |
| 32 | +#' returns the other 16 to the clientside. Further details of the components |
| 33 | +#' of the glm object and summary_glm object can be found under help for |
| 34 | +#' glm and summary(glm) in native R. In addition, the elements that ARE returned |
| 35 | +#' are listed under "return" below. |
| 36 | +#' @param x.name a character string providing the name of a glm object on the |
| 37 | +#' serverside that has previously been created e.g. using ds.glmSLMA |
| 38 | +#' @param newobj a character string specifying the name of the object to which |
| 39 | +#' the summary_glm object representing the output of summary(glm object) |
| 40 | +#' in each study is to be written. If no <newobj> argument is specified, the output |
| 41 | +#' object on the serverside defaults to "summary_glm". |
| 42 | +#' @param datasources specifies the particular 'connection object(s)' to use. |
| 43 | +#' e.g. if you have several data sets in the sources you are working with |
| 44 | +#' called opals.a, opals.w2, and connection.xyz, you can choose which of |
| 45 | +#' these to work with. The call 'datashield.connections_find()' lists all of |
| 46 | +#' the different datasets available and if one of these is called 'default.connections' |
| 47 | +#' that will be the dataset used by default if no other dataset is specified. If you |
| 48 | +#' wish to change the connections you wish to use by default the call |
| 49 | +#' datashield.connections_default('opals.a') will set 'default.connections' |
| 50 | +#' to be 'opals.a' and so in the absence of specific instructions to the contrary |
| 51 | +#' (e.g. by specifiying a particular dataset to be used via the <datasources> |
| 52 | +#' argument) all subsequent function calls will be to the datasets held in opals.a. |
| 53 | +#' If the <datasources> argument is specified, it should be set without |
| 54 | +#' inverted commas: e.g. datasources=opals.a or datasources=default.connections. |
| 55 | +#' The <datasources> argument also allows you to apply a function solely to a subset |
| 56 | +#' of the studies/sources you are working with. For example, the second source |
| 57 | +#' in a set of three, can be specified using a call such as datasources=connection.xyz[2]. |
| 58 | +#' On the other hand, if you wish to specify solely the first and third sources, the |
| 59 | +#' appropriate call will be datasources=connections.xyz[c(1,3)] |
| 60 | +#' @return ds.glmSummary writes a new object to the serverside with name given by |
| 61 | +#' the newobj argument or if that argument is missing or null it is called "summary_glm". |
| 62 | +#' In addition, ds.glmSummary returns an object containing two lists to the clientside |
| 63 | +#' the two lists are named "glm.obj" and "glm.summary.obj" which contain all of the |
| 64 | +#' elements of the original glm object and the summary_glm object on the serverside |
| 65 | +#' but with all potentially disclosive components set to NA or masked in another way |
| 66 | +#' see "details" above. The elements that are returned with a non-NA value in |
| 67 | +#' the glm.obj list object are: "coefficients", "rank", "family", "deviance", "aic", |
| 68 | +#' "null.deviance", "iter", "df.residual", "df.null", "converged", "boundary", |
| 69 | +#' "call", "formula", "terms", "data", "control", "method", "contrasts", "xlevels". |
| 70 | +#' The elements that are returned with a non-NA value in |
| 71 | +#' the glm.summary.obj list object are: "call", "terms", "family", "deviance", |
| 72 | +#' "aic", "contrasts", "df.residual", "null.deviance", "df.null", "iter", |
| 73 | +#' "coefficients", "aliased", "dispersion", "df", "cov.unscaled", "cov.scaled". |
| 74 | +#' For further information see help for glm and summary(glm) in native R |
| 75 | +#' and for ds.glmSLMA in DataSHIELD. |
| 76 | +#' @author Paul Burton, for DataSHIELD Development Team 17/07/20 |
| 77 | +#' @export |
| 78 | + |
| 79 | +ds.glmSummary<-function(x.name, newobj=NULL, datasources=NULL) { |
| 80 | + |
| 81 | +# if no connections are specified look for connection objects in the environment |
| 82 | + if(is.null(datasources)){ |
| 83 | + datasources <- datashield.connections_find() |
| 84 | + } |
| 85 | + |
| 86 | +# check if a value has been provided for x |
| 87 | + if(is.null(x.name)||!is.character(x.name)){ |
| 88 | + return("Error: x.name must denote a character string naming the glm object on the |
| 89 | + serverside to be summarised") |
| 90 | + } |
| 91 | + |
| 92 | +# PREPARE AND CALL THE ASSIGN FUNCTION TO PREPARE THE summary_glm OBJECT ON THE SERVERSIDE |
| 93 | + |
| 94 | +#FORCE newobj TO BE CORRECT IN CASE USER TRIES TO SPECIFY A VALUE FOR IT |
| 95 | +#NAME OF ALL THREE newobj OBJECTS IS DEFINED FULLY BY x.name |
| 96 | + |
| 97 | + newobj<- "summary_glm" |
| 98 | + |
| 99 | + calltext1 <- call("glmSummaryDS.as", x.name) |
| 100 | + |
| 101 | + DSI::datashield.assign(datasources,newobj,calltext1) |
| 102 | + |
| 103 | +# LOOK BELOW CLIENTSIDE MODULE FOR NEXT BLOCK OF CODE |
| 104 | +# PREPARE AND CALL THE SECOND ASSIGN FUNCTION TO PREPARE AN ABBREVIATED |
| 105 | +# summary_glm OBJECT ON THE SERVERSIDE THAT CAN SAFELY BE RETURNED TO CLIENT |
| 106 | + |
| 107 | + |
| 108 | +############################################################################################################# |
| 109 | +#DataSHIELD CLIENTSIDE MODULE: CHECK KEY DATA OBJECTS SUCCESSFULLY CREATED # |
| 110 | + # |
| 111 | +#SET APPROPRIATE PARAMETERS FOR THIS PARTICULAR FUNCTION # |
| 112 | +test.obj.name<-newobj # |
| 113 | + # # |
| 114 | + # |
| 115 | +# CALL SEVERSIDE FUNCTION # |
| 116 | +calltext <- call("testObjExistsDS", test.obj.name) # |
| 117 | + # |
| 118 | +object.info<-DSI::datashield.aggregate(datasources, calltext) # |
| 119 | + # |
| 120 | +# CHECK IN EACH SOURCE WHETHER OBJECT NAME EXISTS # |
| 121 | +# AND WHETHER OBJECT PHYSICALLY EXISTS WITH A NON-NULL CLASS # |
| 122 | +num.datasources<-length(object.info) # |
| 123 | + # |
| 124 | + # |
| 125 | +obj.name.exists.in.all.sources<-TRUE # |
| 126 | +obj.non.null.in.all.sources<-TRUE # |
| 127 | + # |
| 128 | +for(j in 1:num.datasources){ # |
| 129 | + if(!object.info[[j]]$test.obj.exists){ # |
| 130 | + obj.name.exists.in.all.sources<-FALSE # |
| 131 | + } # |
| 132 | + if(object.info[[j]]$test.obj.class=="ABSENT"){ # |
| 133 | + obj.non.null.in.all.sources<-FALSE # |
| 134 | + } # |
| 135 | + } # |
| 136 | + # |
| 137 | +if(obj.name.exists.in.all.sources && obj.non.null.in.all.sources){ # |
| 138 | + # |
| 139 | + return.message<- # |
| 140 | + paste0("A data object <", test.obj.name, "> has been created in all specified data sources") # |
| 141 | + # |
| 142 | + # |
| 143 | + }else{ # |
| 144 | + # |
| 145 | + return.message.1<- # |
| 146 | + paste0("Error: A valid data object <", test.obj.name, "> does NOT exist in ALL specified data sources") # |
| 147 | + # |
| 148 | + return.message.2<- # |
| 149 | + paste0("It is either ABSENT and/or has no valid content/class,see return.info above") # |
| 150 | + # |
| 151 | + return.message.3<- # |
| 152 | + paste0("Please use ds.ls() to identify where missing") # |
| 153 | + # |
| 154 | + # |
| 155 | + return.message<-list(return.message.1,return.message.2,return.message.3) # |
| 156 | + # |
| 157 | + } # |
| 158 | + # |
| 159 | + calltext <- call("messageDS", test.obj.name) # |
| 160 | + studyside.message<-DSI::datashield.aggregate(datasources, calltext) # |
| 161 | + # |
| 162 | + no.errors<-TRUE # |
| 163 | + for(nd in 1:num.datasources){ # |
| 164 | + if(studyside.message[[nd]]!="ALL OK: there are no studysideMessage(s) on this datasource"){ # |
| 165 | + no.errors<-FALSE # |
| 166 | + } # |
| 167 | + } # |
| 168 | + # |
| 169 | + # |
| 170 | + if(no.errors){ # |
| 171 | + cat("\n\nCREATE ASSIGN OBJECT\n") # |
| 172 | + # |
| 173 | + validity.check<-paste0("<",test.obj.name, "> appears valid in all sources") # |
| 174 | + print(list(is.object.created=return.message,validity.check=validity.check)) # |
| 175 | + } # |
| 176 | + # |
| 177 | +if(!no.errors){ # |
| 178 | + validity.check<-paste0("<",test.obj.name,"> invalid in at least one source. See studyside.messages:") # |
| 179 | + print(list(is.object.created=return.message,validity.check=validity.check, # |
| 180 | + studyside.messages=studyside.message)) # |
| 181 | + } # |
| 182 | + # |
| 183 | +#END OF CHECK OBJECT CREATED CORECTLY MODULE # |
| 184 | +############################################################################################################# |
| 185 | + |
| 186 | + |
| 187 | +# PREPARE AND CALL THE SECOND SERVERSIDE (AGGREGATE) FUNCTION TO PREPARE AN ABBREVIATED |
| 188 | +# (disclosure controlled) summary_glm OBJECT TO BE BE RETURNED TO CLIENT |
| 189 | + |
| 190 | + |
| 191 | + calltext2 <- call("glmSummaryDS.ag", x.name) |
| 192 | + |
| 193 | + output.list<-DSI::datashield.aggregate(datasources,calltext2) |
| 194 | + |
| 195 | +return(output.list) |
| 196 | +} |
| 197 | +#ds.glmSummary |
| 198 | + |
0 commit comments