-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathwrite_gct.R
More file actions
568 lines (551 loc) · 22.6 KB
/
Copy pathwrite_gct.R
File metadata and controls
568 lines (551 loc) · 22.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
write_gct <- function(ds, ofile, precision=4, appenddim=TRUE, ver=3) {
if (!methods::is(ds, "GCT")) {
stop("ds must be a GCT object")
}
# make sure it's valid
methods::validObject(ds)
# extract the components
m <- mat(ds)
rdesc <- meta(ds)
cdesc <- meta(ds, dimension="column")
rid <- ids(ds)
cid <- ids(ds, dimension="column")
# append the dimensions of the data set, if desired
if (appenddim) ofile <- append_dim(ofile, m, extension="gct")
precision <- floor(precision)
cat("Saving file to ", ofile, "\n")
nr <- nrow(m)
nc <- ncol(m)
cat(sprintf("Dimensions of matrix: [%dx%d]\n", nr, nc))
cat(sprintf("Setting precision to %d\n", precision))
# open file and write
if (ver == 3) {
# remove the 'id' columns
cdesc$id <- NULL
rdesc$id <- NULL
# get the counts of meta data fields
nrdesc <- ncol(rdesc)
ncdesc <- ncol(cdesc)
colkeys <- names(cdesc)
# append header
cat(sprintf("#1.%d\n%d\t%d\t%d\t%d", ver, nr, nc, nrdesc, ncdesc),
file=ofile,sep='\n')
# line 3: sample row desc keys and sample names
cat(paste(c("id", names(rdesc), cid), collapse="\t"),
file=ofile, sep="\n", append=TRUE)
# line 4 + ncdesc: sample desc
filler <- 'na'
if (ncdesc > 0) {
for (ii in seq_len(ncdesc)) {
if (is.numeric(cdesc[, ii])) {
cat(paste(c(colkeys[ii], rep(filler, nrdesc),
round(cdesc[, ii], precision)),
collapse="\t"),
file=ofile, sep="\n", append=TRUE)
} else {
cat(paste(c(colkeys[ii], rep(filler, nrdesc),
cdesc[, ii]),
collapse="\t"),
file=ofile, sep="\n", append=TRUE)
}
}
}
for (ii in seq_len(nr)) {
# print rows
cat(paste(c(rid[ii],
rdesc[ii, ],
round(m[ii, ], precision)), collapse="\t"),
sep="\n", file=ofile, append=TRUE)
}
} else {
# assume ver 1.2 and below, ignore descriptors
# append header
cat(sprintf("#1.%d\n%d\t%d", ver, nr, nc),
file=ofile, sep="\n")
# line 3: sample row desc keys and sample names
cat(paste(c("id", "Description", cid), collapse="\t"),
file=ofile, sep="\n", append=TRUE)
for (ii in seq_len(nr)) {
# print rows
cat(paste(c(rid[ii],
rdesc[ii, 2],
round(m[ii, ], precision)), collapse="\t"),
sep="\n", file=ofile, append=TRUE)
}
}
cat("Saved.\n")
}
########################################
### GCT class and method definitions ###
########################################
#' An S4 class to represent a GCT object
#'
#' @slot mat a numeric matrix
#' @slot rid a character vector of row ids
#' @slot cid a character vector of column ids
#' @slot rdesc a \code{data.frame} of row descriptors
#' @slot rdesc a \code{data.frame} of column descriptors
#' @slot src a character indicating the source (usually file path) of the data
#'
#' @description The GCT class serves to represent annotated
#' matrices. The \code{mat} slot contains said data and the
#' \code{rdesc} and \code{cdesc} slots contain data frames with
#' annotations about the rows and columns, respectively
#'
#' @seealso \code{\link{parse_gctx}},
#' \code{\link{write_gctx}}, \code{\link{read_gctx_meta}},
#' \code{\link{read_gctx_ids}}
#' @seealso visit \url{http://clue.io/help} for more information on the
#' GCT format
methods::setClass("GCT",
methods::representation(
mat = "matrix",
rid = "character",
cid = "character",
rdesc = "data.frame",
cdesc = "data.frame",
version = "character",
src = "character"
)
)
## ----set up methods for checking GCT validity----
methods::setValidity("GCT",
function(object) {
# check whether dimensions of various
# slots are in sync
m <- mat(object)
rid <- ids(object)
cid <- ids(object, dim="column")
rdesc <- meta(object)
cdesc <- meta(object, dim="column")
nrows <- nrow(m)
ncols <- ncol(m)
if (nrows != length(rid)) {
return(
"rid must be the same length as number of matrix rows")
}
if (ncols != length(cid)) {
return(
"cid must be the same length as number of matrix columns")
}
if (any(duplicated(cid))) {
return("cid must be unique")
}
if (any(duplicated(rid))) {
return("rid must be unique")
}
if (nrow(cdesc) != ncols & nrow(cdesc) != 0) {
return(paste(
"cdesc must either have 0 rows or the",
"same number of rows as matrix has columns"))
}
if (nrow(rdesc) != nrows & nrow(rdesc) != 0) {
return(paste(
"rdesc must either have 0 rows or the same number",
"of rows as matrix has rows"))
}
else {
return(T)
}
}
)
## ----define the initialization method for the GCT class----
methods::setMethod("initialize",
signature = "GCT",
definition = function(.Object, mat=NULL, rdesc=NULL,
cdesc=NULL,
src=NULL, rid=NULL, cid=NULL,
matrix_only=FALSE) {
# if we were supplied a matrix and annotations, use them
if (!is.null(mat)) {
.Object@mat <- mat
# if given rid and cid, use those as well
if (!is.null(rid)) {
.Object@rid <- rid
} else {
.Object@rid <- rownames(mat)
}
if (!is.null(cid)) {
.Object@cid <- cid
} else {
.Object@cid <- colnames(mat)
}
if (!is.null(rdesc)) {
.Object@rdesc <- rdesc
}
if (!is.null(cdesc)) {
.Object@cdesc <- cdesc
}
# make sure rid, cid and dimnames of mat in sync
dimnames(.Object@mat) <- list(.Object@rid, .Object@cid)
} else if (!is.null(src)) {
# we were not given a matrix, were we given a src file?
# check to make sure it's either .gct or .gctx
if (! (grepl(".gct$", src) || grepl(".gctx$", src) ))
stop("Either a .gct or .gctx file must be given")
if (grepl(".gct$", src)) {
if ( ! is.null(rid) || !is.null(cid) )
warning(
"rid and cid values may only be given for",
".gctx files, ignoring")
# parse the .gct
.Object@src <- src
# get the .gct version by reading first line
.Object@version <- scan(src, what="", nlines=1,
sep="\t", quiet=TRUE)[1]
# get matrix dimensions by reading second line
dimensions <- scan(src, what=double(0), nlines=1,
skip=1, sep="\t", quiet=TRUE)
nrmat <- dimensions[1]
ncmat <- dimensions[2]
if (length(dimensions) == 4) {
# a #1.3 file
message("parsing as GCT v1.3")
nrhd <- dimensions[3]
nchd <- dimensions[4]
} else {
# a #1.2 file
message("parsing as GCT v1.2")
nrhd <- 0
nchd <- 0
}
message(paste(
src, nrmat, "rows,", ncmat, "cols,", nrhd,
"row descriptors,", nchd, "col descriptors"))
# read in header line
header <- scan(src, what="", nlines=1, skip=2,
sep="\t", quote=NULL, quiet=TRUE)
# construct row header and column id's from the
# header line
if ( nrhd > 0 ) {
rhd <- header[2:(nrhd+1)]
cid <- header[-(nrhd+1):-1]
col_offset <- 1
}
else {
if (any(grepl("description", header,
ignore.case=T))) {
# check for presence of description column
# in v1.2 files
col_offset <- 2
} else {
col_offset <- 1
}
rhd <- NULL
cid <- header[(1+col_offset):length(header)]
}
# read in the next set of headers (column annotations)
# and shape into a matrix
if ( nchd > 0 ) {
header <- scan(src, what="", nlines=nchd,
skip=3,
sep="\t", quote=NULL, quiet=TRUE)
header <- matrix(header, nrow=nchd,
ncol=ncmat + nrhd + 1, byrow=TRUE)
# extract the column header and column descriptions
chd <- header[,1]
cdesc <- header[,-(nrhd+1):-1]
# need to transpose in the case where there's
# only one column annotation
if ( nchd == 1 )
cdesc <- t(cdesc)
}
else {
chd = NULL
cdesc <- data.frame(id=cid)
}
# read in the data matrix and row descriptions
# shape into a matrix
m <- scan(src, what="", nlines=nrmat,
skip=3 + nchd, sep="\t", quote=NULL,
quiet=TRUE)
m <- matrix(m, nrow=nrmat,
ncol=ncmat + nrhd + col_offset,
byrow=TRUE)
# message(paste(dim(mat), collapse="\t"))
# Extract the row id's row descriptions,
# and the data matrix
rid <- m[,1]
if ( nrhd > 0 ) {
# need as.matrix for the case where there's
# only one row annotation
rdesc <- as.matrix(m[,2:(nrhd + 1)])
m <- matrix(as.numeric(m[,-(nrhd + 1):-1]),
nrow=nrmat, ncol=ncmat)
}
else {
rdesc <- data.frame(id=rid)
m <- matrix(as.numeric(
m[, (1+col_offset):ncol(m)]),
nrow=nrmat, ncol=ncmat)
}
# assign names to the data matrix and the
# row and column descriptions
dimnames(m) <- list(rid, cid)
if ( nrhd > 0 ) {
dimnames(rdesc) <- list(rid, rhd)
rdesc <- as.data.frame(rdesc,
stringsAsFactors=FALSE)
}
if ( nchd > 0 ) {
cdesc <- t(cdesc)
dimnames(cdesc) <- list(cid, chd)
cdesc <- as.data.frame(cdesc,
stringsAsFactors=FALSE)
}
# assign to the GCT slots
.Object@mat <- m
.Object@rid <- rownames(m)
.Object@cid <- colnames(m)
if (!matrix_only) {
# return annotations as well as matrix
.Object@rdesc <- fix_datatypes(rdesc)
.Object@cdesc <- fix_datatypes(cdesc)
# add id columns to rdesc and cdesc
.Object@rdesc$id <- rownames(.Object@rdesc)
.Object@cdesc$id <- rownames(.Object@cdesc)
}
}
else {
# parse the .gctx
message("reading ", src)
.Object@src <- src
# if the rid's or column id's are .grp files,
# read them in
if ( length(rid) == 1 && grepl(".grp$", rid) )
rid <- parse_grp(rid)
if ( length(cid) == 1 && grepl(".grp$", cid) )
cid <- parse_grp(cid)
# get all the row and column ids
all_rid <- read_gctx_ids(src, dim="row")
all_cid <- read_gctx_ids(src, dim="col")
# if rid or cid specified, read only those rows/columns
# if already numeric, use as is
# else convert to numeric indices
processed_rids <- process_ids(rid, all_rid, type="rid")
processed_cids <- process_ids(cid, all_cid, type="cid")
# read the data matrix
.Object@mat <-
rhdf5::h5read(
src, name="0/DATA/0/matrix",
index=list(processed_rids$idx, processed_cids$idx))
# set the row and column ids, casting as characters
.Object@rid <- processed_rids$ids
.Object@cid <- processed_cids$ids
rownames(.Object@mat) <- processed_rids$ids
colnames(.Object@mat) <- processed_cids$ids
# get the meta data
if (!matrix_only) {
.Object@rdesc <- read_gctx_meta(
src, dim="row",
ids=processed_rids$ids)
.Object@cdesc <- read_gctx_meta(
src, dim="col",
ids=processed_cids$ids)
}
else {
.Object@rdesc <- data.frame(id=.Object@rid,
stringsAsFactors=FALSE)
.Object@cdesc <- data.frame(id=.Object@cid,
stringsAsFactors=FALSE)
}
# close any open handles and return the object
if(utils::packageVersion('rhdf5') < "2.23.0") {
rhdf5::H5close()
} else {
rhdf5::h5closeAll()
}
message("done")
}
}
# finally, make sure object is valid before returning
ok <- methods::validObject(.Object)
return(.Object)
}
)
#' Initialize an object of class \code{GCT}
#' @param mat a matrix
#' @param rdesc a \code{data.frame} of row metadata
#' @param cdesc a \code{data.frame} of column metadata
#' @param src path to a GCT file to read
#' @param rid vector of character identifiers for rows
#' @param cid vector of character identifiers for columns
#' @param matrix_only logical indicating whether to read just the matrix
#' data from \code{src}
#'
#' @details
#' If \code{mat} is provided, \code{rid} and \code{cid} are treated as
#' the row and column identifiers for the matrix and are assigned to the
#' \code{rid} and \code{cid} slots of the \code{GCT} object.
#'
#' If \code{mat} is not provided but \code{src} is provided,
#' \code{rid} and \code{cid} are treated as filters. Data will be read from
#' the file path provided to \code{src} and will then be restricted to the
#' character ids or integer indices provided to \code{rid} and \code{cid}.
#' In a similar manner, \code{matrix_only} controls whether the
#' row and column metadata are also read from the \code{src} file path.
#'
#' @returns a \code{GCT} object
#' @examples
#' # an empty object
#' (g <- GCT())
#' # with a matrix
#' # note we must specify row and column ids
#' (g <- GCT(mat=matrix(rnorm(100), nrow=10),
#' rid=letters[1:10], cid=letters[1:10]))
#' # from file
#' gct_file <- system.file("extdata", "modzs_n25x50.gctx", package="cmapR")
#' (g <- GCT(src=gct_file))
#' @family GCTX parsing functions
#' @importFrom methods new
#' @export
GCT <- function(mat=NULL, rdesc=NULL, cdesc=NULL,
src=NULL, rid=NULL, cid=NULL,
matrix_only=FALSE) {
methods::new("GCT", mat=mat, rdesc=rdesc, cdesc=cdesc,
src=src, rid=rid, cid=cid, matrix_only=matrix_only)
}
###########################################
### accessor functions for GCT objects ###
###########################################
# set method for displaying a GCT object
# just use the 'str' function to show its structure
setMethod("show", methods::signature("GCT"), function(object) {
utils::str(object)
})
#' Extract or set the matrix of GCT object
#' @param g the GCT object
#' @param value a numeric matrix
#' @return a matrix
#' @examples
#' # get the matrix
#' m <- mat(ds)
#' # set the matrix
#' mat(ds) <- matrix(0, nrow=nrow(m), ncol=ncol(m))
#' @family GCT accessor methods
#' @export
methods::setGeneric("mat", function(g) {
standardGeneric("mat")
})
#' @rdname mat
methods::setMethod("mat", "GCT", function(g) g@mat)
#' @export
#' @rdname mat
methods::setGeneric("mat<-", function(g, value) {
standardGeneric("mat<-")
})
#' @rdname mat
methods::setMethod("mat<-", "GCT", function(g, value) {
g@mat <- value
methods::validObject(g)
return(g)
})
#' Extract the or set row or column ids of a GCT object
#' @param g the GCT object
#' @param dimension the dimension to extract/update ['row' or 'column']
#' @param value a character vector
#' @return a vector of row ids
#' @examples
#' # extract rids
#' rids <- ids(ds)
#' # extract column ids
#' cids <- ids(ds, "column")
#' # set rids
#' ids(ds) <- as.character(1:length(rids))
#' # set cids
#' ids(ds, "column") <- as.character(1:length(cids))
#' @family GCT accessor methods
#' @export
methods::setGeneric("ids", function(g, dimension="row") {
standardGeneric("ids")
})
#' @rdname ids
methods::setMethod("ids", "GCT", function(g, dimension="row") {
dimension <- tolower(dimension)
if (dimension == "col") dimension <- "column"
stopifnot(dimension %in% c("row", "column"))
switch(dimension, row=g@rid, column=g@cid)
})
#' @export
#' @rdname ids
methods::setGeneric("ids<-", function(g, dimension="row", value) {
standardGeneric("ids<-")
})
#' @rdname ids
methods::setMethod("ids<-", "GCT", function(g, dimension="row", value) {
dimension <- tolower(dimension)
if (dimension == "col") dimension <- "column"
stopifnot(dimension %in% c("row", "column"))
if (dimension == "row") {
g@rid <- value
} else {
g@cid <- value
}
methods::validObject(g)
return(g)
})
#' Extract the or set metadata of a GCT object
#' @param g the GCT object
#' @param dimension the dimension to extract/update ['row' or 'column']
#' @param value a data.frame
#' @return a data.frame
#' @examples
#' # extract rdesc
#' rdesc <- meta(ds)
#' # extract cdesc
#' cdesc <- meta(ds, dim="column")
#' # set rdesc
#' meta(ds) <- data.frame(x=sample(letters, nrow(rdesc), replace=TRUE))
#' # set cdesc
#' meta(ds, dim="column") <- data.frame(x=sample(letters, nrow(cdesc),
#' replace=TRUE))
#' @family GCT accessor methods
#' @export
methods::setGeneric("meta", function(g, dimension="row") {
standardGeneric("meta")
})
#' @rdname meta
methods::setMethod("meta", "GCT", function(g, dimension="row") {
dimension <- tolower(dimension)
if (dimension == "col") dimension <- "column"
stopifnot(dimension %in% c("row", "column"))
switch(dimension, row=g@rdesc, column=g@cdesc)
})
#' @export
#' @rdname meta
methods::setGeneric("meta<-", function(g, dimension="row", value) {
standardGeneric("meta<-")
})
#' @rdname meta
methods::setMethod("meta<-", "GCT", function(g, dimension="row", value) {
dimension <- tolower(dimension)
if (dimension == "col") dimension <- "column"
stopifnot(dimension %in% c("row", "column"))
if (dimension == "row") {
g@rdesc <- value
} else {
g@cdesc <- value
}
methods::validObject(g)
return(g)
})
###########################################
### cast GCT as SummarizedExperiment ###
###########################################
#' as("GCT", "SummarizedExperiment")
#'
#' Create SummarizedExperiment object from GCT object.
#'
#' @examples
#'
#' se <- as(ds, "SummarizedExperiment")
#'
#' @importFrom SummarizedExperiment SummarizedExperiment
#' @importFrom methods validObject
setAs("GCT", "SummarizedExperiment", function(from) {
stopifnot(methods::validObject(from))
SummarizedExperiment::SummarizedExperiment(
assays = list(exprs = mat(from)),
colData = meta(from, dimension="column"),
rowData = meta(from))
})