| 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 |
}
|