Skip to content

Commit 5e967d5

Browse files
committedJul 19, 2021
Rewrite summary output, UI input edits
Rewrite summary output into a table, which now includes a fixed R squared, per minute rate, and segment boundaries. Edit the input form so that the logger selection is a dropdown and the minseglen input is a numeric
1 parent f4ec4ca commit 5e967d5

File tree

3 files changed

+99
-36
lines changed

3 files changed

+99
-36
lines changed
 

‎server.R

+75-24
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ library(magrittr)
22
library(shiny)
33
library(dbplyr)
44

5+
#' Reads a ulog file and outputs a tibble
6+
#'
7+
#' @param path Filepath to the ulog file
8+
#'
9+
#' @return A tibble
10+
#' @export
11+
#'
12+
#' @examples
513
ulog_to_df = function(path){
614
con = DBI::dbConnect(RSQLite::SQLite(), dbname = path)
715
dplyr::inner_join(
@@ -17,8 +25,17 @@ ulog_to_df = function(path){
1725
dplyr::relocate(id, time, conc)
1826
}
1927

20-
segment = function(df, minseglen){
21-
cpt_model = EnvCpt::envcpt(df$conc, models="trendcpt", minseglen=minseglen)$trendcpt
28+
#' Segments a respirometry data frame
29+
#'
30+
#' @param df Containing a "conc" column, which contains the concentration
31+
#' @param ... Extra parameters to pass into `envcpt`
32+
#'
33+
#' @return
34+
#' @export
35+
#'
36+
#' @examples
37+
segment = function(df, ...){
38+
cpt_model = EnvCpt::envcpt(df$conc, models="trendcpt", ...)$trendcpt
2239
purrr::pmap_dfr(list(
2340
seq_along(cpt_model@cpts),
2441
dplyr::lag(cpt_model@cpts, default=0),
@@ -42,7 +59,17 @@ vline = function(x = 0, ...) {
4259
)
4360
}
4461

45-
make_plot = function(df, segments, selected_segment){
62+
#' Make the interactive plotly plot for showing the entire time series of a single logger run
63+
#'
64+
#' @param df The data frame for a single logger run, containing `time`, and `conc` columns
65+
#' @param segments A data frame containing segments, containing `time`, and `fitted` columns
66+
#' @param selected_segment A data frame containing one or more selected rows. This has the same format as, and should be a subset of the `segments` argument.
67+
#'
68+
#' @return The plotly object
69+
#' @export
70+
#'
71+
#' @examples
72+
make_macro_plot = function(df, segments, selected_segment){
4673
plotly::plot_ly() %>%
4774
plotly::add_trace(name="Data", data=df, x = ~time, y = ~conc, type = 'scatter', mode = 'markers') %>%
4875
plotly::add_trace(name="Fit", data=segments, x=~time, y=~fitted, type = 'scatter', mode = 'lines', line = list(color = 'orange', width = 2)) %>%
@@ -57,28 +84,41 @@ make_plot = function(df, segments, selected_segment){
5784
}
5885

5986
# Define server logic required to draw a histogram
60-
shiny::shinyServer(function(input, output) {
87+
shiny::shinyServer(function(input, output, session) {
6188

62-
df = reactive({
89+
all_loggers = reactive({
6390
if (input$files %>% length == 0){ return(NULL) }
6491
input$files$datapath %>%
6592
dplyr::first() %>%
66-
ulog_to_df() %>%
67-
dplyr::filter(logger==paste("Logger", input$logger))
93+
ulog_to_df()
6894
})
6995

70-
segments = reactive({
96+
observe({
7197
if (input$files %>% length == 0){ return(NULL) }
98+
updateSelectInput(session = session, inputId = 'logger', choices=all_loggers() %>% dplyr::pull(logger))
99+
})
100+
101+
ready_to_plot = reactive({
102+
input$files %>% length > 0 && input$logger != ""
103+
})
104+
105+
df = reactive({
106+
if (!ready_to_plot()){ return(NULL) }
107+
all_loggers() %>% dplyr::filter(logger == input$logger)
108+
})
109+
110+
segments = reactive({
111+
if (!ready_to_plot()){ return(NULL) }
72112
segment(df(), input$minseglen)
73113
})
74114

75115
output$segments = plotly::renderPlotly({
76-
if (input$files %>% length == 0){ return(NULL) }
77-
make_plot(df(), segments(), selected_segment()) %>% plotly::event_register("plotly_click")
116+
if (!ready_to_plot()){ return(NULL) }
117+
make_macro_plot(df(), segments(), selected_segment()) %>% plotly::event_register("plotly_click")
78118
})
79119

80120
selected_point = reactive({
81-
if (input$files %>% length == 0){ return(NULL) }
121+
if (!ready_to_plot()){ return(NULL) }
82122

83123
# This returns the single row of data corresponding to where the user clicked
84124
d = plotly::event_data("plotly_click")
@@ -88,10 +128,12 @@ shiny::shinyServer(function(input, output) {
88128

89129
x = d$x[[1]]
90130
segment = segments()
91-
segment %>% dplyr::filter(dplyr::near(time, x, tol=0.5))
131+
segment %>% dplyr::filter(dplyr::near(time, x, tol=0.05))
92132
})
93133

94134
selected_segment = reactive({
135+
if (!ready_to_plot()){ return(NULL) }
136+
95137
# This returns all the rows corresponding to all the data points in the current model segment
96138
sel = selected_point()
97139
all_seg = segments()
@@ -102,25 +144,34 @@ shiny::shinyServer(function(input, output) {
102144
all_seg %>% dplyr::filter(model_number == sel$model_number)
103145
})
104146

105-
output$description = renderPrint({
147+
output$description = renderTable({
148+
region = selected_segment()
106149
segment = selected_point()
107-
if (is.null(segment) || nrow(segment) == 0){ return(NULL) }
108-
150+
if (is.null(segment) || nrow(segment) == 0 || nrow(region) == 0){ return(NULL) }
109151
lm = segment %>% dplyr::pull(lm) %>% dplyr::first()
110152
cofs = coefficients(lm)
111-
equation = stringr::str_glue(
112-
"f(x) = {slope}x + {intercept}",
113-
slope=formatC(cofs[[2]], digits=3),
114-
intercept = formatC(cofs[[1]], digits=3)
115-
)
116-
rsquared = (lm %>% vcov() %>% cov2cor() %>% `[[`(1, 2))^2
117-
stringr::str_glue("{equation}\nR Squared: {r2}", equation=equation, r2=rsquared %>% formatC(digits=3))
118-
})
153+
154+
list(
155+
c('Equation', stringr::str_glue(
156+
"f(x) = {slope}x + {intercept}",
157+
slope=formatC(cofs[[2]], digits=3),
158+
intercept = formatC(cofs[[1]], digits=3)
159+
) %>% as.character),
160+
c('R Squared', lm %>% summary %>% `$`('r.squared') %>% as.character),
161+
c('Intercept (&mu; mol L<sup>-1</sup>)', cofs %>% `[[`(1) %>% as.character),
162+
c('Rate (&mu; mol L <sup>-1</sup> s<sup>-1</sup>)', cofs %>% `[[`(2) %>% as.character),
163+
c('Rate (&mu; mol L <sup>-1</sup> min<sup>-1</sup>)', cofs %>% `[[`(2) %>% `*`(60) %>% as.character),
164+
c('Segment Start (sec)', region$time %>% min %>% as.character),
165+
c('Segment End (sec)', region$time %>% max %>% as.character)
166+
) %>% purrr::map_dfr(function(l){
167+
tibble::tibble_row(Attribute=l[[1]], Value=l[[2]])
168+
})
169+
}, sanitize.text.function = function(x) x, striped=TRUE, width="100%", colnames = FALSE)
119170

120171
output$fit = renderPlot({
121172
segment = selected_point()
122173
if (is.null(segment) || nrow(segment) == 0){ return(NULL) }
123-
174+
124175
lm = segment %>% dplyr::pull(lm) %>% dplyr::first()
125176
cofs = coefficients(lm)
126177
ggplot2::ggplot(lm$model, ggplot2::aes(x=time, y=conc)) +

‎todo.txt

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Show parameters in output
2+
Allow for manual split
3+
[x] Add per minute and per second rate
4+
[x] Fix R squared
5+
Consider fixed length, rolling regression
6+
Consider manual splits
7+
[x] Dropdown for logger
8+
Optional annotation of formula onto the graph
9+
Allow export for customizable graph (graphpad / excel)
10+
[x] Change default to be lower
11+
Allow hiding segment lines
12+
[x] Convert output into table
13+
Show segment boundaries in table

‎ui.R

+11-12
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,19 @@ shinyUI(fluidPage(
1919
title="Choose a .ulog logging file"
2020
),
2121
shinyBS::tipify(
22-
numericInput(
22+
selectInput(
2323
"logger",
2424
"Logger Number",
25-
min = 1,
26-
max = 100,
27-
value = 1
25+
choices = NULL
2826
),
2927
title = "The number of the logger to analyse, within the whole experiment"
3028
),
3129
shinyBS::tipify(
32-
sliderInput(
30+
numericInput(
3331
"minseglen",
3432
"Minimum Segment Length:",
35-
min = 3,
36-
max = 100,
37-
value = 30
33+
min = 2,
34+
value = 3
3835
),
3936
title = "The algorithm will not produce line segments any smaller than this. Increase this in order to reduce the number of splits"
4037
)
@@ -53,10 +50,12 @@ shinyUI(fluidPage(
5350
fluidRow(
5451
column(
5552
width=4,
56-
shinyBS::tipify(
57-
verbatimTextOutput("description"),
58-
title= "Regression statistics will appear here after you click on a line segment"
59-
),
53+
withMathJax(
54+
shinyBS::tipify(
55+
tableOutput("description"),
56+
title= "Regression statistics will appear here after you click on a line segment"
57+
)
58+
)
6059
),
6160
column(
6261
width=8,

0 commit comments

Comments
 (0)