NZ GRS waiting list shiny app https://shiny.petras.space/GRS
rstats
statistics
rshiny
new-zealand
grs
transgender

app.R 11KB

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