3
3
# ' \code{position_fill} additionally standardises each stack to have unit
4
4
# ' height.
5
5
# '
6
+ # ' @details \code{position_fill} and \code{position_stack} automatically stacks
7
+ # ' values so their order follows the decreasing sort order of the fill
8
+ # ' aesthetic. This makes sure that the stack order is aligned with the order in
9
+ # ' the legend, as long as the scale order has not been changed using the
10
+ # ' \code{breaks} argument. This also means that in order to change stacking
11
+ # ' order while preserving parity with the legend order it is necessary to
12
+ # ' reorder the factor levels of the fill aesthetic (see examples)
13
+ # '
14
+ # ' Stacking of positive and negative values are performed separately so that
15
+ # ' positive values stack upwards from the x-axis and negative values stack
16
+ # ' downward. Do note that parity with legend order cannot be ensured when
17
+ # ' positive and negative values are mixed.
18
+ # '
6
19
# ' @family position adjustments
7
20
# ' @seealso See \code{\link{geom_bar}}, and \code{\link{geom_area}} for
8
21
# ' more examples.
41
54
# '
42
55
# ' # But realise that this makes it *much* harder to compare individual
43
56
# ' # trends
57
+ # '
58
+ # ' # Stacking order can be changed using ordered factors
59
+ # ' data.set$Type <- factor(data.set$Type, levels = c('c', 'b', 'd', 'a'))
60
+ # ' ggplot(data.set, aes(Time, Value)) + geom_area(aes(fill = Type))
61
+ # '
62
+ # ' # while changing the scale order won't affect the stacking
63
+ # ' ggplot(data.set, aes(Time, Value)) + geom_area(aes(fill = Type)) +
64
+ # ' scale_fill_discrete(breaks = c('a', 'b', 'c', 'd'))
65
+ # '
66
+ # ' # Negative values can be stacked as well
67
+ # ' neg <- data.set$Type %in% c('a', 'd')
68
+ # ' data.set$Value[neg] <- data.set$Value[neg] * -1
69
+ # ' ggplot(data.set, aes(Time, Value)) + geom_area(aes(fill = Type))
70
+ # '
44
71
position_stack <- function () {
45
72
PositionStack
46
73
}
@@ -61,14 +88,31 @@ PositionStack <- ggproto("PositionStack", Position,
61
88
" Maybe you want position = 'identity'?" )
62
89
return (data )
63
90
}
64
-
65
- if (! is.null(data $ ymin ) && ! all(data $ ymin == 0 ))
66
- warning(" Stacking not well defined when ymin != 0" , call. = FALSE )
91
+ if (! is.null(data $ ymax ) && ! is.null(data $ ymin )) {
92
+ switch_index <- data $ ymax < data $ ymin
93
+ data $ ymin [switch_index ] <- data $ ymax [switch_index ]
94
+ data $ ymax [switch_index ] <- 0
95
+ }
96
+ if (! is.null(data $ ymin ) && ! all((data $ ymin == 0 & data $ ymax > = 0 ) | data $ ymax == 0 & data $ ymin < = 0 ))
97
+ warning(" Stacking not well defined when ymin and ymax is on opposite sides of 0" , call. = FALSE )
67
98
68
99
data
69
100
},
70
101
71
102
compute_panel = function (data , params , scales ) {
72
- collide(data , NULL , " position_stack" , pos_stack )
103
+ negative <- if (! is.null(data $ ymin )) data $ ymin < 0 else rep(FALSE , nrow(data ))
104
+ neg <- data [which(negative ), ]
105
+ pos <- data [which(! negative ), ]
106
+ if (any(negative )) {
107
+ # Negate group so sorting order is consistent across the x-axis.
108
+ # Undo negation afterwards so it doesn't mess up the rest
109
+ neg $ group <- - neg $ group
110
+ neg <- collide(neg , NULL , " position_stack" , pos_stack )
111
+ neg $ group <- - neg $ group
112
+ }
113
+ if (any(! negative )) {
114
+ pos <- collide(pos , NULL , " position_stack" , pos_stack )
115
+ }
116
+ rbind(pos , neg )
73
117
}
74
118
)
0 commit comments