How to Create a Reactive Base R Boxplot in Shiny? - r

I have my data frame named df...
pain_severity = c(1, 5, 10, 8, 6, 4)
urinary_severity = c(3, 8, 9, 7, 6, 10)
df = data.frame(pain_severity, urinary_severity)
I let the user choose whether they want pain or urinary severity in select input...
UI:
tabPanel("1-Variable Visualization",
sidebarLayout(
sidebarPanel(
h1("Menu Selection"),
selectInput(inputId = "variable_choice",
label = "Variable",
choices = df,
selected = "pain_severity")
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Boxplot", plotOutput("boxplot")),
)))))
SERVER:
server <- function(input, output, session) {
output$boxplot <- renderPlot({
boxplot(input$variable_choice,
data = df,
main = "", xlab="", ylab = "", horizontal = TRUE)
points(mean(input$variable_choice, na.rm = TRUE),
data = df,
1,
col = "red")
})
}
I keep getting an error that reads... ERROR: non-numeric argument to binary operator
Why does this happen? When I run the code in base R using just pain or urinary severity as my variable it works perfectly. When I ask it to adapt based on user input from variable_choice, it gives me that error message. Do I have to use ggplot when trying to run user inputs?

Your code has several issues:
the choices argument should only take a vector of column names, try choices = colnames(df)
the plot functions either need a formula (however I couldn't make it work without a grouping variable) or the data itself. Try:
output$boxplot <- renderPlot({
boxplot(df[input$variable_choice],
main = "", xlab=input$variable_choice, ylab = "", horizontal = TRUE)
points(mean(df[input$variable_choice], na.rm = TRUE),
1,
col = "red")
})
In this way, you use the character input$variable_choice to index your data.frame

Thank you. Starja is right. I have a more in depth code for choices based on user selections that I felt would bog down the original question so I simplified it by just writing df, but I should have said colnames(df), you're right. The boxplot works perfectly, but the point returns an NA value and the error says "argument is not numeric or logical: returning NA" and the point does not show up on the graph.
My solution to this was to use colMeans and define it in the server like so...
SERVER:
col_mean <- colMeans(filtered_data()[1:908], na.rm = TRUE)
points(col_mean[input$variable_choice], 1, col = "red")
I created col_mean to grab the first 908 numeric columns in my dataset and convert it to a one mean point in a new dataset. I then used that new dataset in points which gave me the correct mean display for each.

Related

How to change the y in flexdashboard selectInput

I'm trying to make a flexdashboard using IMDb data, that has an interactive jitter plot where you can change the x and y for visualizing hierarchical clustering result. The code that I've already made can change only the x and number of k. I think I should use reactive function but I don't really understand in using that. I've already tried many other ways from youtube and some documentary but still can't change the y. Here is layout of my dashboard, The y stuck at the runtime variable
data=df %>%
select(Rating, Votes, Gross, Runtime, Metascore)
selectInput("x", label = "X : ",choices = names(data))
selectInput("y", label = "Y : ",choices = names(data))
sliderInput('k',"Cluster",min = 2,max = 10, value = 6)
selectedData=reactive({
data %>% select(input$x, input$y)
})
data_scaled=scale(data)
dist_data=dist(data_scaled, method='euclidean')
hc_data=hclust(dist_data, method = "average")
renderPlot({
ggplot(selectedData(),
aes(x=!!rlang::sym(input$x), y=!!rlang::sym(input$y),
col=factor(cutree(hc_data, k=input$k))))+
geom_jitter(size=5, alpha=0.5 )+
labs(col="Cluster")
})
Here is an alternative example that seems to work, using the diamonds dataset from ggplot2. My guess is that the scaling and clustering steps take so long to run that the the y reactive only appears not to work. I would suggest pre-processing your data if app run times are a problem.
data=diamonds[1:1e3,] %>%
dplyr::select(where(is.numeric))
selectInput("x", label = "X : ",choices = names(data))
selectInput("y", label = "Y : ",choices = names(data))
sliderInput('k',"Cluster",min = 2,max = 10, value = 6)
data_scaled=scale(data)
dist_data=dist(data_scaled, method='euclidean')
hc_data=hclust(dist_data, method = "average")
renderPlot({
ggplot(data,
aes(x=!!rlang::sym(input$x), y=!!rlang::sym(input$y),
col=factor(cutree(hc_data, k=input$k))))+
geom_jitter(size=5, alpha=0.5 )+
labs(col="Cluster")
})

How can I summarize reactive data from outside a render function in a Shiny app?

For this particular shiny example I am trying to apply a circular model and display and summarize it within the ggplot and a summary table. This is straightforward up until trying to add in reactive 'brushplot' capabilities. Each of the data points represent a date and the point of the selective graph is to be able to discard undesirable dates. As far as I've figured out, this requires the filtering and model fitting to be within a renderPlot which then leads to complications (unable to find the data/model) trying to call the filtered data and the circular model's statistical outputs outside the function and/or within another reactive function. This yields the Error: object 'k_circ.lm' not found So my questions are:
How can I read the filtered data from the renderPlot function
to the summarytable matrix?
How could I similarly add the fitted model values and residuals from k_circ.lm?
Is there a better or simpler way to arrange app to avoid this?
Alternatative code lines are commented out for a working (if poorly formatted) summary table.
library(dplyr) # For data manipulation
library(ggplot2) # For drawing plots
library(shiny) # For running the app
library(plotly) # For data manipulation
library(circular) # For Circular regressions
library(gridExtra)
# Define UI ----
ui <- fluidPage(
# App title ----
titlePanel("Circular Brushplot Demo"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
sidebarPanel(
actionButton("exclude_toggle", "Toggle points"),
actionButton("exclude_reset", "Reset")
),
# Main panel for displaying outputs ----
mainPanel(
#reactive plot output with point and 'brush' selection
fluidRow(plotOutput("k", height = 400,
click = "k_click",
brush = brushOpts(
id = "k_brush" ))),
plotOutput("s", height = 400)
)
)
)
# Define server logic
server <- function(input, output) {
psideg <- c(356,97,211,232,343,292,157,302,335,302,324,85,324,340,157,238,254,146,232,122,329)
thetadeg <- c(119,162,221,259,270,29,97,292,40,313,94,45,47,108,221,270,119,248,270,45,23)
## Data in radians then to "circular format"
psirad <- psideg*2*pi/360
thetarad <- thetadeg*2*pi/360
cpsirad <- circular(psirad)
cthetarad <- circular(thetarad)
cdat <- data.frame(cpsirad, cthetarad)
###### reactive brush plot ########
# For storing which rows have been excluded
vals <- reactiveValues(
keeprows = rep(TRUE, nrow(cdat)))
output$k <- renderPlot({
# Plot the kept and excluded points as two separate data sets
keep <- cdat[ vals$keeprows, , drop = FALSE]
exclude <- cdat[!vals$keeprows, , drop = FALSE]
## Fits circular model specifically for 'keeprows' of selected data
k_circlm <- lm.circular(type = "c-c", y = keep$cthetarad, x = keep$cpsirad, order = 1)
k_circlm
ggplot(keep, aes(cthetarad, cpsirad)) +
geom_point(aes(cthetarad, cpsirad, colour = keep$Vmag, size = 5))+
scale_colour_gradient(low ="blue", high = "red")+
geom_smooth(method = lm, fullrange = TRUE, color = "black") +
geom_point(data = exclude, shape = 13, size = 5, fill = NA, color = "black", alpha = 0.25) +
annotate("text", x = min(keep$cthetarad), y = Inf, hjust = .1, vjust = 1,
label = paste0("p value 1 = ", round(k_circlm$p.values[1], 2)), size = 7)+
annotate("text", x = min(keep$cthetarad), y = Inf, hjust = .1, vjust = 2.5,
label = paste0("p value 2 = ", round(k_circlm$p.values[2], 2)), size = 7)+
annotate("text", x = min(keep$cthetarad), y = Inf, hjust = .1, vjust = 4,
label = paste0("rho = ", round(k_circlm$rho, 2)), size = 7)+
xlab("Lighthouse Direction (radians)")+ ylab("ADCP site direction (radians)")+
theme(axis.title.x = element_text(size = 20), axis.title.y = element_text(size = 20))
})
# Toggle points that are clicked
observeEvent(input$k_click, {
res <- nearPoints(cdat, input$k_click, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)})
# Toggle points that are brushed, when button is clicked
observeEvent(input$exclude_toggle, {
res <- brushedPoints(cdat, input$k_brush, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)})
# Reset all points
observeEvent(input$exclude_reset, {
vals$keeprows <- rep(TRUE, nrow(cdat))})
output$s <- renderPlot({
# Create Summary table
summarytable <- data.frame(matrix(ncol = 4, nrow = nrow(keep)))
colnames(summarytable) <- c( "Psi_dir", "Theta_dir", "Fitted_values", "Residuals")
# Un-comment lines below to read from non-reactive data for working summary table
#summarytable$Psi_dir <- round(cdat$cpsirad, 2)
#summarytable$Theta_dir <- round(cdat$cthetarad, 2)
# attempting to pull from circlm within render plot
# comment out for summarytable to work
summarytable$Psi_dir <- round(keep$cpsirad, 2)
summarytable$Theta_dir <- round(keep$cthetarad, 2)
summarytable$Fitted_values <- round(k_circ.lm$fitted)
summarytable$Residuals <- round(k_circ.lm$residuals)
# outputing table with minimal formatting
summarytable <-na.omit(summarytable)
t <- tableGrob(summarytable)
Q <- grid.arrange(t, nrow = 1)
Q
}
)
}
shinyApp(ui = ui, server = server)
Here's a few ideas - but there are multiple approaches to handling this, and you probably want to restructure your server function a bit more after working with this further.
First, you probably want a reactive expression that will update your model based on vals$keeprows as this changes with your clicks. Then, you can access the model results from this expression from both your plot and data table.
Here is an example:
fit_model <- reactive({
## Keep and exclude based on reactive value keeprows
keep = cdat[ vals$keeprows, , drop = FALSE]
exclude = cdat[!vals$keeprows, , drop = FALSE]
## Fits circular model specifically for 'keeprows' of selected data
k_circlm <- lm.circular(type = "c-c", y = keep$cthetarad, x = keep$cpsirad, order = 1)
## Returns list of items including what to keep, exclude, and model
list(k_circlm = k_circlm, keep = keep, exclude = exclude)
})
It will return a list that you can access from the plot:
output$k <- renderPlot({
exclude <- fit_model()[["exclude"]]
keep <- fit_model()[["keep"]]
k_circlm <- fit_model()[["k_circlm"]]
ggplot(keep, aes(cthetarad, cpsirad)) +
...
And can access the same from your table (though you have as renderPlot?):
output$s <- renderPlot({
keep = fit_model()[["keep"]]
k_circ.lm <- fit_model()[["k_circlm"]]
# Create Summary table
summarytable <- data.frame(matrix(ncol = 4, nrow = nrow(keep)))
...
Note that because the table length changes with rows kept, you might want to use nrow(keep) as I have above, rather than nrow(cdat), unless I am mistaken.
I also loaded gridExtra library for testing this.
I suspect there are a number of other improvements you could consider, but thought this might help you get to a functional state first.

How do I get a summary table from a custom function to react to user input of variables?

This is my first attempt at using Shiny.
I have a simulated patient-level dataset with 4 variables:
group: Categorical, takes on values A, B and C. Represents 3 different treatment types that were used in the study.
week: Numeric variable, takes on values 1, 4, 8.Represents follow-up week.
painscore: Numeric variable, score on scale of 1-10, with 1 indicating no pain, 10 indicating extreme pain.
dependscore: Numeric variable, score on scale of 1-10, with 1 indicating no dependency on pain meds, 10 indicating extreme dependency.
Trying to build a simple app that accepts two inputs: the week, and the variable, and provides two outputs:
A boxplot of distribution of scores for the selected variable for the selected week. The x axis would represent the 3 levels of group (A, B and C).
A summary table the shows the number of observations, median, 25th percentile, 75th percentile and number of missing.
I was able to create the interactive boxplot, but I am unable to create the summary table. I was able to create static versions of this table in RMarkdown using the summaryBy function from doBy, but I am not able to implement it in Shiny. Tried following the advice here and here but I'm missing something.
Here's my code for reproducibility. Excuse the extensive annotations, (I'm a complete beginner) they are more for myself than for anyone else.
#libraries--------------------
library(shiny)
library(tidyverse)
library(knitr)
library(doBy)
#----------------------------
#input data
set.seed(123)
mydf <- data.frame( group = rep(rep(c("A","B","C"), each = 3), times = 3),
week = rep(rep(c(1,4,8), each = 9)),
painscore = sample(1:10, 27, replace = TRUE),
dependscore = sample(1:10, 27, replace = TRUE) )
#--------------------------
#define custom function to calculate summary statistics for column of interest.
#function explained in a little more detail when applied in the server function.
fun <- function(x) {
c( n = length(x),
m = median(x),
firstq = round(quantile(x)[2], 1),
lastq = round(quantile(x)[4], 1),
missing = mean(is.na(x)))
}
#-------------------------
#UI
ui <- fluidPage(
titlePanel("Shiny Boxplot and Table"),
#User can provide two different inputs
sidebarLayout(
sidebarPanel(
#1. allow user to pick week using radiobuttons
radioButtons(inputId = "pickedwk",
label = "week you want to display",
choices = c(1,4,8),
selected = 1),
#2. user can pick variable to visualize using dropdownboxes
selectInput(inputId = "var",
label = "variable to visualize",
list("How much pain did you feel today?" = "painscore",
"How dependent are you on medication?" = "dependscore")),
#helpertext
helpText("Enter week/variable choices here")
),
#Spaceholders for output
mainPanel(
plotOutput("boxplot"), #boxplot placeholder
htmlOutput("descriptives") #kable html table placeholder
)
)
)
#-------------------------
#Server
server <- function(input, output) {
#create dataset that subsets down to the week picked by user.
weeksub <- reactive({
mydf %>% filter(week == input$pickedwk[1])
})
#1. use reactive datasubset to render boxplot.
output$boxplot <- renderPlot({
ggplot(weeksub(), aes_string(x = "group", y = input$var)) + #input$var works here
geom_boxplot(fill = "red", outlier.shape = 15, outlier.color = "black") +
scale_y_continuous(name = as.character(input$var)) +
scale_x_discrete(name = "group") +
ggtitle(paste("Distribution of", as.character(input$var), "by treatment group"))
})
#2. use same reactive datasubset to render kable descriptive statistics for the variable picked.
output$descriptives <- renderText({
kable(summaryBy(input$var ~ group, data = as.data.frame(weeksub()), FUN = fun),
#note: here, I'm using the summaryBy function from package doBy. It takes the form var~ categoricalvar
# so, either painscore ~ group, or dependscore ~ group depending on what the user picked, and uses
#my custom function to return a table of count, median, 25th percentile, 75th percentile and missing count for
#the 3 levels of the group variable (for A, B, and C)
col.names = c("Number", "Median", "1Q", "3Q", "Missing"))
})
}#server function ends
# Run the application
shinyApp(ui = ui, server = server)
There are a couple of problems in your code:
The formula notation doesn't know how to deal with input$var. summaryBy supports an alternate syntax that works better. (You could also use as.formula and paste to build a formula.)
You are missing the "Group" column in col.names
You have to generate HTML from kable and pass it as HTML to the UI.
Change your table output to this:
output$descriptives <- renderUI({
HTML(kable(summaryBy(list(input$var, 'group'), data = as.data.frame(weeksub()), FUN = fun),
col.names = c('Group', "Number", "Median", "1Q", "3Q", "Missing"),
format='html'
))
})

ggplot won't use levels for x-axis order when a reactive input is empty

I am trying to create a shiny app with multiple sections, the section I am having trouble with right now displays calculated values on a ggplot graph. The user selects one Target gene from a dropdown list, and the graph displays calculated analysis values against a selection of other, Control genes. So far so simple.
I have a few default Control genesets, which I have preselected and that are always displayed, and then I have an option for the user to specify their own Control genes to perform analysis against. There is a checkbox that can be ticked if the user wants to select their own Controls. The user can also select different numbers of custom Controls, whereas the default controls each have sets of 3 Control genes.
Code for the default genesets as so:
ABC_control <- reactive(
Analysis_function(c("ACTB", "GAPDH", "TUBB")))
And code for the custom genesets is as so:
CUSTOM_control1 <- reactive( if (input$custom_checkbox1) {
Analysis_function(input$custom_controls1)
} else { NA } )
I have an if command in the Custom genesets so that they are not calculated and displayed if the tickbox is not checked.
First problem: The plot still displays an x axis label for the Custom control even if none is selected and the checkbox is not ticked. This is not a major problem, just an annoying one.
The second problem:
When displaying just the default genesets everything runs perfectly. And when the user selects their own Control genes, everything runs fine.
The problem is when the user ticks the CheckboxInput(), and the selectizeInput() for the custom control genes is still empty, the graph goes and re-orders its x-axis into alphabetical order, rather than the levels order that I have specified earlier. As soon as a Control gene is selected, it re-orders back into the levels order. The problem only occurs when the selectizeInput box is empty, or a new gene is being selected.
How can I force the plot to always display in the correct levels order, even when the reactive custom input is empty?
Also, preferably, how can I prevent the Custom input from being displayed on the graph at all unless the checkbox is ticked.
A full Shiny app data is below:
#### Load packages ####
library(shiny)
library(ggplot2)
library(dplyr)
#### Load data files ####
load("GeneNames.Rda")
load("Dataset.Rda")
#### Define UI ####
ui <- fluidPage(
#### Sidebar inputs ####
sidebarLayout(
sidebarPanel(width = 3,
#first wellpanel for selecting Target gene
h4("Target gene selection"),
wellPanel(
selectInput(
inputId = "gene_select",
label = NULL,
choices = GeneNames,
selected = "ESAM")),
#Second wellpanel for selecting custom Control genes
h4("Custom control genes"),
wellPanel(
checkboxInput(inputId = "custom_checkbox1",
label = "Custom 1:"),
conditionalPanel(condition = "input.custom_checkbox1 == true",
selectizeInput(inputId = "custom_controls1",
label = NULL,
choices = GeneNames,
multiple = TRUE,
options = list(openOnFocus = FALSE, closeAfterSelect = TRUE, maxOptions = 50, maxItems = 6))))
),
#### Mainpanel results Normal ####
mainPanel(width = 9,
#HTML code to have the last entry in any tables bolded (last entry is Mean in all tables)
#Results title and main bar plot graph
fluidRow(plotOutput(outputId = "celltype_bar_plot"),width = 9)
)))
#### Define server ####
server <- function(input, output) {
target_gene <- reactive({
input$gene_select
})
#### calculations ####
Analysis_function <- function(controls){
cor(Dataset[, target_gene()], Dataset[, controls])
}
ABC_control <- reactive(
Analysis_function(c("ACTB", "GAPDH", "TUBB")))
GHI_control <- reactive(
Analysis_function(c("ACTB", "GAPDH", "TUBB")))
DEF_control <- reactive(
Analysis_function(c("ACTB", "GAPDH", "TUBB")))
CUSTOM_control1 <- reactive( if (input$custom_checkbox1) {
Analysis_function(input$custom_controls1)
} else { NA } )
#### Analysis datatables Normal ####
control_list <- c("ABC_control", "GHI_control", "DEF_control", "CUSTOM_control1")
analysis_list <- reactive({ list(ABC_control(), GHI_control(), DEF_control(), CUSTOM_control1()) })
#generating melted data table of the induvidual analysed gene values, transposed to get in right format, and times = c(length()) to replicate titles the correct no of times
values_list <- reactive({
data.frame(Control_types2 = factor(rep(control_list, times = c(unlist(lapply(analysis_list(), length)))), levels =control_list),
values = c(unlist(lapply(analysis_list(), t))))
})
#Generating data table of the means of analysed values above
Mean_list <- reactive({
data.frame(Control_types = factor(control_list, levels =control_list),
Mean_correlation = c(unlist(lapply(analysis_list(), mean))))
})
#### Main Bar Plot Normal ####
output$celltype_bar_plot <- renderPlot({
ggplot() +
geom_point(data = values_list(),aes(x=Control_types2, y=values,size = 7, color = Control_types2), show.legend = FALSE, position=position_jitter(h=0, w=0.1), alpha = 0.7) +
geom_boxplot(data = Mean_list(), aes(Control_types, Mean_correlation), size = 0.5, colour = "black")
})
}
#### Run application ####
shinyApp(ui = ui, server = server)
I can't fully test this solution since the data you provided isn't available (so I can't run the app), but I suspect that the following should help.
First, by using ordered or factor(..., ordered = TRUE) you can tell the graph what order to put label in.
Second, in order to prevent the column from showing up on the graph you must remove all datapoints for that column INCLUDING NA.
control_list <- c("ABC_control", "GHI_control", "DEF_control", "CUSTOM_control1")
# Some data to try out
values_list <- data.frame(
Control_types2 = ordered(rep(control_list, times = 4), levels =control_list),
values = c(0.25,0.50,0.75,NA)
)
Mean_list <- data.frame(
Control_types = ordered(control_list, levels =control_list),
Mean_correlation = c(0.25,0.50,0.75,NA)
)
# Original plot code
ggplot() +
geom_point(data = values_list,aes(x=Control_types2, y=values,size = 7, color = Control_types2), show.legend = FALSE, position=position_jitter(h=0, w=0.1), alpha = 0.7) +
geom_boxplot(data = Mean_list, aes(Control_types, Mean_correlation), size = 0.5, colour = "black")
# New plot with NA values removed
ggplot() +
geom_point(data = values_list %>% filter(!is.na(values)),
aes(x=Control_types2, y=values,size = 7, color = Control_types2),
show.legend = FALSE,
position=position_jitter(h=0, w=0.1),
alpha = 0.7) +
geom_boxplot(data = Mean_list %>% filter(!is.na(Mean_correlation)),
aes(Control_types, Mean_correlation),
size = 0.5,
colour = "black")

Shiny issue related to withMathJax and the evaluation order

Yesterday, I did my first attempt at creating a shiny app. I have come this far but some things I don't quite understand yet so here is the app:
library(shiny)
library(ggplot2)
### Ui
ui <- shinyUI(fluidPage(
titlePanel("Distributions"),
sidebarLayout(
sidebarPanel(
selectInput("dist", "Distribution",
c("Normal distribution" = "norm",
"t-distribution" = "t",
"Poisson distribution" = "pois"
)
),
uiOutput("ui")
),
mainPanel(
plotOutput("plot")
)
)
)
)
### Server
server <- shinyServer(function(input, output) {
output$ui <- renderUI({
switch(input$dist,
"norm" = withMathJax(wellPanel(numericInput("norm_mu", "Mean: \\(\\mu\\)", value = 0),
numericInput("norm_sd", "Standard deviation: \\(\\sigma\\)", value = 1, min = 0))),
"t" = numericInput("t_df", "Degrees of freedom:", value = 1, min = 1),
"pois" = withMathJax(numericInput("pois_lambda", "Parameter: \\(\\lambda\\)",
value = 1))
)
})
output$plot <- renderPlot({
dist_range <- switch(input$dist,
"norm" = c(-10, 10),
"t" = c(-10, 10),
"pois" = seq(0, 20, by = 1))
a <- ggplot(data.frame(x = dist_range), aes(x = x))
a <- a + switch(input$dist,
"norm" = stat_function(fun = dnorm, args = list(mean = input$norm_mu, sd = input$norm_sd)),
"t" = stat_function(fun = dt, args = list(df = input$t_df)),
"pois" = geom_bar(aes(y = dpois(x, lambda = input$pois_lambda)), stat = "identity")
)
a
})
})
shinyApp(ui = ui, server = server)
Now my questions are:
When I run the app for the first time, I get no plot in the main panel but two errors in my console:
Error in (function (x, mean = 0, sd = 1, log = FALSE) :
Non-numeric argument for mathematical function
Error in exists(name, envir = env, mode = mode) :
argument "env" is missing, with no default
I figured out, that withMathJax is causing these errors but two things puzzle me anyway.
a. When I remove withMathJax, the plot appears but I still get those errors. Why is that?
b. If I dont remove withMathJax, run the app and then chose another distribution, I get a plot, however the errors are still there. If I go back to the normal distribution again, the plot appears (along with errors). If I choose the t-distribution again, everything works perfectly without any errors. Why is that ?
Note: I guess it has something to do with what gets evaluated when and how. If so, could someone 1.) clarify that for me or at least point to some good documentation and 2.) explain how to avoid this behaviour.
Choosing the Poisson distribution results in an error:
Error in dpois(x, lambda = input$pois_lambda) : object 'input' not found
Why can't input$pois_lambda be accessed here? What is different to the stat_function(...) part used for the normal and the t-distribution, where accessing doesn't seem to be a problem?
Help is much appreciated! Thanks

Resources