|
@@ -1,10 +1,261 @@
|
1
|
1
|
library(shiny)
|
|
2
|
+library(ggplot2)
|
|
3
|
+library(scales)
|
|
4
|
+library(tidyverse)
|
|
5
|
+library(shinycssloaders)
|
2
|
6
|
|
3
|
|
-ui <- fluidPage(
|
|
7
|
+# UI ----
|
|
8
|
+ui <- navbarPage("Gender Reassignment Surgery", collapsible = TRUE,
|
|
9
|
+ selected = "About",
|
|
10
|
+ # Vaginoplasty UI ----
|
|
11
|
+ tabPanel("Vaginoplasty — \"MtF\"",
|
|
12
|
+ sidebarPanel(width=3,
|
|
13
|
+ checkboxInput("vagprojc", label="Project waiting list out to:",
|
|
14
|
+ value=FALSE),
|
|
15
|
+ sliderInput("vagprojyears", label=NULL, min=2030, max=2080,
|
|
16
|
+ step=1, value=2030, sep=""),
|
|
17
|
+ numericInput("vagin", "Additions to list per year", min=0,
|
|
18
|
+ max=NA, step=0.1, value=0),
|
|
19
|
+ numericInput("vagout", "Funded vaginoplasties per year",
|
|
20
|
+ min=0.1, max=NA, step=0.1, value=1.5),
|
|
21
|
+ h4("Options"),
|
|
22
|
+ checkboxInput("vagwly", "Show waiting list length in years"),
|
|
23
|
+ checkboxInput("vagminc", "Set graph minimum", value=TRUE),
|
|
24
|
+ conditionalPanel("input.vagminc",
|
|
25
|
+ numericInput("vagmin", label=NULL, value=0)),
|
|
26
|
+ checkboxInput("vagmaxc", "Set graph maximum", value=FALSE),
|
|
27
|
+ conditionalPanel("input.vagmaxc",
|
|
28
|
+ numericInput("vagmax", label=NULL, value=NA))
|
|
29
|
+ ),
|
|
30
|
+ mainPanel(
|
|
31
|
+ tabsetPanel(
|
|
32
|
+ tabPanel("Graph",
|
|
33
|
+ h3("New Zealand vaginoplasty waiting list"),
|
|
34
|
+ plotOutput("vagPlot") %>% withSpinner(type=5)
|
|
35
|
+ ),
|
|
36
|
+ tabPanel("Table",
|
|
37
|
+ h3("Table of projected waiting list values"),
|
|
38
|
+ tableOutput("vagpTable") %>% withSpinner(type=1)
|
|
39
|
+ )
|
|
40
|
+ )
|
|
41
|
+ )
|
|
42
|
+ ),
|
|
43
|
+ # Phalloplasty UI ----
|
|
44
|
+ tabPanel("Phalloplasty — \"FtM\"",
|
|
45
|
+ sidebarPanel(width=3,
|
|
46
|
+ checkboxInput("phalprojc", label="Project waiting list out to:",
|
|
47
|
+ value=FALSE),
|
|
48
|
+ sliderInput("phalprojyears", label=NULL, min=2030, max=2080,
|
|
49
|
+ step=1, value=2030, sep=""),
|
|
50
|
+ numericInput("phalin", "Additions to list per year", min=0,
|
|
51
|
+ max=NA, step=0.1, value=0),
|
|
52
|
+ numericInput("phalout", "Funded phalloplasties per year",
|
|
53
|
+ min=0.1, max=NA, step=0.1, value=0.5),
|
|
54
|
+ h4("Options"),
|
|
55
|
+ checkboxInput("phalwly", "Show waiting list length in years"),
|
|
56
|
+ checkboxInput("phalminc", "Set graph minimum", value=TRUE),
|
|
57
|
+ conditionalPanel("input.phalminc",
|
|
58
|
+ numericInput("phalmin", label=NULL, value=0)),
|
|
59
|
+ checkboxInput("phalmaxc", "Set graph maximum", value=FALSE),
|
|
60
|
+ conditionalPanel("input.phalmaxc",
|
|
61
|
+ numericInput("phalmax", label=NULL, value=NA))
|
|
62
|
+ ),
|
|
63
|
+ mainPanel(
|
|
64
|
+ tabsetPanel(
|
|
65
|
+ tabPanel("Graph",
|
|
66
|
+ h3("New Zealand phalloplasty waiting list"),
|
|
67
|
+ plotOutput("phalPlot") %>% withSpinner(type=5)
|
|
68
|
+ ),
|
|
69
|
+ tabPanel("Table",
|
|
70
|
+ h3("Table of projected waiting list values"),
|
|
71
|
+ tableOutput("phalpTable") %>% withSpinner(type=1)
|
|
72
|
+ )
|
|
73
|
+ )
|
|
74
|
+ )
|
|
75
|
+ ),
|
|
76
|
+ # About Page ----
|
|
77
|
+ tabPanel("About",
|
|
78
|
+ p("Since a review in 2003 the New Zealand Ministry of Health",
|
|
79
|
+ "has nominally funded \"MtF\" GRS (i.e. trans women's vaginoplasty) at",
|
|
80
|
+ "a rate of three every two years, and \"FtM\" GRS (trans men's phalloplasty)",
|
|
81
|
+ "at one every two years. The retirement of Peter Walker, who was performing",
|
|
82
|
+ "vaginoplasty for the MoH, in 2014—coupled with the apparent increase",
|
|
83
|
+ "in number of publically out transgender people—has led to astronomical rises to",
|
|
84
|
+ "the waiting lists for both types of surgery."),
|
|
85
|
+ p("Several people have obatained details about the waiting lists from",
|
|
86
|
+ "the Ministry via Official Information Act requests since 2015.",
|
|
87
|
+ "The data obtained is summarised at the bottom of this page;",
|
|
88
|
+ "if you have newer datapoints or others",
|
|
89
|
+ "that have been missed please send them to me on",
|
|
90
|
+ a(href="https://twitter.com/OleumPetra", "Twitter"), "or",
|
|
91
|
+ a(href="mailto:oleumpetra+blog@gmail.com", "via email"), "."),
|
|
92
|
+ p("This app shows that data visually, but it also projects it",
|
|
93
|
+ "into the future, allowing the rate of people being added",
|
|
94
|
+ "to the list to be changed by the user, along with the",
|
|
95
|
+ "level of government funding. The inadequacy of the present",
|
|
96
|
+ "funding levels can be easily seen, and the size of the increase",
|
|
97
|
+ "that is required estimated."),
|
|
98
|
+ p("Note however that, as this crisis has been ongoing for some",
|
|
99
|
+ "time, many people interested in surgery will not have taken",
|
|
100
|
+ "the time and expense to formally add themselves to the",
|
|
101
|
+ "waiting list. Therefore the true size of the problem",
|
|
102
|
+ "cannot be easily estimated. In addition the Ministry",
|
|
103
|
+ "has contacted a number of people on the list and found that,",
|
|
104
|
+ "given the sheer length of time that has elapsed already,",
|
|
105
|
+ "some do not wish to continue with surgery or have found",
|
|
106
|
+ "some way to self-fund, shortening",
|
|
107
|
+ "the list without actually funding a surgery."),
|
|
108
|
+ hr(),
|
|
109
|
+ h4("Known vaginoplasty waiting list lengths"),
|
|
110
|
+ tableOutput("mtfData") %>% withSpinner(type=1, proxy.height = "100px"),
|
|
111
|
+ h4("Known phalloplasty waiting list lengths"),
|
|
112
|
+ tableOutput("ftmData") %>% withSpinner(type=1, proxy.height = "100px")
|
|
113
|
+ )
|
4
|
114
|
|
5
|
115
|
)
|
6
|
116
|
|
|
117
|
+# Server ----
|
7
|
118
|
server <- function(input, output) {
|
|
119
|
+ # Data logic ----
|
|
120
|
+ pData <- reactive({
|
|
121
|
+ read.csv("P.csv", header=T, stringsAsFactors = FALSE) %>%
|
|
122
|
+ mutate(Date = as.Date(Date))
|
|
123
|
+ })
|
|
124
|
+
|
|
125
|
+ vData <- reactive({
|
|
126
|
+ read.csv("V.csv", header=T, stringsAsFactors = FALSE) %>%
|
|
127
|
+ mutate(Date = as.Date(Date))
|
|
128
|
+ })
|
|
129
|
+
|
|
130
|
+ output$mtfData <- renderTable({
|
|
131
|
+ vData() %>% mutate(Date = format(Date, "%d/%m/%Y"),
|
|
132
|
+ `Length (years)` = Length / 1.5) %>%
|
|
133
|
+ select("Date", "Length", "Length (years)", "Source")
|
|
134
|
+ })
|
|
135
|
+
|
|
136
|
+ output$ftmData <- renderTable({
|
|
137
|
+ pData() %>% mutate(Date = format(Date, "%d/%m/%Y"),
|
|
138
|
+ `Length (years)` = Length / 0.5) %>%
|
|
139
|
+ select("Date", "Length", "Length (years)", "Source")
|
|
140
|
+ })
|
|
141
|
+
|
|
142
|
+ # Projection logic ----
|
|
143
|
+ vagProj <- reactive({
|
|
144
|
+ vd <- vData()
|
|
145
|
+ ld <- vd$Date[nrow(vd)]
|
|
146
|
+ ld.y <- as.numeric(format(ld, "%Y")) # year of last data value
|
|
147
|
+ ld.d <- format(ld, "-%m-%d") # date part of last data value
|
|
148
|
+ lv <- vd$Length[nrow(vd)] # last length value
|
|
149
|
+ yrs <- (ld.y + 1):(input$vagprojyears) # Vector of projection years
|
|
150
|
+ ny <- length(yrs) # Number of projection years
|
|
151
|
+ pc <- rep(input$vagin - input$vagout, ny)
|
|
152
|
+ pv <- cumsum(pc) + lv
|
|
153
|
+ pv <- ifelse(pv < 0, 0, pv)
|
|
154
|
+ dates <- as.Date(paste0(yrs, ld.d))
|
|
155
|
+ data.frame(Date=c(ld, dates), Length = c(lv, pv))
|
|
156
|
+ })
|
|
157
|
+
|
|
158
|
+ phalProj <- reactive({
|
|
159
|
+ pd <- pData()
|
|
160
|
+ ld <- pd$Date[nrow(pd)]
|
|
161
|
+ ld.y <- as.numeric(format(ld, "%Y")) # year of last data value
|
|
162
|
+ ld.d <- format(ld, "-%m-%d") # date part of last data value
|
|
163
|
+ lv <- pd$Length[nrow(pd)] # last length value
|
|
164
|
+ yrs <- (ld.y + 1):(input$phalprojyears) # Vector of projection years
|
|
165
|
+ ny <- length(yrs) # Number of projection years
|
|
166
|
+ pc <- rep(input$phalin - input$phalout, ny)
|
|
167
|
+ pv <- cumsum(pc) + lv
|
|
168
|
+ pv <- ifelse(pv < 0, 0, pv)
|
|
169
|
+ dates <- as.Date(paste0(yrs, ld.d))
|
|
170
|
+ data.frame(Date=c(ld, dates), Length = c(lv, pv))
|
|
171
|
+ })
|
|
172
|
+
|
|
173
|
+ # V plot logic ----
|
|
174
|
+ output$vagPlot <- renderPlot({
|
|
175
|
+ vd <- vData()
|
|
176
|
+ showyearsleft <- input$vagwly
|
|
177
|
+ if (showyearsleft) {
|
|
178
|
+ vd <- mutate(vd, Length = Length / 1.5)
|
|
179
|
+ }
|
|
180
|
+ ggplot(vd, aes(Date, Length)) -> v.p
|
|
181
|
+ v.p + geom_line(size = 1.5, na.rm = TRUE) -> v.p
|
|
182
|
+ if (input$vagprojc) {
|
|
183
|
+ vpr <- vagProj()
|
|
184
|
+ if (showyearsleft) {
|
|
185
|
+ vpr <- mutate(vpr, Length = Length / input$vagout)
|
|
186
|
+ }
|
|
187
|
+ v.p + geom_line(data=vpr, aes(Date, Length),
|
|
188
|
+ size=1.5, linetype = "dashed",
|
|
189
|
+ na.rm=TRUE) -> v.p
|
|
190
|
+ }
|
|
191
|
+ if (showyearsleft) {
|
|
192
|
+ v.p + ylab("Waiting list length (years)") -> v.p
|
|
193
|
+ } else {
|
|
194
|
+ v.p + ylab("Waiting list length (people)") -> v.p
|
|
195
|
+ }
|
|
196
|
+
|
|
197
|
+ v.p + theme_linedraw() -> v.p
|
|
198
|
+ glim <- c(
|
|
199
|
+ ifelse(input$vagminc, input$vagmin, NA),
|
|
200
|
+ ifelse(input$vagmaxc, input$vagmax, NA)
|
|
201
|
+ )
|
|
202
|
+ v.p + scale_y_continuous(limits=glim) -> v.p
|
|
203
|
+ v.p
|
|
204
|
+ })
|
|
205
|
+
|
|
206
|
+ # P plot logic ----
|
|
207
|
+ output$phalPlot <- renderPlot({
|
|
208
|
+ pd <- pData()
|
|
209
|
+ showyearsleft <- input$phalwly
|
|
210
|
+ if (showyearsleft) {
|
|
211
|
+ pd <- mutate(pd, Length = Length / 0.5)
|
|
212
|
+ }
|
|
213
|
+ ggplot(pd, aes(Date, Length)) -> p.p
|
|
214
|
+ p.p + geom_line(size = 1.5, na.rm=TRUE) -> p.p
|
|
215
|
+ if (input$phalprojc) {
|
|
216
|
+ ppr <- phalProj()
|
|
217
|
+ if (showyearsleft) {
|
|
218
|
+ ppr <- mutate(ppr, Length = Length / input$phalout)
|
|
219
|
+ }
|
|
220
|
+ p.p + geom_line(data=ppr, aes(Date, Length),
|
|
221
|
+ size=1.5, linetype = "dashed",
|
|
222
|
+ na.rm=TRUE) -> p.p
|
|
223
|
+ }
|
|
224
|
+ if (showyearsleft) {
|
|
225
|
+ p.p + ylab("Waiting list length (years)") -> p.p
|
|
226
|
+ } else {
|
|
227
|
+ p.p + ylab("Waiting list length (people)") -> p.p
|
|
228
|
+ }
|
|
229
|
+
|
|
230
|
+ p.p + theme_linedraw() -> p.p
|
|
231
|
+ glim <- c(
|
|
232
|
+ ifelse(input$phalminc, input$phalmin, NA),
|
|
233
|
+ ifelse(input$phalmaxc, input$phalmax, NA)
|
|
234
|
+ )
|
|
235
|
+ p.p + scale_y_continuous(limits=glim) -> p.p
|
|
236
|
+ p.p
|
|
237
|
+ })
|
|
238
|
+
|
|
239
|
+ # Projection table logic ----
|
|
240
|
+ output$phalpTable <- renderTable({
|
|
241
|
+ pp <- phalProj() %>% mutate(Date = format(Date, "%Y"))
|
|
242
|
+ names(pp) <- c("Year", "Length")
|
|
243
|
+ if (input$phalwly) {
|
|
244
|
+ pp <- mutate(pp, Length = Length / input$phalout)
|
|
245
|
+ names(pp) <- c("Year", "Length (years)")
|
|
246
|
+ }
|
|
247
|
+ pp
|
|
248
|
+ })
|
|
249
|
+
|
|
250
|
+ output$vagpTable <- renderTable({
|
|
251
|
+ vp <- vagProj() %>% mutate(Date = format(Date, "%Y"))
|
|
252
|
+ names(vp) <- c("Year", "Length")
|
|
253
|
+ if (input$vagwly) {
|
|
254
|
+ vp <- mutate(vp, Length = Length / input$vagout)
|
|
255
|
+ names(vp) <- c("Year", "Length (years)")
|
|
256
|
+ }
|
|
257
|
+ vp
|
|
258
|
+ })
|
8
|
259
|
}
|
9
|
260
|
|
10
|
261
|
shinyApp(ui = ui, server = server)
|