Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mrc-5455-v2 POC: Can add graphs to Run and drag variables to select or hide #205

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

EmmaLRussell
Copy link
Contributor

@EmmaLRussell EmmaLRussell commented Jun 13, 2024

This proof of concept demonstrates an interface allowing the user to add more graphs, in order to show different sets of variables (for which different scales may be appropriate) on each graph. You can run it up locally or it is currently deployed to wodin-dev: https://wodin-dev.dide.ic.ac.uk/preview/apps/defaultcode/

From the Selected Variables area on the Code Tab, the user can now "Add graph" from a button and drag variables between graphs, or to "Hidden variables" to remove from all graphs. This currently just adds graphs to the Run Tab, but we'll also include new graphs on Sensitivity too, and will ensure that all graphs' Time axes are identical.

You can press Ctrl key when drag to make a duplicate variable in another graph. Duplicates have an 'x' button to remove that instance only (or you can also drag to Hidden from one graph, which will still leave it behind on any others).

When we implement for real, we'll tidy up the code including:

  • make the graphs configuration an array rather than a dictionary, as the keys aren't significant, and we don't let users edit the graph names anyway.
  • move the graphs configuration into a different module, doesn't really belong in model
  • eliminate duplicate code in Hidden Variables/Selected Variables

I'll implement what's here "for real" in a couple of separate branches with tests etc, so this PR shouldn't be merged. Would be great to get your input on the usability in particular.

Still TODO (not in POC):

  • can delete all graphs apart from the final one, rather than hardcode Graph 1 as non-deletable
  • ripple view of any duplicates when hide or delete another instance
  • hide Time axis label except for final graph
  • make "Add to new graph" available as additional drop zone (to automatically add a graph without using button"
  • delayed tooltips with available actions on variables (maybe)
  • make graph height variable by user
  • include extra graphs in Sensitivity
  • Fit tab to show all variables except those which are Hidden
  • eye icon in graph label header to hide or show graph
  • cog icon in graph label header for changing Graph Settings
  • harmonise Time axes and legend width in plotly (can link plots?). Show or hide legend globally (ensure legend always appear on downloaded plots).

When you test, you might want to use the Ebola model code to give you more variables to play with:

################################################################################
# Model 3 elaborates on Model 2 by including an accute death infectious stage 
# representing the infectiouness associated with unsafe burials procedures.
# A given proportion of cases who dies and doesn't go to hospital and are buried 
# unsafely. For infectious cases who will die, we assume that a proportion 
# 'p_funeral' of onward transmission potential would occur during the accute 
# death stage mentionned above.
# 
# S = # susceptible 
# E = # latent (split in two for more realistic distributed latency)
# I_d = # early infectious - not yet hospitalised - will die
# I_r = # early infectious - not yet hospitalised - will recover
# H_d = # hospitalised - will die
# H_r = # community (non-hospitalised) - will recover
# C_d = # community (non-hospitalised) - will die
# C_r = # hospitalised - will recover
# R = # recovered
# A_d = # between death and buried (accute transmission during dead stage)
################################################################################

################################################################################
### model dynamics
################################################################################

deriv (S) <- - S/N * (beta_r * (I_r + C_r) + 
                         beta_d * (I_d + C_d + beta__acute_d * A_d))

deriv (E_1) <- S/N * (beta_r * (I_r + C_r) + 
                         beta_d * (I_d + C_d + beta__acute_d * A_d)) - 
                         gamma_1 * E_1
deriv (E_2) <- gamma_1 * E_1 - gamma_2 * E_2

deriv (I_d) <- cfr * gamma_2 * E_2 - sigma_h * I_d 
deriv (I_r) <- (1 - cfr) * gamma_2 * E_2 - sigma_h * I_r

deriv(H_d) <- p_hosp * sigma_h * I_d - sigma_d * H_d
deriv(C_d) <- (1 - p_hosp) * sigma_h * I_d - sigma_d * C_d

deriv(H_r) <- p_hosp * sigma_h * I_r - sigma_r * H_r
deriv(C_r) <- (1 - p_hosp) * sigma_h * I_r - sigma_r * C_r

deriv(A_d) <- (1 - p_safe) * sigma_d * C_d - sigma_acute_d * A_d

deriv(R) <- sigma_r * (H_r + C_r)
deriv(Dead) <- sigma_d * (H_d + C_d)

deriv(cumul_onset) <-  p_hosp * gamma_2 * E_2 
deriv(cumul_death_h) <-  sigma_d * H_d

### create delayed variables in order to compute non cumulative (weekly) 
### incidence and deaths variables
cumul_onset_delayed <- delay(cumul_onset, 7) # 7 for weekly delay
cumul_death_h_delayed <- delay(cumul_death_h, 7) # 7 for weekly delay
output(weekly_onset) <- cumul_onset - cumul_onset_delayed
output(weekly_death_h) <- cumul_death_h - cumul_death_h_delayed

### useful variables to output

Exposed <- E_1 + E_2
Infectious_Community <- I_d + I_r + C_d + C_r
Infectious_isolated <- H_d + H_r
unsafe_community_burial <- (1 - p_safe) * sigma_d * C_d
safe_community_burial <- p_safe * sigma_d * C_d

################################################################################
### initial numbuer of individuals in each compartment
################################################################################

initial(S) <- N
initial(E_1) <- 0
initial(E_2) <- 0
initial(I_d) <- I0 / 2
initial(I_r) <- I0 / 2
initial(H_d) <- 0
initial(C_d) <- 0
initial(A_d) <- 0
initial(H_r) <- 0
initial(C_r) <- 0
initial(R) <- 0 
initial(Dead) <- 0
initial(cumul_onset) <- 0
initial(cumul_death_h)  <- 0

################################################################################
### user defined parameters
################################################################################

N <- user(5e+5, min = 0) # population size with default value
I0 <- user(1, min = 0) # initial number of infected individuals 
L <- user(9.92, min = 0) # mean latency
L_frac_1 <- user(0.212, min = 0, max = 1) # fraction of latency spent in first latent compartment
mu_d <- user(8.0, min = 0) # time from admission to death in days
sigma_acute_d <- user(1, min = 0) # 1 / mean time of acute death stage
mu_r <- user(16.98, min = 0) # mean time from admission to recovery in days
cfr <- user(0.5656, min = 0, max = 1) # case fatality ratio
p_hosp <- user(0.7, min = 0, max = 1) # proportion hospitalised
R0 <- user(2.5, min = 0) # R0 (assumed the same for those who stay in community and 
    # (1) recover or (2) died with unsafe burial AND pFuneral transmission event 
    # occur after death for those dying
p_funeral <- user(0.5, min = 0, max = 1) # proportion of transmission potential occurring 
  # during funeral for those who die
t_intervention <- user(170, min = 0) # time of interventions
p_safe_before <- user(0.1, min = 0, max = 1) # proportion of safe burials before interventions
p_safe_after <- user(0.7, min = 0, max = 1) # proportion of safe burials after interventions
mu_h_before <- user(3.34, min = 0) # mean onset to hosp. delay before interventions
mu_h_after <- user(1.46, min = 0) # mean onset to hosp. delay after interventions

### compute other parameters from the ones above
gamma_1 <- 1/(L*L_frac_1)
gamma_2 <- 1/(L*(1 - L_frac_1))
sigma_h <- 1 / mu_h
sigma_d <- 1 / (mu_d - mu_h)
sigma_r <- 1 / (mu_r - mu_h)
beta_r <- R0 / mu_r
beta_d <- R0 * p_funeral / mu_d
beta__acute_d <- mu_d
# get an Rt (assuming no running out of suceptible, i.e. S~N)
Rt <- cfr * beta_d * 
  (p_hosp * mu_h + (1 - p_hosp) * mu_d + 
     (1 - p_hosp) * (1 - p_safe) * beta__acute_d) + 
  (1 - cfr) * beta_r * (p_hosp * mu_h + (1 - p_hosp) * mu_r)

p_safe <- if (t <= t_intervention) p_safe_before else p_safe_after 
mu_h <- if (t <= t_intervention) mu_h_before else mu_h_after 

### additional things to output
output(mu_h) <- TRUE
output(p_safe) <- TRUE

Copy link
Collaborator

@david-mears-2 david-mears-2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works nicely. I like the gradient colors of the variable buttons. Could extend out the range to include purple at one end, in case someone has a whole lot of variables.

Also, possibly useful to be able to select and drag more than one variable at once? (shift+click to select more than one.) But might not add enough value to be worth bothering to implement.

Comment on lines +19 to +25
<div class="ms-2">
Drag variables to move to another graph, or to hide variable. Press the Ctrl key on drag to make a
copy of a variable.
</div>
<selected-variables v-for="graphKey in graphKeys" :graph-key="graphKey"></selected-variables>
<hidden-variables style="clear: both"></hidden-variables>
<button class="btn btn-primary mt-2 float-end" id="add-graph-btn" @click="addGraph">Add Graph</button>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we only show this if there are multiple graphs? Otherwise, it's a bit confusing because you can't in fact drag the variables, yet.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see, you can drag them already, to the hidden variables section.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I overlooked the 'hidden variables' bit at first while I was trying to suss out what the interaction was by trying to drag. So I wonder how this could have been more obvious to me on first seeing the feature. I should have created a graph first rather than trying to drag ineffectually.

Here are some ideas...

One half-baked idea is that there could be a 'graph N+1' (as it were) that you can always drag to, such that when you've only got Graph 1, you can still drag variables onto a new graph (so, in this case, Graph 2), and dragging variables into the next graph causes the graph to start existing? Would perhaps need to grey out this draph graft.

A better iteration on that idea: when you start dragging a variable, if there is only one graph in existence, then a field appears to receive them, 'Drag variables here to create a new graph with these variables'.

Or, edit the instructions to mention the possibility of adding a graph or change the implied order of trying out the functionality, e.g.:

Drag variables to 'Hidden variables' to remove them from your graph, or click 'Add Graph' to create a new graph to move them to. Press the Ctrl key on drag to make a copy of a variable.

Or, move the 'add graph' button to sit before the 'hidden variables' bit, because the 'add graph' button is well into my peripheral vision when I'm reading the instructions, I wouldn't spot it unless looking for it. If moving it to this position, I'd make it into a + icon for concision.

@absternator
Copy link
Collaborator

Great work honestly looks really snazzy and works like a charm!!!!! Didnt review code itself as only a POC... just some minor suggestions/questions with UI/UX

  • Hidden variables may be easier with a toggle of sorts rather than a drag drop... Especially if multiple graphs will have to scroll down lots
  • dropdown title select variables may need to be renamed
  • Can the variables be duplicate at all times? If they need to 'turn off' variables they can from graph view? Would remove the need to do the ctrl + click and also all the logic surrounding that

@EmmaLRussell
Copy link
Contributor Author

Could extend out the range to include purple at one end, in case someone has a whole lot of variables.

Yeah, nice idea. As I recall, the colour scales was stolen from the old Shiny version. I think B -> R works nicely for a small number of colours as it keeps the ends of the scale very different, but we could add in the purples if you have lots of variables.

possibly useful to be able to select and drag more than one variable at once? (shift+click to select more than one.) But might not add enough value to be worth bothering to implement.

That would be nice too - I imagine most of the time users will want to see the effect of dragging one variable, but could also be useful to do multiple if it's a well known model.

Will add tickets for both of these.

@EmmaLRussell
Copy link
Contributor Author

EmmaLRussell commented Jun 25, 2024

A better iteration on that idea: when you start dragging a variable, if there is only one graph in existence, then a field appears to receive them, 'Drag variables here to create a new graph with these variables'.

Yep, Rich made this suggestion already, and there is a ticket for it. We'd have that even if there are already multiple graphs. We'll still keep the Add button as an alternative.

Or, edit the instructions to mention the possibility of adding a graph or change the implied order of trying out the functionality, e.g.:
Drag variables to 'Hidden variables' to remove them from your graph, or click 'Add Graph' to create a new graph to move them to. Press the Ctrl key on drag to make a copy of a variable.

Nice, will do.

Or, move the 'add graph' button to sit before the 'hidden variables' bit, because the 'add graph' button is well into my peripheral vision when I'm reading the instructions, I wouldn't spot it unless looking for it. If moving it to this position, I'd make it into a + icon for concision.

Yeah, it's definitely not quite right as it is. So maybe it does slot in above Hidden Variables, and the button transforms into the "Add to new graph" drop area (when implemented) on drag.

@EmmaLRussell
Copy link
Contributor Author

EmmaLRussell commented Jun 25, 2024

Hidden variables may be easier with a toggle of sorts rather than a drag drop... Especially if multiple graphs will have to scroll down lots

Yeah, and as David noted, users won't necessarily notice the Hidden variables area. One option would be for all variables to have the 'x' button, not just duplicates, and clicking that will move to Hidden (if there are no other instances).

dropdown title select variables may need to be renamed

Agreed - I just haven't thought of a better title yet. Maybe just "Graphs", but I quite like that "Select variables" gives you a clue about what the idea is behind having these graphs.

Can the variables be duplicate at all times? If they need to 'turn off' variables they can from graph view? Would remove the need to do the ctrl + click and also all the logic surrounding that

As I understand it, researchers are likely to be mostly wanting to shuffle round a single set of variables, assigning some to be hidden all the time, with occasional need to duplicate a variable in a couple of graphs. So this seemed to be a friendlier UI than making them use the plotly trace toggle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants