6
6
# ' @param which_layout adopt the layout of which plot? If the default value of
7
7
# ' "merge" is used, all plot level layout options will be included in the final
8
8
# ' layout. This argument also accepts a numeric vector which will restric
9
- # ' @param margin a numeric value between 0 and 1. Corrsepnds to the proportion
10
- # ' of plot width/height to attribute to margins between subplots.
9
+ # ' @param margin either a single value or four values (all between 0 and 1).
10
+ # ' If four values are provided, the first is used as the left margin, the second
11
+ # ' is used as the right margin, the third is used as the top margin, and the
12
+ # ' fourth is used as the bottom margin.
13
+ # ' If a single value is provided, it will be used as all four margins.
11
14
# ' @return A plotly object
12
15
# ' @export
13
16
# ' @author Carson Sievert
18
21
# ' }
19
22
20
23
21
- # # TODO: add warning if geo and non-geo coordinates are used!!!
22
- subplot <- function (... , nrows = 1 , which_layout = " merge" , margin = 0.1 / nrows ) {
24
+ # # TODO: throw warning if geo and non-geo coordinates are used!!!
25
+ subplot <- function (... , nrows = 1 , which_layout = " merge" , margin = 0 ) {
23
26
# note that dots is a _list of plotlys_
24
27
dots <- lapply(list (... ), plotly_build )
25
28
# put existing plot anchors and domain information into a tidy format
@@ -62,7 +65,6 @@ subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.1 / nrows
62
65
ctr <- ctr + 1
63
66
}
64
67
}
65
- # browser()
66
68
# put p_info into a data.frame()
67
69
p_info <- Reduce(rbind , p_info )
68
70
row.names(p_info ) <- NULL
@@ -77,25 +79,8 @@ subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.1 / nrows
77
79
# Only do domain computations if they are _completely_ missing
78
80
# (I don't think it makes sense to support partial specification of domains)
79
81
if (all(is.na(with(p_info , c(xstart , xend , ystart , yend ))))) {
80
- nplots <- max(p_info $ key )
81
- ncols <- ceiling(nplots / nrows )
82
- xdom <- get_domains(nplots , ncols , margin )
83
- ydom <- get_domains(nplots , nrows , margin )
84
- xdf <- cbind(
85
- list2df(xdom , c(" xstart" , " xend" )),
86
- key = seq_len(nplots )
87
- )
88
- ydf <- list2df(ydom , c(" ystart" , " yend" ))
89
- # get_domains() currently assumes plots are drawn from _lower_ left
90
- # corner to _upper_ right, but we need them going from _upper_ left
91
- # to _lower-right_
92
- ydf <- with(ydf , data.frame (ystart = 1 - yend , yend = 1 - ystart ))
93
- ydf <- ydf [order(ydf $ ystart , decreasing = TRUE ), ]
94
- ydf $ key <- seq_len(nplots )
95
- # overwrite relevant info
96
- p_info <- p_info [! grepl(" start$|end$" , names(p_info ))]
97
- p_info <- plyr :: join(p_info , xdf , by = " key" )
98
- p_info <- plyr :: join(p_info , ydf , by = " key" )
82
+ p_info [c(" xstart" , " xend" , " yend" , " ystart" )] <-
83
+ get_domains(max(p_info $ key ), nrows , margin )
99
84
}
100
85
# empty plot container that we'll fill up with new info
101
86
p <- list (
@@ -105,13 +90,17 @@ subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.1 / nrows
105
90
ls <- if (which_layout == " merge" ) {
106
91
lapply(dots , " [[" , " layout" )
107
92
} else {
108
- # TODO: warning if referencing non-exitant layouts?
93
+ if (! is.numeric(which_layout )) warning(" which_layout must be numeric" )
94
+ if (! all(idx <- which_layout %in% seq_along(dots ))) {
95
+ warning(" which_layout is referencing non-existant layouts" )
96
+ which_layout <- which_layout [idx ]
97
+ }
109
98
lapply(dots [which_layout ], " [[" , " layout" )
110
99
}
111
100
ls <- ls [! vapply(ls , is.null , logical (1 ))]
112
101
p [[" layout" ]] <- Reduce(modifyList , ls )
113
102
114
-
103
+ # tack on trace, domain, and anchor information
115
104
p_info $ plot <- as.numeric(p_info $ plot )
116
105
p_info $ trace <- as.numeric(p_info $ trace )
117
106
for (i in seq_along(p $ data )) {
@@ -136,11 +125,11 @@ subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.1 / nrows
136
125
# (but overwrite domain/anchor info)
137
126
l <- dots [[info $ plot ]]$ layout
138
127
p $ layout [[xaxis ]] <- modifyList(
139
- l [names(l ) %in% " xaxis" ][[ 1 ]] % || % list () ,
128
+ l [names(l ) %in% " xaxis" ],
140
129
list (domain = xdom , anchor = info $ yaxis )
141
130
)
142
131
p $ layout [[yaxis ]] <- modifyList(
143
- l [names(l ) %in% " yaxis" ][[ 1 ]] % || % list () ,
132
+ l [names(l ) %in% " yaxis" ],
144
133
list (domain = ydom , anchor = info $ xaxis )
145
134
)
146
135
p $ data [[i ]]$ xaxis <- info $ xaxis
@@ -151,23 +140,31 @@ subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.1 / nrows
151
140
}
152
141
153
142
154
- # margins should shrink as # of plots increase
155
- get_domains <- function (nplots = 1 , nsplits = 1 , mar = 0.1 / nsplits ,
156
- decreasing = FALSE ) {
157
- if (nsplits == 1 ) {
158
- lapply(vector(" list" , nplots ), function (x ) c(0 , 1 ))
159
- } else {
160
- domains <- vector(" list" , nsplits )
161
- for (i in seq_len(nsplits )) {
162
- l <- ((i - 1 ) / nsplits ) + ifelse(i == 1 , 0 , mar )
163
- u <- (i / nsplits ) - ifelse(i == nsplits , 0 , mar )
164
- domains [[i ]] <- c(l , u )
165
- }
166
- rep_len(domains , nplots )
143
+ get_domains <- function (nplots = 1 , nrows = 1 , margins = 0.01 ) {
144
+ if (length(margins ) == 1 ) margins <- rep(margins , 4 )
145
+ if (length(margins ) != 4 ) stop(" margins must be length 1 or 4" , call. = FALSE )
146
+ ncols <- ceiling(nplots / nrows )
147
+
148
+ xs <- vector(" list" , ncols )
149
+ for (i in seq_len(ncols )) {
150
+ xs [[i ]] <- c(
151
+ xstart = ((i - 1 ) / ncols ) + ifelse(i == 1 , 0 , margins [1 ]),
152
+ xend = (i / ncols ) - ifelse(i == ncols , 0 , margins [2 ])
153
+ )
167
154
}
155
+ xz <- rep_len(xs , nplots )
156
+
157
+ ys <- vector(" list" , nrows )
158
+ for (i in seq_len(nplots )) {
159
+ j <- ceiling(i / ncols )
160
+ ys [[i ]] <- c(
161
+ ystart = 1 - ((j - 1 ) / nrows ) - ifelse(j == 1 , 0 , margins [3 ]),
162
+ yend = 1 - (j / nrows ) + ifelse(j == nrows , 0 , margins [4 ])
163
+ )
164
+ }
165
+ list2df(Map(c , xz , ys ))
168
166
}
169
167
170
-
171
168
list2df <- function (x , nms ) {
172
169
stopifnot(length(unique(sapply(x , length ))) == 1 )
173
170
m <- Reduce(rbind , x )
0 commit comments