The R Project SVN R

Rev

Rev 87093 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
42338 ripley 1
#  File src/library/base/R/array.R
68948 ripley 2
#  Part of the R package, https://www.R-project.org
42338 ripley 3
#
89289 maechler 4
#  Copyright (C) 1995-2026 The R Core Team
60146 ripley 5
#
42338 ripley 6
#  This program is free software; you can redistribute it and/or modify
7
#  it under the terms of the GNU General Public License as published by
8
#  the Free Software Foundation; either version 2 of the License, or
9
#  (at your option) any later version.
10
#
11
#  This program is distributed in the hope that it will be useful,
12
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
#  GNU General Public License for more details.
15
#
16
#  A copy of the GNU General Public License is available at
68948 ripley 17
#  https://www.R-project.org/Licenses/
42338 ripley 18
 
22691 hornik 19
array <-
20
function(data = NA, dim = length(data), dimnames = NULL)
59623 ripley 21
{
59712 ripley 22
    ## allow for as.vector.factor (converts to character)
23
    if(is.atomic(data) && !is.object(data))
24
        return(.Internal(array(data, dim, dimnames)))
59705 ripley 25
    data <- as.vector(data)
26
    ## package rv has an as.vector() method which leave this as a classed list
59623 ripley 27
    if(is.object(data)) {
59708 ripley 28
        dim <- as.integer(dim)
75223 hornik 29
        if (!length(dim)) stop("'dim' cannot be of length 0")
59623 ripley 30
        vl <- prod(dim)
31
        if(length(data) != vl) {
32
            ## C code allows long vectors, but rep() does not.
33
            if(vl > .Machine$integer.max)
34
                stop("'dim' specifies too large an array")
62246 maechler 35
            data <- rep_len(data, vl)
59623 ripley 36
        }
37
        if(length(dim)) dim(data) <- dim
38
        if(is.list(dimnames) && length(dimnames)) dimnames(data) <- dimnames
39
        data
40
    } else .Internal(array(data, dim, dimnames))
41
}
22691 hornik 42
 
43
slice.index <-
77708 pd 44
function(x, MARGIN)
22691 hornik 45
{
77708 pd 46
    d <- dim(x)
22691 hornik 47
    if(is.null(d))
77708 pd 48
        d <- length(x)
22691 hornik 49
    n <- length(d)
50
 
77701 pd 51
    ## Extract the margins and associated dimnames
52
 
53
    if (is.character(MARGIN)) {
77708 pd 54
        dn <- dimnames(x)
77701 pd 55
        if(is.null(dnn <- names(dn))) # names(NULL) is NULL
77708 pd 56
           stop("'x' must have named dimnames")
77701 pd 57
        MARGIN <- match(MARGIN, dnn)
58
        if (anyNA(MARGIN))
59
            stop("not all elements of 'MARGIN' are names of dimensions")
60
    }
61
 
62
 
75097 hornik 63
    if(!length(MARGIN) || any(MARGIN < 1L) || any(MARGIN > n))
33423 ripley 64
        stop("incorrect value for 'MARGIN'")
22691 hornik 65
 
53820 ripley 66
    if(any(d == 0L)) return(array(integer(), d))
24092 ripley 67
 
75097 hornik 68
    m <- MARGIN[1L]
69
    y <- rep.int(rep.int(1L:d[m],
70
                         prod(d[seq_len(m - 1L)]) *
71
                         rep.int(1L, d[m])),
72
                 prod(d[seq.int(from = m + 1L,
73
                                length.out = n - m)]))
74
    if(length(MARGIN) > 1L) {
75
        p <- d[m]
76
        for(m in MARGIN[-1L]) {
77
            y <- y + p * rep.int(rep.int(seq.int(0L, d[m] - 1L),
78
                                         prod(d[seq_len(m - 1L)]) *
79
                                         rep.int(1L, d[m])),
80
                                 prod(d[seq.int(from = m + 1L,
81
                                                length.out = n - m)]))
82
            p <- p * d[m]
83
        }
84
    }
85
 
22691 hornik 86
    dim(y) <- d
87
    y
88
}
59216 maechler 89
 
75097 hornik 90
provideDimnames <-
89289 maechler 91
function(x, sep = "", base = list(. = LETTERS), unique = TRUE,
92
         use.names = FALSE)
63968 ripley 93
{
89289 maechler 94
    ## provide dimnames and optionally names(dimnames) where missing,
95
    ## not copying 'x' unnecessarily
59216 maechler 96
    dx <- dim(x)
97
    dnx <- dimnames(x)
98
    if(new <- is.null(dnx))
99
	dnx <- vector("list", length(dx))
68295 maechler 100
    k <- length(M <- lengths(base))
59216 maechler 101
    for(i in which(vapply(dnx, is.null, NA))) {
102
	ii <- 1L+(i-1L) %% k # recycling
63968 ripley 103
        ss <- seq_len(dx[i]) - 1L # dim could be zero
69795 maechler 104
	bi <- base[[ii]][1L+ (ss %% M[ii])]
105
	dnx[[i]] <- if(unique) make.unique(bi, sep = sep) else bi
59216 maechler 106
	new <- TRUE
107
    }
89289 maechler 108
    if(use.names && !is.null(nbase <- names(base)) && # no names() or *some* non-"" names:
109
       (is.null(ndnx <- names(dnx)) || length(i <- which(!nzchar(ndnx))))) {
110
        nbi <- if(is.null(ndnx)) rep_len(nbase, length(dx))
111
               else nbase[1L + (i-1L) %% k]
112
        if(unique)
113
            nbi <- make.unique(nbi, sep = sep)
114
        names(dnx) <- if(is.null(ndnx)) nbi
115
                      else `[<-`(ndnx, i, nbi)
116
        new <- TRUE
117
    }
59216 maechler 118
    if(new) dimnames(x) <- dnx
119
    x
120
}
75186 hornik 121
 
82437 maechler 122
## The array split part used by apply() { ./apply.R }
77708 pd 123
## (With 'X' replaced by 'x').
75186 hornik 124
 
125
asplit <-
87087 hornik 126
function(x, MARGIN, drop = FALSE)
82437 maechler 127
{
77708 pd 128
    ## Ensure that x is an array object
129
    dl <- length(dim(x))
82452 ripley 130
    if(!dl) stop("dim(x) must have a positive length")
77708 pd 131
    if(is.object(x))
132
        x <- if(dl == 2L) as.matrix(x) else as.array(x)
75186 hornik 133
    ## now record dim as coercion can change it
134
    ## (e.g. when a data frame contains a matrix).
77708 pd 135
    d <- dim(x)
136
    dn <- dimnames(x)
75186 hornik 137
    ds <- seq_len(dl)
82437 maechler 138
 
75186 hornik 139
    ## Extract the margins and associated dimnames
140
 
141
    if (is.character(MARGIN)) {
142
        if(is.null(dnn <- names(dn))) # names(NULL) is NULL
82452 ripley 143
           stop("'x' must have named dimnames")
75186 hornik 144
        MARGIN <- match(MARGIN, dnn)
145
        if (anyNA(MARGIN))
146
            stop("not all elements of 'MARGIN' are names of dimensions")
147
    }
82437 maechler 148
    d.call <- d[-MARGIN]
149
    d.ans  <- d[ MARGIN]
150
    if (anyNA(d.call) || anyNA(d.ans))
151
        stop("'MARGIN' does not match dim(X)")
75186 hornik 152
    s.call <- ds[-MARGIN]
82437 maechler 153
    s.ans  <- ds[ MARGIN]
75186 hornik 154
    dn.call <- dn[-MARGIN]
82437 maechler 155
    dn.ans  <- dn[ MARGIN]
156
    dimnames(x) <- NULL
75186 hornik 157
 
87093 hornik 158
    .Internal(asplit(aperm(x, c(s.call, s.ans)),
159
                     d.ans, d.call, dn.ans, dn.call,
160
                     prod(d.call), prod(d.ans),
161
                     as.logical(drop)))
75186 hornik 162
}
84178 deepayan 163
 
164
## Convert to data frame, mainly for list arrays produced by tapply()
165
 
166
array2DF <-
167
function (x, responseName = "Value", sep = "",
168
          base = list(LETTERS),
84254 deepayan 169
          simplify = TRUE, allowLong = TRUE)
84178 deepayan 170
{
171
    .df_helper <- function(x) # for data frames
172
    {
89289 maechler 173
        ## check whether all components of list array 'x'
84178 deepayan 174
        ## - are data frames
175
        ## - have same column names
89289 maechler 176
        ## If TRUE, return value is a vector of corresponding nrow()-s
84178 deepayan 177
        ## If FALSE, return value is integer(0)
178
        if (!is.list(x)) return(integer(0))
179
        if (!all(vapply(x, inherits, TRUE, "data.frame"))) return(integer(0))
180
        if (length(unique(vapply(x, ncol, 1L))) > 1L) return(integer(0))
181
        if (length(unique(lapply(x, colnames))) > 1L) return(integer(0))
182
        return(vapply(x, nrow, 1L))
183
    }
184
    .unvec_helper <- function(x) # for unnamed vectors
185
    {
89289 maechler 186
        ## check whether all components of list array 'x'
84178 deepayan 187
        ## - are atomic vectors
188
        ## - have no names
89289 maechler 189
        ## If TRUE, return value is a vector of corresponding nrow()-s
84178 deepayan 190
        ## If FALSE, return value is integer(0)
191
        if (!is.list(x)) return(integer(0))
192
        if (!all(vapply(x, is.atomic, TRUE))) return(integer(0))
193
        if (!all(vapply(x, function(v) is.null(names(v)), TRUE))) return(integer(0))
194
        return(vapply(x, length, 1L))
195
    }
196
    keys <-
197
        do.call(expand.grid,
198
                c(dimnames(provideDimnames(x, sep = sep, base = base)),
199
                  KEEP.OUT.ATTRS = FALSE, stringsAsFactors = FALSE))
200
    vals <- NULL
201
    if(simplify) {
202
        ## handle data frames with identical colnames
203
        dfrows <- .df_helper(x)
204
        if (length(dfrows))
205
            return(cbind(keys[ rep(seq_along(dfrows), dfrows), , drop = FALSE],
206
                         do.call(rbind, x)))
207
        ## handle unnamed vectors
208
        if (allowLong) {
209
            unvecrows <- .unvec_helper(x)
210
            if (length(unvecrows))
211
                return(cbind(keys[ rep(seq_along(unvecrows), unvecrows), , drop = FALSE],
212
                             structure(data.frame(V = do.call(c, x)),
213
                                       names = responseName)))
214
        }
215
        ## handle generic case
216
        x <- simplify2array(c(x))
217
        if(is.array(x)) {
218
            vals <- asplit(x, 1L)
219
            if(is.null(names(vals)))
220
                names(vals) <-
221
                    paste0(responseName, sep, seq_along(vals))
222
        }
223
    }
224
    if(is.null(vals)) {
225
        vals <- list(c(x))
226
        names(vals) <- responseName
227
    }
228
    cbind(keys, list2DF(vals))
229
}