diff --git a/.Rprofile b/.Rprofile new file mode 100644 index 0000000..81b960f --- /dev/null +++ b/.Rprofile @@ -0,0 +1 @@ +source("renv/activate.R") diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..c48ae19 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,35 @@ +on: + workflow_dispatch: + push: + branches: main + +name: Quarto Publish + +jobs: + build-deploy: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Quarto + uses: quarto-dev/quarto-actions/setup@v2 + + - name: Install R + uses: r-lib/actions/setup-r@v2 + with: + r-version: '4.4.0' + + - name: Install R Dependencies + uses: r-lib/actions/setup-renv@v2 + with: + cache-version: 1 + + - name: Render and Publish + uses: quarto-dev/quarto-actions/publish@v2 + with: + target: gh-pages + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/GLM_cache/html/__packages b/GLM_cache/html/__packages deleted file mode 100644 index 1a23265..0000000 --- a/GLM_cache/html/__packages +++ /dev/null @@ -1,4 +0,0 @@ -colorDF -prettycode -ggplot2 -DescTools diff --git a/GLM_cache/html/data preprocessing_333ea1cdad323e4e81d6171f165c3db3.RData b/GLM_cache/html/data preprocessing_333ea1cdad323e4e81d6171f165c3db3.RData deleted file mode 100644 index 49e8b31..0000000 Binary files a/GLM_cache/html/data preprocessing_333ea1cdad323e4e81d6171f165c3db3.RData and /dev/null differ diff --git a/GLM_cache/html/data preprocessing_333ea1cdad323e4e81d6171f165c3db3.rdb b/GLM_cache/html/data preprocessing_333ea1cdad323e4e81d6171f165c3db3.rdb deleted file mode 100644 index d3bdfd1..0000000 Binary files a/GLM_cache/html/data preprocessing_333ea1cdad323e4e81d6171f165c3db3.rdb and /dev/null differ diff --git a/GLM_cache/html/data preprocessing_333ea1cdad323e4e81d6171f165c3db3.rdx b/GLM_cache/html/data preprocessing_333ea1cdad323e4e81d6171f165c3db3.rdx deleted file mode 100644 index fa426c6..0000000 Binary files a/GLM_cache/html/data preprocessing_333ea1cdad323e4e81d6171f165c3db3.rdx and /dev/null differ diff --git a/GLM_cache/html/dichotomize dv_b142289b2991c32ebffb4390228b43c5.RData b/GLM_cache/html/dichotomize dv_b142289b2991c32ebffb4390228b43c5.RData deleted file mode 100644 index 19809e7..0000000 Binary files a/GLM_cache/html/dichotomize dv_b142289b2991c32ebffb4390228b43c5.RData and /dev/null differ diff --git a/GLM_cache/html/dichotomize dv_b142289b2991c32ebffb4390228b43c5.rdb b/GLM_cache/html/dichotomize dv_b142289b2991c32ebffb4390228b43c5.rdb deleted file mode 100644 index 4686f03..0000000 Binary files a/GLM_cache/html/dichotomize dv_b142289b2991c32ebffb4390228b43c5.rdb and /dev/null differ diff --git a/GLM_cache/html/dichotomize dv_b142289b2991c32ebffb4390228b43c5.rdx b/GLM_cache/html/dichotomize dv_b142289b2991c32ebffb4390228b43c5.rdx deleted file mode 100644 index 4f7f840..0000000 Binary files a/GLM_cache/html/dichotomize dv_b142289b2991c32ebffb4390228b43c5.rdx and /dev/null differ diff --git a/GLM_cache/html/estimate probabilities_67fcd8b4852dd069a8a3cf9109640e73.RData b/GLM_cache/html/estimate probabilities_67fcd8b4852dd069a8a3cf9109640e73.RData deleted file mode 100644 index 1841613..0000000 Binary files a/GLM_cache/html/estimate probabilities_67fcd8b4852dd069a8a3cf9109640e73.RData and /dev/null differ diff --git a/GLM_cache/html/estimate probabilities_67fcd8b4852dd069a8a3cf9109640e73.rdb b/GLM_cache/html/estimate probabilities_67fcd8b4852dd069a8a3cf9109640e73.rdb deleted file mode 100644 index 2ffd741..0000000 Binary files a/GLM_cache/html/estimate probabilities_67fcd8b4852dd069a8a3cf9109640e73.rdb and /dev/null differ diff --git a/GLM_cache/html/estimate probabilities_67fcd8b4852dd069a8a3cf9109640e73.rdx b/GLM_cache/html/estimate probabilities_67fcd8b4852dd069a8a3cf9109640e73.rdx deleted file mode 100644 index ccf9e5b..0000000 Binary files a/GLM_cache/html/estimate probabilities_67fcd8b4852dd069a8a3cf9109640e73.rdx and /dev/null differ diff --git a/GLM_cache/html/estimate proportion_9700e80743eb027071a036156351aa15.RData b/GLM_cache/html/estimate proportion_9700e80743eb027071a036156351aa15.RData deleted file mode 100644 index 5644452..0000000 Binary files a/GLM_cache/html/estimate proportion_9700e80743eb027071a036156351aa15.RData and /dev/null differ diff --git a/GLM_cache/html/estimate proportion_9700e80743eb027071a036156351aa15.rdb b/GLM_cache/html/estimate proportion_9700e80743eb027071a036156351aa15.rdb deleted file mode 100644 index 26a8b32..0000000 Binary files a/GLM_cache/html/estimate proportion_9700e80743eb027071a036156351aa15.rdb and /dev/null differ diff --git a/GLM_cache/html/estimate proportion_9700e80743eb027071a036156351aa15.rdx b/GLM_cache/html/estimate proportion_9700e80743eb027071a036156351aa15.rdx deleted file mode 100644 index bf06f28..0000000 Binary files a/GLM_cache/html/estimate proportion_9700e80743eb027071a036156351aa15.rdx and /dev/null differ diff --git a/GLM_cache/html/install packages_8ba233c9e3d3d517c51c1149ee205091.RData b/GLM_cache/html/install packages_8ba233c9e3d3d517c51c1149ee205091.RData deleted file mode 100644 index 46b10c8..0000000 Binary files a/GLM_cache/html/install packages_8ba233c9e3d3d517c51c1149ee205091.RData and /dev/null differ diff --git a/GLM_cache/html/install packages_8ba233c9e3d3d517c51c1149ee205091.rdb b/GLM_cache/html/install packages_8ba233c9e3d3d517c51c1149ee205091.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/GLM_cache/html/install packages_8ba233c9e3d3d517c51c1149ee205091.rdx b/GLM_cache/html/install packages_8ba233c9e3d3d517c51c1149ee205091.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/GLM_cache/html/install packages_8ba233c9e3d3d517c51c1149ee205091.rdx and /dev/null differ diff --git a/GLM_cache/html/logistic regression_93f5fbcd7bea3ae23f5a6d4c4e8be2ca.RData b/GLM_cache/html/logistic regression_93f5fbcd7bea3ae23f5a6d4c4e8be2ca.RData deleted file mode 100644 index abc0272..0000000 Binary files a/GLM_cache/html/logistic regression_93f5fbcd7bea3ae23f5a6d4c4e8be2ca.RData and /dev/null differ diff --git a/GLM_cache/html/logistic regression_93f5fbcd7bea3ae23f5a6d4c4e8be2ca.rdb b/GLM_cache/html/logistic regression_93f5fbcd7bea3ae23f5a6d4c4e8be2ca.rdb deleted file mode 100644 index 217eab2..0000000 Binary files a/GLM_cache/html/logistic regression_93f5fbcd7bea3ae23f5a6d4c4e8be2ca.rdb and /dev/null differ diff --git a/GLM_cache/html/logistic regression_93f5fbcd7bea3ae23f5a6d4c4e8be2ca.rdx b/GLM_cache/html/logistic regression_93f5fbcd7bea3ae23f5a6d4c4e8be2ca.rdx deleted file mode 100644 index 9c317eb..0000000 Binary files a/GLM_cache/html/logistic regression_93f5fbcd7bea3ae23f5a6d4c4e8be2ca.rdx and /dev/null differ diff --git a/GLM_cache/html/plot_b9b043d0e96e6cd7502a1e235d4d873a.RData b/GLM_cache/html/plot_b9b043d0e96e6cd7502a1e235d4d873a.RData deleted file mode 100644 index 6b39e99..0000000 Binary files a/GLM_cache/html/plot_b9b043d0e96e6cd7502a1e235d4d873a.RData and /dev/null differ diff --git a/GLM_cache/html/plot_b9b043d0e96e6cd7502a1e235d4d873a.rdb b/GLM_cache/html/plot_b9b043d0e96e6cd7502a1e235d4d873a.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/GLM_cache/html/plot_b9b043d0e96e6cd7502a1e235d4d873a.rdx b/GLM_cache/html/plot_b9b043d0e96e6cd7502a1e235d4d873a.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/GLM_cache/html/plot_b9b043d0e96e6cd7502a1e235d4d873a.rdx and /dev/null differ diff --git a/GLM_cache/html/power analysis_9d1a2931a28a609aedf4ab38d3933b71.RData b/GLM_cache/html/power analysis_9d1a2931a28a609aedf4ab38d3933b71.RData deleted file mode 100644 index e187e9b..0000000 Binary files a/GLM_cache/html/power analysis_9d1a2931a28a609aedf4ab38d3933b71.RData and /dev/null differ diff --git a/GLM_cache/html/power analysis_9d1a2931a28a609aedf4ab38d3933b71.rdb b/GLM_cache/html/power analysis_9d1a2931a28a609aedf4ab38d3933b71.rdb deleted file mode 100644 index 2c343d9..0000000 Binary files a/GLM_cache/html/power analysis_9d1a2931a28a609aedf4ab38d3933b71.rdb and /dev/null differ diff --git a/GLM_cache/html/power analysis_9d1a2931a28a609aedf4ab38d3933b71.rdx b/GLM_cache/html/power analysis_9d1a2931a28a609aedf4ab38d3933b71.rdx deleted file mode 100644 index 37eaee3..0000000 Binary files a/GLM_cache/html/power analysis_9d1a2931a28a609aedf4ab38d3933b71.rdx and /dev/null differ diff --git a/GLM_cache/html/pwr2ppl_74d35d622e96f966f6e24586c6029cfe.RData b/GLM_cache/html/pwr2ppl_74d35d622e96f966f6e24586c6029cfe.RData deleted file mode 100644 index 54fb8d7..0000000 Binary files a/GLM_cache/html/pwr2ppl_74d35d622e96f966f6e24586c6029cfe.RData and /dev/null differ diff --git a/GLM_cache/html/pwr2ppl_74d35d622e96f966f6e24586c6029cfe.rdb b/GLM_cache/html/pwr2ppl_74d35d622e96f966f6e24586c6029cfe.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/GLM_cache/html/pwr2ppl_74d35d622e96f966f6e24586c6029cfe.rdx b/GLM_cache/html/pwr2ppl_74d35d622e96f966f6e24586c6029cfe.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/GLM_cache/html/pwr2ppl_74d35d622e96f966f6e24586c6029cfe.rdx and /dev/null differ diff --git a/GLM_cache/html/safeguard estimation_f3b5bbfc5377e749597a8b229f53d98a.RData b/GLM_cache/html/safeguard estimation_f3b5bbfc5377e749597a8b229f53d98a.RData deleted file mode 100644 index 75f0c54..0000000 Binary files a/GLM_cache/html/safeguard estimation_f3b5bbfc5377e749597a8b229f53d98a.RData and /dev/null differ diff --git a/GLM_cache/html/safeguard estimation_f3b5bbfc5377e749597a8b229f53d98a.rdb b/GLM_cache/html/safeguard estimation_f3b5bbfc5377e749597a8b229f53d98a.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/GLM_cache/html/safeguard estimation_f3b5bbfc5377e749597a8b229f53d98a.rdx b/GLM_cache/html/safeguard estimation_f3b5bbfc5377e749597a8b229f53d98a.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/GLM_cache/html/safeguard estimation_f3b5bbfc5377e749597a8b229f53d98a.rdx and /dev/null differ diff --git a/GLM_cache/html/safeguard power analysis_521c3cb74e98afd655e0c6f0cfcaf014.RData b/GLM_cache/html/safeguard power analysis_521c3cb74e98afd655e0c6f0cfcaf014.RData deleted file mode 100644 index 8649bb5..0000000 Binary files a/GLM_cache/html/safeguard power analysis_521c3cb74e98afd655e0c6f0cfcaf014.RData and /dev/null differ diff --git a/GLM_cache/html/safeguard power analysis_521c3cb74e98afd655e0c6f0cfcaf014.rdb b/GLM_cache/html/safeguard power analysis_521c3cb74e98afd655e0c6f0cfcaf014.rdb deleted file mode 100644 index 4cbd978..0000000 Binary files a/GLM_cache/html/safeguard power analysis_521c3cb74e98afd655e0c6f0cfcaf014.rdb and /dev/null differ diff --git a/GLM_cache/html/safeguard power analysis_521c3cb74e98afd655e0c6f0cfcaf014.rdx b/GLM_cache/html/safeguard power analysis_521c3cb74e98afd655e0c6f0cfcaf014.rdx deleted file mode 100644 index fe6c56e..0000000 Binary files a/GLM_cache/html/safeguard power analysis_521c3cb74e98afd655e0c6f0cfcaf014.rdx and /dev/null differ diff --git a/GLM_cache/html/simulate data_40fb632fdd9b8535087593249b94701a.RData b/GLM_cache/html/simulate data_40fb632fdd9b8535087593249b94701a.RData deleted file mode 100644 index 6348c63..0000000 Binary files a/GLM_cache/html/simulate data_40fb632fdd9b8535087593249b94701a.RData and /dev/null differ diff --git a/GLM_cache/html/simulate data_40fb632fdd9b8535087593249b94701a.rdb b/GLM_cache/html/simulate data_40fb632fdd9b8535087593249b94701a.rdb deleted file mode 100644 index 8815916..0000000 Binary files a/GLM_cache/html/simulate data_40fb632fdd9b8535087593249b94701a.rdb and /dev/null differ diff --git a/GLM_cache/html/simulate data_40fb632fdd9b8535087593249b94701a.rdx b/GLM_cache/html/simulate data_40fb632fdd9b8535087593249b94701a.rdx deleted file mode 100644 index dea5e65..0000000 Binary files a/GLM_cache/html/simulate data_40fb632fdd9b8535087593249b94701a.rdx and /dev/null differ diff --git a/GLM_cache/html/unnamed-chunk-1_6b661e39c6d5803e478c7576162cbab3.RData b/GLM_cache/html/unnamed-chunk-1_6b661e39c6d5803e478c7576162cbab3.RData deleted file mode 100644 index 0505140..0000000 Binary files a/GLM_cache/html/unnamed-chunk-1_6b661e39c6d5803e478c7576162cbab3.RData and /dev/null differ diff --git a/GLM_cache/html/unnamed-chunk-1_6b661e39c6d5803e478c7576162cbab3.rdb b/GLM_cache/html/unnamed-chunk-1_6b661e39c6d5803e478c7576162cbab3.rdb deleted file mode 100644 index 6358d31..0000000 Binary files a/GLM_cache/html/unnamed-chunk-1_6b661e39c6d5803e478c7576162cbab3.rdb and /dev/null differ diff --git a/GLM_cache/html/unnamed-chunk-1_6b661e39c6d5803e478c7576162cbab3.rdx b/GLM_cache/html/unnamed-chunk-1_6b661e39c6d5803e478c7576162cbab3.rdx deleted file mode 100644 index 425832e..0000000 Binary files a/GLM_cache/html/unnamed-chunk-1_6b661e39c6d5803e478c7576162cbab3.rdx and /dev/null differ diff --git a/GLM_files/figure-html/plot-1.png b/GLM_files/figure-html/plot-1.png deleted file mode 100644 index d5fc6e2..0000000 Binary files a/GLM_files/figure-html/plot-1.png and /dev/null differ diff --git a/GLM_files/figure-html/safeguard power analysis-1.png b/GLM_files/figure-html/safeguard power analysis-1.png deleted file mode 100644 index feecc44..0000000 Binary files a/GLM_files/figure-html/safeguard power analysis-1.png and /dev/null differ diff --git a/LM1.qmd b/LM1.qmd index 1de358b..f01e074 100644 --- a/LM1.qmd +++ b/LM1.qmd @@ -1,495 +1,495 @@ ---- -title: "Ch. 1: Linear Model 1: A single dichotomous predictor" -author: "Felix Schönbrodt" -execute: - cache: true -project-type: website ---- - -*Reading/working time: \~50 min.* - -```{r} -#| output: false -# Preparation: Install and load all necessary packages - -#install.packages(c("ggplot2", "ggdist", "gghalves", "pwr", "MBESS")) - -library(ggplot2) -library(ggdist) -library(gghalves) -library(pwr) -library(MBESS) -``` - -We start with the simplest possible linear model: (a) a continuous outcome variable is predicted by a single dichotomous predictor. This model actually [rephrases a t-test as a linear model](https://lindeloev.github.io/tests-as-linear/)! Then we build up increasingly complex models: (b) a single continuous predictor and (c) multiple continuous predictors (i.e., multiple regression). - -# Data = Model + Error - -The general equation for a linear model is: - -$$y = b_0 + b_1*x_1 + b_2*x_2 + ... + e$$ - -where $y$ is the continuous outcome variable, $b_0$ is the intercept, $x_1$ is the first predictor variable with its associated regression weight $b_1$, etc. Finally, $e$ is the error term which captures all variability in the outcome that is not explained by the predictor variables in the model. It is assumed that the error term is independently and identically distributed (iid) following a normal distribution with a mean of zero and a variance of $\sigma^2$: - -$$e \mathop{\sim}\limits^{\mathrm{iid}} N(mean=0, var=\sigma^2)$$ - -This assumption of the distribution of the errors is important for our simulations, as we will simulate the error according to that assumption. In an actual statistical analysis, the variance of the errors, $\sigma^2$, is estimated from the data. - -"i.i.d" means that the error of each observation is independent from all other observations. This assumption is violated, for example, when we look at romantic couples. If one partner is exceptionally distressed, then the other partner presumably will also have higher stress levels. In this case, the errors of both partners are correlated, and not independent any more. (See the section on [modelling multilevel data](LMM.qmd) for a statistical way of handling such interdependences). - -# A concrete example - -Let's fill the abstract symbols of Eq. 1 with some concrete content. Assume that we want to analyze the treatment effect of an intervention supposed to reduce depressive symptoms. In the study, a sample of participants with a diagnosed depression are randomized to either a control group or the treatment group. - -We have the following two variables in our data set: - -- `BDI` is the continuous outcome variable representing the severity of depressive symptoms after the treatment, assessed with the BDI-II inventory (higher values mean higher depression scores). -- `treatment` is our dichotomous predictor defining the random group assignment. We assign the following numbers: 0 = control group (e.g., a waiting list group), 1 = treatment group. - -This is an opportunity to think about the actual scale of our variables. At the end, when we simulate data we need to insert concrete numbers into our equations: - -- What BDI values would we expect on average in our sample (before treatment)? -- What variability would we expect in our sample? -- What average treatment effect would we expect? - -All of these values are needed to be able to simulate data, and the chosen numbers imply a certain effect size. - -## Get some real data as starting point - -::: callout-note -The creators of this tutorial are no experts in clinical psychology; we opportunistically selected open data sets based on their availability. Usually, we would look for meta-analyses - ideally bias-corrected - for more comprehensive evidence. -::: - -The R package `HSAUR` contains open data on 100 depressive patients, where 50 received treatment-as-usual (`TAU`) and 50 received a new treatment ("Beat the blues"; `BtheB`). Data was collected in a pre-post-design with several follow-up measurements. For the moment, we focus on the pre-treatment baseline value (`bdi.pre`) and the first post-treatment value (`bdi.2m`). We will use that data set as a "pilot study" for our power analysis. - -Note that this pilot data does *not* contain an inactive control group, such as the waiting list group that we assume for our planned study. Both the `BtheB` and the `TAU` group are active treatment groups. Nonetheless, we will be able to infer our treatment effect (vs. an inactive control group) from that data by looking at the pre-treatment data. - -```{r} -# the data can be found in the HSAUR package, must be installed first -#install.packages("HSAUR") - -# load the data -data("BtheB", package = "HSAUR") - -# get some information about the data set: -?HSAUR::BtheB - -hist(BtheB$bdi.pre) -``` - -The standardized cutoffs for the BDI are: - -- 0--13: minimal depression -- 14--19: mild depression -- 20--28: moderate depression -- 29--63: severe depression. - -Returning to our questions from above: - -**What BDI values would we expect on average in our sample before treatment?** - -```{r} -# we take the pre-score here: -mean(BtheB$bdi.pre) -``` - -The average BDI score before treatment was 23, corresponding to a "moderate depression". - -- What variability would we expect in our sample? - -```{r} -var(BtheB$bdi.pre) -``` - -- What average treatment effect would we expect? - -```{r} -# we take the 2 month follow-up measurement, -# separately for the "treatment as usual" and -# the "Beat the blues" group: -mean(BtheB$bdi.2m[BtheB$treatment == "TAU"], na.rm=TRUE) -mean(BtheB$bdi.2m[BtheB$treatment == "BtheB"]) -``` - -Hence, the two treatments reduced BDI scores from an average of 23 to 19 (`TAU`) and 15 (`BtheB`). Based on that data set, we can conclude that a typical treatment effect is somewhere between a 4 and a 8-point reduction of BDI scores.[^1] - -[^1]: By comparing only the post-treatment scores we used an unbiased causal model for the treatment effect. Later we will increase the precision of the estimate by including the baseline score for each participant into the model. - -For our purpose, we compute the average treatment effect combined for both treatments. The average post-treatment score is: - -```{r} -mean(BtheB$bdi.2m, na.rm=TRUE) -``` - -So, the average reduction across both treatments is $23-17=6$. In the following scripts, we'll use that value as our assumed treatment effect. - -## Enter specific values for the model parameters - -Let's rewrite the abstract equation with the specific variable names. We first write the equation for the systematic part (without the error term). This also represents the predicted value: - -$$\widehat{\text{BDI}} = b_0 + b_1*\text{treatment}$$ - -We use the notation $\widehat{\text{BDI}}$ (with a hat) to denote the predicted BDI score. - -The predicted score for the *control group* then simply is the intercept of the model, as the second term is erased by entering the value "0" for the control group: - -$$\widehat{\text{BDI}} = b_0 + b_1*0 = b_0$$ - -The predicted score for the *treatment group* is the value for the control group plus the regression weight: - -$$\widehat{\text{BDI}} = b_0 + b_1*1$$ Hence, the regression weight (aka. "slope parameter") $b_1$ estimates the mean difference between both groups, which is the treatment effect. - -With our knowledge from the open BDI data, we insert plausible values for the intercept $b_0$ and the treatment effect $b_1$. We expect a *reduction* of the depression score, so the treatment effect is assumed to be negative. We take the combined treatment effect of the two pilot treatments. And as power analysis is not rocket science, we generously round the values: - -$$\widehat{\text{BDI}} = 23 - 6*treatment$$ - -Hence, the predicted value is $23 - 6*0 = 23$ for the control group, and $23 - 6*1 = 17$ for the treatment group. - -With the current model, all participants in the control group have the same predicted value (23), as do all participants in the treatment group (17). - -As a final step, we add the random noise to the model, based on the variance in the pilot data: - -$$\text{BDI} = 23 - 6*treatment + e; e \sim N(0, var=117) $$ - -That's our final equation with assumed population parameters! With that equation, we assume a certain state of reality and can sample "virtual participants". - -## What is the effect size in the model? - -Researchers often have been trained to think in standardized effect sizes, such as Cohen's $d$, a correlation $r$, or other indices such as $f^2$ or partial $\eta^2$. In the simulation approach, we typical work on the raw scale of variables. - -The *raw* effect size is simply the treatment effect on the original BDI scale (i.e., the group difference in the outcome variable). In our case we assume that the treatment lowers the BDI score by 6 points, on average. Defining the raw effect requires some domain knowledge - you need to know your measurement scale, and you need to know what the values (and differences between values) mean. In our example, a reduction of 6 BDI points means that the average patient moves from a moderate depression (23 points) to a mild depression (17 points). Working with raw effect sizes forces you to think about your actual data (instead of plugging in content-free default standardized effect sizes), and enables you to do plausibility checks on your simulation. - -The *standardized* effect size relates the raw effect size to the unexplained error variance. In the two-group example, this can be expressed as Cohen's *d*, which is the mean difference divided by the standard deviation (SD): - -$$d = \frac{M_{treat} - M_{control}}{SD} = \frac{17 - 23}{\sqrt{117}} = -0.55$$ - -The standardized effect size always relates two components: The raw effect size (here: 6 points difference) and the error variance. Hence, you can increase the standardized effect size by (a) increasing the raw treatment effect, or (b) reducing the error variance. - -::: callout-note -If you look up the formula of Cohen's *d*, it typically uses the *pooled* SD from both groups. As we assumed that both groups have the same SD, we simply took that value. -::: - -```{r} -#| eval: false -#| include: false - -## Plausibility check (not run, not shown in output): -post <- na.omit(BtheB$bdi.2m) -pre <- na.omit(BtheB$bdi.pre) -f = c(rep("T", length(post)), rep("C", length(pre))) -d = c(post, pre) -cohen.d(d,f, na.rm=TRUE, pooled=TRUE, noncentral = FALSE) - -(mean(post) - mean(pre))/sd(d) -``` - -# Let's simulate! - -Once we committed to an equation with concrete parameter values, we can simulate data for a sample of, say, $n=100$ participants. Simply write down the equation and add random normal noise with the `rnorm` function. Note that the `rnorm` function takes the standard deviation (not the variance). Finally, set a seed so that the generated random numbers are reproducible: - -```{r} -set.seed(0xBEEF) - -# define all simulation parameters and predictor variables: -n <- 100 -treatment <- c(rep(0, n/2), rep(1, n/2)) # first 50 control, then 50 treatment - -# Write down the equation -BDI <- 23 - 6*treatment + rnorm(n, mean=0, sd=sqrt(117)) - -# combine all variables in one data frame -df <- data.frame(treatment, BDI) - -head(df) -tail(df) -``` - -Let's plot the simulated data with raincloud plots (see [here](https://z3tt.github.io/Rainclouds/) for a tutorial): - -```{r} -#| warning: false -ggplot(df, aes(x=as.factor(treatment), y=BDI)) + - ggdist::stat_halfeye(adjust = .5, width = .3, .width = 0, - justification = -.3, point_colour = NA) + - geom_boxplot(width = .1, outlier.shape = NA) + - gghalves::geom_half_point(side = "l", range_scale = .4, - alpha = .5) -``` - -This graph gives us an impression of the plausibility of our simulation: In the control group, we have a median BDI score of 26, and 50% of participants, represented by the box of the boxplot, are roughly between 18 and 32 points (i.e., in the range of a moderate depression). After the simulated treatment, the median BDI score is at 18 (mild depression). - -The plot also highlights an unrealistic aspect of our simulations: In reality, the BDI score is bounded to be \>=0; but our random data (sampled from a normal distribution) can generate values below zero. For our current power simulations this can be neglected (as negative values are quite rare), but in real data collection such floor or ceiling effects can impose a range restriction that might lower the statistical power. - -Let's assume that we collected and analyzed this specific sample - what would have been the results? - -```{r} -summary(lm(BDI ~ treatment, data=df)) -``` - -Here, and in the entire tutorial, we assume an $\alpha$-level of .005, as this provides more reasonable false positive rates and stronger evidence for new claims (Benjamin et al., [2018](https://doi.org/10.1038/s41562-017-0189-z)). - -As you can see, in this simulated sample (based on a specific seed), the treatment effect is not significant (p \> .02). But how likely are we to detect an effect with a sample of this size? - -## Doing the power analysis - -Now we need to repeatedly draw many samples and see how many of the analyses would have detected the existing effect. To do this, we put the code from above into a function called `sim`. We coded the function to either return the focal p-value (as default) or to print a model summary (helpful for debugging and testing the function). This function takes two parameters: - -- `n` defines the required sample size -- `treatment_effect` defines the treatment effect in the raw scale (i.e., reduction in BDI points) - -We then use the `replicate` function to repeatedly call the `sim` function for 1000 iterations. - -```{r} -#| echo: true -#| results: hide - -set.seed(0xBEEF) - -iterations <- 1000 # the number of Monte Carlo repetitions -n <- 100 # the size of our simulated sample - -sim1 <- function(n=100, treatment_effect=-6, print=FALSE) { - treatment <- c(rep(0, n/2), rep(1, n/2)) - BDI <- 23 + treatment_effect*treatment + rnorm(n, mean=0, sd=sqrt(117)) - - # this lm() call should be exactly the function that you use - # to analyse your real data set - res <- lm(BDI ~ treatment) - p_value <- summary(res)$coefficients["treatment", "Pr(>|t|)"] - - if (print==TRUE) print(summary(res)) - else return(p_value) -} - -# now run the sim() function a 1000 times and store the p-values in a vector: -p_values <- replicate(iterations, sim1(n=100)) -``` - -How many of our 1000 virtual samples would have found the effect? - -```{r} -table(p_values < .005) -``` - -Only `r round(sum(p_values < .005)*100/iterations)`% of samples with the same size of $n=100$ result in a significant p-value. - -`r round(sum(p_values < .005)*100/iterations)`% - that is our power for $\alpha = .005$, Cohen's $d=.55$, and $n=100$. - -## Sample size planning: Find the necessary sample size - -Now we know that a sample size of 100 does not lead to a sufficient power. But what sample size would we need to achieve a power of at least 80%? In the simulation approach you need to test different $n$s until you find the necessary sample size. We do this by wrapping the simulation code into a loop that continuously increases the n. We then store the computed power for each n. - -```{r} -#| echo: true -#| results: hide - -set.seed(0xBEEF) - -# define all predictor and simulation variables. -iterations <- 1000 -ns <- seq(100, 300, by=20) # test ns between 100 and 300 - -result <- data.frame() - -for (n in ns) { # loop through elements of the vector "ns" - p_values <- replicate(iterations, sim1(n=n)) - - result <- rbind(result, data.frame( - n = n, - power = sum(p_values < .005)/iterations) - ) - - # show the result after each run (not shown here in the tutorial) - print(result) -} -``` - -Let's plot there result: - -```{r} -ggplot(result, aes(x=n, y=power)) + geom_point() + geom_line() -``` - -Hence, with n=180 (90 in each group), we have a 80% chance to detect the effect. - -🥳 **Congratulations! You did your first power analysis by simulation.** 🎉 - -For these simple models, we can also compute analytic solutions. Let's verify our results with the `pwr` package - a linear regression with a single dichotomous predictor is equivalent to a t-test: - -```{r} -pwr.t.test(d = 0.55, sig.level = 0.005, power = .80) -``` - -Exactly the same result - phew 😅 - -# Sensitivity analyses - -Be aware that "pilot studies are next to worthless to estimate effect sizes" (see Brysbaert, [2019](http://doi.org/10.5334/joc.72), which has a section with that title). When we entered the specific numbers into our equations, we used point estimates from a relatively small pilot sample. As there is considerable uncertainty around these estimates, the true population value could be much smaller or larger. Furthermore, if the very reason for running a study is a significant (exploratory) finding in a small pilot study, your effect size estimate is probably upward biased (because you would have ignored the pilot study if there hadn't been a significant result). - -So, ideally, we base our power estimation on more reliable and/or more conservative prior information, or on theoretical considerations about the smallest effect size of interest. Therefore, we will explore two other ways to set the effect size: *safeguard power analysis* and the *smallest effect size of interest (SESOI)*. - -## Safeguard power analysis - -As sensitivity analysis, we will apply a safeguard power analysis (Perugini et al., [2014](https://doi.org/10.1177/1745691614528519)) that aims for the lower end of a two-sided 60% CI around the parameter of the treatment effect (the intercept is irrelevant). (Of course you can use any other value than 60%, but this is the value (tentatively) mentioned by the inventors of the safeguard power analysis.) - -::: callout-note -If you assume publication bias, another heuristic for aiming at a more realistic population effect size is the "divide-by-2" heuristic. (see [kickoff presentation](https://osf.io/bzxnj)) -::: - -We can use the `ci.smd` function from the `MBESS` package to compute a CI around Cohen's $d$ that we computed for our treatment effect: - -```{r} -ci.smd(smd=-0.55, n.1=50, n.2=50, conf.level=.60) -``` - -However, in the simulated regression equation, we need the *raw* effect size - so we have to backtransform the standardized confidence limits into the original metric. As the assumed effect is negative, we aim for the *upper*, i.e., the more conservative limit. After backtransformation in the raw metric, it is considerably smaller, at -4.1: - -$$d = \frac{M_{diff}}{SD} \Rightarrow M_{diff} = d*SD = -0.377 * \sqrt{117} = -4.08$$ - -Now we can rerun the power simulation with this more conservative value (the only change to the code above is that we changed the treatment effect from -6 to -4.1). - -```{r} -#| results: hide -#| code-fold: true -#| code-summary: "Show the code" - -set.seed(0xBEEF) - -# define all predictor and simulation variables. -iterations <- 1000 -ns <- seq(200, 400, by=20) # test ns between 200 and 400 - -result <- data.frame() - -for (n in ns) { # loop through elements of the vector "ns" - p_values <- replicate(iterations, sim1(n=n, treatment_effect = -4.1)) - - result <- rbind(result, data.frame( - n = n, - power = sum(p_values < .005)/iterations) - ) - - # show the result after each run - print(result) -} -``` - -```{r} -#| echo: false -print(result) -``` - -With that more conservative effect size assumption, we would need around 380 participants, i.e. 190 per group. - -## Smallest effect size of interest (SESOI) - -Many methodologists argue that we should not power for the *expected* effect size, but rather for the smallest effect size of interest (SESOI). In this case, a non-significant result can be interpreted as "We accept the $H_0$, and even if a real effect existed, it most likely is too small to be relevant". - -What change of BDI scores is perceived as "clinically important"? The hard part is to find a convincing theoretical or empirical argument for the chosen SESOI. In the case of the BDI, luckily someone else did that work. - -The [NICE guidance](https://www.nice.org.uk/guidance/qs8) suggest that a change of \>=3 BDI-II points is clinically important. - -However, as you can expect, things are more complicated. Button et al. ([2015](https://doi.org/10.1017/S0033291715001270)) analyzed data sets where patients have been asked, after a treatment, whether they felt "better", "the same" or "worse". With these subjective ratings, they could relate changes in BDI-II scores to perceived improvements. Hence, even when depressive symptoms were measurably reduced in the BDI, patients still might answer "feels the same", which indicates that the reduction did not surpass a threshold of subjective relevant improvement. But the minimal clinical importance depends on the baseline severity: For patients to feel notably better, they need *more* reduction of BDI-II scores if they start from a higher level of depressive symptoms. Following from this analysis, typical SESOIs are *higher* than the NICE guidelines, more in the range of -6 BDI points. - -For our example, let's use the NICE recommendation of -3 BDI points as a lower threshold for our power analysis (anything larger than that will be covered anyway). - -```{r} -#| results: hide -#| code-fold: true -#| code-summary: "Show the code" - -set.seed(0xBEEF) - -# define all predictor and simulation variables. -iterations <- 1000 - -# CHANGE: we adjusted the range of probed sample sizes upwards, as the effect size now is considerably smaller -ns <- seq(600, 800, by=20) - -result <- data.frame() - -for (n in ns) { - p_values <- replicate(iterations, sim1(n=n, treatment_effect = -3)) - - result <- rbind(result, data.frame( - n = n, - power = sum(p_values < .005)/iterations) - ) - - print(result) -} -``` - -```{r} -#| echo: false - -# show the results -print(result) -``` - -Hence, we need around 700 participants to reliably detect this smallest effect size of interest. - -Did you spot the strange pattern in the result? At n=720, the power is 83%, but only 82% with n=740? This is not possible, as power monotonically increases with sample size. It suggests that this is simply Monte Carlo sampling error - 1000 iterations are not enough to get precise estimates. When we increase iterations to 10,000, it takes much longer, but gives more precise results: - -```{r} -#| results: hide -#| code-fold: true -#| code-summary: "Show the code" - -set.seed(0xBEEF) - -# define all predictor and simulation variables. -iterations <- 10000 -ns <- seq(640, 740, by=20) - -result <- data.frame() - -for (n in ns) { - p_values <- replicate(iterations, sim1(n=n, treatment_effect = -3)) - - result <- rbind(result, data.frame( - n = n, - power = sum(p_values < .005)/iterations) - ) - - print(result) -} -``` - -```{r} -#| echo: false - -# show the results -print(result) -``` - -Now power increases monotonically with sample size, as expected.\ -To explore how many Monte-Carlo iterations are necessary to get stable computational results, see the bonus page [Bonus: How many Monte-Carlo iterations are necessary?](https://malikaihle.github.io/Simulations-for-Advanced-Power-Analyses/how_many_iterations.html) - -# Recap: What did we do? - -These are the steps we did in this part of the tutorial: - -1. **Define the statistical model that you will use to analyze your data** (*in our case: a linear regression*). This sometimes is called the *data generating mechanism* or the *data generating model*. -2. **Find empirical information about the parameters in the model.** These are typically mean values, variances, group differences, regression coefficients, or correlations (*in our case: a mean value (before treatment), a group difference, and an error variance*). These numbers define the assumed state of the population. -3. **Program your regression equation**, which consists of a systematic (i.e., deterministic) part and (one or more) random error terms. -4. **Do the simulation**: - a) Repeatedly sample data from your equation (only the random error changes in each run, the deterministic part stays the same) - b) Compute the index of interest (typically: the p-value of a focal effect) - c) Count how many samples would have detected the effect (i.e., the statistical power) - d) Tune your simulation parameters until the desired level of power is achieved. - -Concerning step 4d, the typical scenario is that you increase sample size until your desired level of power is achieved. But you could imagine other ways to increase power, e.g.: - -- Increase the reliability of a measurement instrument (which will be reflected in a smaller error variance) until desired power is achieved. (This could relate to a trade-off: Do you invest more effort and time in better measurement, or do you keep an unreliable measure and collect more participants?) -- Compare a within- and a between-design in simulations - -# References - -Benjamin, D. J. et al. (2018). Redefine statistical significance. Nature Human Behaviour, 2(1), 6--10. - -Brysbaert, M. (2019). How many participants do we have to include in properly powered experiments? A tutorial of power analysis with reference tables. Journal of Cognition, 2(1), 16. DOI: - -Button, K. S., Kounali, D., Thomas, L., Wiles, N. J., Peters, T. J., Welton, N. J., Ades, A. E., & Lewis, G. (2015). Minimal clinically important difference on the Beck Depression Inventory---II according to the patient's perspective. Psychological Medicine, 45(15), 3269--3279. - -Perugini, M., Gallucci, M., & Costantini, G. (2014). Safeguard power as a protection against imprecise power estimates. Perspectives on Psychological Science, 9(3), 319--332. +--- +title: "Ch. 1: Linear Model 1: A single dichotomous predictor" +author: "Felix Schönbrodt" +execute: + cache: true +project-type: website +--- + +*Reading/working time: \~50 min.* + +```{r} +#| output: false +# Preparation: Install and load all necessary packages + +#install.packages(c("ggplot2", "ggdist", "gghalves", "pwr", "MBESS")) + +library(ggplot2) +library(ggdist) +library(gghalves) +library(pwr) +library(MBESS) +``` + +We start with the simplest possible linear model: (a) a continuous outcome variable is predicted by a single dichotomous predictor. This model actually [rephrases a t-test as a linear model](https://lindeloev.github.io/tests-as-linear/)! Then we build up increasingly complex models: (b) a single continuous predictor and (c) multiple continuous predictors (i.e., multiple regression). + +# Data = Model + Error + +The general equation for a linear model is: + +$$y = b_0 + b_1*x_1 + b_2*x_2 + ... + e$$ + +where $y$ is the continuous outcome variable, $b_0$ is the intercept, $x_1$ is the first predictor variable with its associated regression weight $b_1$, etc. Finally, $e$ is the error term which captures all variability in the outcome that is not explained by the predictor variables in the model. It is assumed that the error term is independently and identically distributed (iid) following a normal distribution with a mean of zero and a variance of $\sigma^2$: + +$$e \mathop{\sim}\limits^{\mathrm{iid}} N(mean=0, var=\sigma^2)$$ + +This assumption of the distribution of the errors is important for our simulations, as we will simulate the error according to that assumption. In an actual statistical analysis, the variance of the errors, $\sigma^2$, is estimated from the data. + +"i.i.d" means that the error of each observation is independent from all other observations. This assumption is violated, for example, when we look at romantic couples. If one partner is exceptionally distressed, then the other partner presumably will also have higher stress levels. In this case, the errors of both partners are correlated, and not independent any more. (See the section on [modelling multilevel data](LMM.qmd) for a statistical way of handling such interdependences). + +# A concrete example + +Let's fill the abstract symbols of Eq. 1 with some concrete content. Assume that we want to analyze the treatment effect of an intervention supposed to reduce depressive symptoms. In the study, a sample of participants with a diagnosed depression are randomized to either a control group or the treatment group. + +We have the following two variables in our data set: + +- `BDI` is the continuous outcome variable representing the severity of depressive symptoms after the treatment, assessed with the BDI-II inventory (higher values mean higher depression scores). +- `treatment` is our dichotomous predictor defining the random group assignment. We assign the following numbers: 0 = control group (e.g., a waiting list group), 1 = treatment group. + +This is an opportunity to think about the actual scale of our variables. At the end, when we simulate data we need to insert concrete numbers into our equations: + +- What BDI values would we expect on average in our sample (before treatment)? +- What variability would we expect in our sample? +- What average treatment effect would we expect? + +All of these values are needed to be able to simulate data, and the chosen numbers imply a certain effect size. + +## Get some real data as starting point + +::: callout-note +The creators of this tutorial are no experts in clinical psychology; we opportunistically selected open data sets based on their availability. Usually, we would look for meta-analyses - ideally bias-corrected - for more comprehensive evidence. +::: + +The R package `HSAUR` contains open data on 100 depressive patients, where 50 received treatment-as-usual (`TAU`) and 50 received a new treatment ("Beat the blues"; `BtheB`). Data was collected in a pre-post-design with several follow-up measurements. For the moment, we focus on the pre-treatment baseline value (`bdi.pre`) and the first post-treatment value (`bdi.2m`). We will use that data set as a "pilot study" for our power analysis. + +Note that this pilot data does *not* contain an inactive control group, such as the waiting list group that we assume for our planned study. Both the `BtheB` and the `TAU` group are active treatment groups. Nonetheless, we will be able to infer our treatment effect (vs. an inactive control group) from that data by looking at the pre-treatment data. + +```{r} +# the data can be found in the HSAUR package, must be installed first +#install.packages("HSAUR") + +# load the data +data("BtheB", package = "HSAUR") + +# get some information about the data set: +?HSAUR::BtheB + +hist(BtheB$bdi.pre) +``` + +The standardized cutoffs for the BDI are: + +- 0--13: minimal depression +- 14--19: mild depression +- 20--28: moderate depression +- 29--63: severe depression. + +Returning to our questions from above: + +**What BDI values would we expect on average in our sample before treatment?** + +```{r} +# we take the pre-score here: +mean(BtheB$bdi.pre) +``` + +The average BDI score before treatment was 23, corresponding to a "moderate depression". + +- What variability would we expect in our sample? + +```{r} +var(BtheB$bdi.pre) +``` + +- What average treatment effect would we expect? + +```{r} +# we take the 2 month follow-up measurement, +# separately for the "treatment as usual" and +# the "Beat the blues" group: +mean(BtheB$bdi.2m[BtheB$treatment == "TAU"], na.rm=TRUE) +mean(BtheB$bdi.2m[BtheB$treatment == "BtheB"]) +``` + +Hence, the two treatments reduced BDI scores from an average of 23 to 19 (`TAU`) and 15 (`BtheB`). Based on that data set, we can conclude that a typical treatment effect is somewhere between a 4 and a 8-point reduction of BDI scores.[^1] + +[^1]: By comparing only the post-treatment scores we used an unbiased causal model for the treatment effect. Later we will increase the precision of the estimate by including the baseline score for each participant into the model. + +For our purpose, we compute the average treatment effect combined for both treatments. The average post-treatment score is: + +```{r} +mean(BtheB$bdi.2m, na.rm=TRUE) +``` + +So, the average reduction across both treatments is $23-17=6$. In the following scripts, we'll use that value as our assumed treatment effect. + +## Enter specific values for the model parameters + +Let's rewrite the abstract equation with the specific variable names. We first write the equation for the systematic part (without the error term). This also represents the predicted value: + +$$\widehat{\text{BDI}} = b_0 + b_1*\text{treatment}$$ + +We use the notation $\widehat{\text{BDI}}$ (with a hat) to denote the predicted BDI score. + +The predicted score for the *control group* then simply is the intercept of the model, as the second term is erased by entering the value "0" for the control group: + +$$\widehat{\text{BDI}} = b_0 + b_1*0 = b_0$$ + +The predicted score for the *treatment group* is the value for the control group plus the regression weight: + +$$\widehat{\text{BDI}} = b_0 + b_1*1$$ Hence, the regression weight (aka. "slope parameter") $b_1$ estimates the mean difference between both groups, which is the treatment effect. + +With our knowledge from the open BDI data, we insert plausible values for the intercept $b_0$ and the treatment effect $b_1$. We expect a *reduction* of the depression score, so the treatment effect is assumed to be negative. We take the combined treatment effect of the two pilot treatments. And as power analysis is not rocket science, we generously round the values: + +$$\widehat{\text{BDI}} = 23 - 6*treatment$$ + +Hence, the predicted value is $23 - 6*0 = 23$ for the control group, and $23 - 6*1 = 17$ for the treatment group. + +With the current model, all participants in the control group have the same predicted value (23), as do all participants in the treatment group (17). + +As a final step, we add the random noise to the model, based on the variance in the pilot data: + +$$\text{BDI} = 23 - 6*treatment + e; e \sim N(0, var=117) $$ + +That's our final equation with assumed population parameters! With that equation, we assume a certain state of reality and can sample "virtual participants". + +## What is the effect size in the model? + +Researchers often have been trained to think in standardized effect sizes, such as Cohen's $d$, a correlation $r$, or other indices such as $f^2$ or partial $\eta^2$. In the simulation approach, we typical work on the raw scale of variables. + +The *raw* effect size is simply the treatment effect on the original BDI scale (i.e., the group difference in the outcome variable). In our case we assume that the treatment lowers the BDI score by 6 points, on average. Defining the raw effect requires some domain knowledge - you need to know your measurement scale, and you need to know what the values (and differences between values) mean. In our example, a reduction of 6 BDI points means that the average patient moves from a moderate depression (23 points) to a mild depression (17 points). Working with raw effect sizes forces you to think about your actual data (instead of plugging in content-free default standardized effect sizes), and enables you to do plausibility checks on your simulation. + +The *standardized* effect size relates the raw effect size to the unexplained error variance. In the two-group example, this can be expressed as Cohen's *d*, which is the mean difference divided by the standard deviation (SD): + +$$d = \frac{M_{treat} - M_{control}}{SD} = \frac{17 - 23}{\sqrt{117}} = -0.55$$ + +The standardized effect size always relates two components: The raw effect size (here: 6 points difference) and the error variance. Hence, you can increase the standardized effect size by (a) increasing the raw treatment effect, or (b) reducing the error variance. + +::: callout-note +If you look up the formula of Cohen's *d*, it typically uses the *pooled* SD from both groups. As we assumed that both groups have the same SD, we simply took that value. +::: + +```{r} +#| eval: false +#| include: false + +## Plausibility check (not run, not shown in output): +post <- na.omit(BtheB$bdi.2m) +pre <- na.omit(BtheB$bdi.pre) +f = c(rep("T", length(post)), rep("C", length(pre))) +d = c(post, pre) +cohen.d(d,f, na.rm=TRUE, pooled=TRUE, noncentral = FALSE) + +(mean(post) - mean(pre))/sd(d) +``` + +# Let's simulate! + +Once we committed to an equation with concrete parameter values, we can simulate data for a sample of, say, $n=100$ participants. Simply write down the equation and add random normal noise with the `rnorm` function. Note that the `rnorm` function takes the standard deviation (not the variance). Finally, set a seed so that the generated random numbers are reproducible: + +```{r} +set.seed(0xBEEF) + +# define all simulation parameters and predictor variables: +n <- 100 +treatment <- c(rep(0, n/2), rep(1, n/2)) # first 50 control, then 50 treatment + +# Write down the equation +BDI <- 23 - 6*treatment + rnorm(n, mean=0, sd=sqrt(117)) + +# combine all variables in one data frame +df <- data.frame(treatment, BDI) + +head(df) +tail(df) +``` + +Let's plot the simulated data with raincloud plots (see [here](https://z3tt.github.io/Rainclouds/) for a tutorial): + +```{r} +#| warning: false +ggplot(df, aes(x=as.factor(treatment), y=BDI)) + + ggdist::stat_halfeye(adjust = .5, width = .3, .width = 0, + justification = -.3, point_colour = NA) + + geom_boxplot(width = .1, outlier.shape = NA) + + gghalves::geom_half_point(side = "l", range_scale = .4, + alpha = .5) +``` + +This graph gives us an impression of the plausibility of our simulation: In the control group, we have a median BDI score of 26, and 50% of participants, represented by the box of the boxplot, are roughly between 18 and 32 points (i.e., in the range of a moderate depression). After the simulated treatment, the median BDI score is at 18 (mild depression). + +The plot also highlights an unrealistic aspect of our simulations: In reality, the BDI score is bounded to be \>=0; but our random data (sampled from a normal distribution) can generate values below zero. For our current power simulations this can be neglected (as negative values are quite rare), but in real data collection such floor or ceiling effects can impose a range restriction that might lower the statistical power. + +Let's assume that we collected and analyzed this specific sample - what would have been the results? + +```{r} +summary(lm(BDI ~ treatment, data=df)) +``` + +Here, and in the entire tutorial, we assume an $\alpha$-level of .005, as this provides more reasonable false positive rates and stronger evidence for new claims (Benjamin et al., [2018](https://doi.org/10.1038/s41562-017-0189-z)). + +As you can see, in this simulated sample (based on a specific seed), the treatment effect is not significant (p \> .02). But how likely are we to detect an effect with a sample of this size? + +## Doing the power analysis + +Now we need to repeatedly draw many samples and see how many of the analyses would have detected the existing effect. To do this, we put the code from above into a function called `sim`. We coded the function to either return the focal p-value (as default) or to print a model summary (helpful for debugging and testing the function). This function takes two parameters: + +- `n` defines the required sample size +- `treatment_effect` defines the treatment effect in the raw scale (i.e., reduction in BDI points) + +We then use the `replicate` function to repeatedly call the `sim` function for 1000 iterations. + +```{r} +#| echo: true +#| results: hide + +set.seed(0xBEEF) + +iterations <- 1000 # the number of Monte Carlo repetitions +n <- 100 # the size of our simulated sample + +sim1 <- function(n=100, treatment_effect=-6, print=FALSE) { + treatment <- c(rep(0, n/2), rep(1, n/2)) + BDI <- 23 + treatment_effect*treatment + rnorm(n, mean=0, sd=sqrt(117)) + + # this lm() call should be exactly the function that you use + # to analyse your real data set + res <- lm(BDI ~ treatment) + p_value <- summary(res)$coefficients["treatment", "Pr(>|t|)"] + + if (print==TRUE) print(summary(res)) + else return(p_value) +} + +# now run the sim() function a 1000 times and store the p-values in a vector: +p_values <- replicate(iterations, sim1(n=100)) +``` + +How many of our 1000 virtual samples would have found the effect? + +```{r} +table(p_values < .005) +``` + +Only `r round(sum(p_values < .005)*100/iterations)`% of samples with the same size of $n=100$ result in a significant p-value. + +`r round(sum(p_values < .005)*100/iterations)`% - that is our power for $\alpha = .005$, Cohen's $d=.55$, and $n=100$. + +## Sample size planning: Find the necessary sample size + +Now we know that a sample size of 100 does not lead to a sufficient power. But what sample size would we need to achieve a power of at least 80%? In the simulation approach you need to test different $n$s until you find the necessary sample size. We do this by wrapping the simulation code into a loop that continuously increases the n. We then store the computed power for each n. + +```{r} +#| echo: true +#| results: hide + +set.seed(0xBEEF) + +# define all predictor and simulation variables. +iterations <- 1000 +ns <- seq(100, 300, by=20) # test ns between 100 and 300 + +result <- data.frame() + +for (n in ns) { # loop through elements of the vector "ns" + p_values <- replicate(iterations, sim1(n=n)) + + result <- rbind(result, data.frame( + n = n, + power = sum(p_values < .005)/iterations) + ) + + # show the result after each run (not shown here in the tutorial) + print(result) +} +``` + +Let's plot there result: + +```{r} +ggplot(result, aes(x=n, y=power)) + geom_point() + geom_line() +``` + +Hence, with n=180 (90 in each group), we have a 80% chance to detect the effect. + +🥳 **Congratulations! You did your first power analysis by simulation.** 🎉 + +For these simple models, we can also compute analytic solutions. Let's verify our results with the `pwr` package - a linear regression with a single dichotomous predictor is equivalent to a t-test: + +```{r} +pwr.t.test(d = 0.55, sig.level = 0.005, power = .80) +``` + +Exactly the same result - phew 😅 + +# Sensitivity analyses + +Be aware that "pilot studies are next to worthless to estimate effect sizes" (see Brysbaert, [2019](http://doi.org/10.5334/joc.72), which has a section with that title). When we entered the specific numbers into our equations, we used point estimates from a relatively small pilot sample. As there is considerable uncertainty around these estimates, the true population value could be much smaller or larger. Furthermore, if the very reason for running a study is a significant (exploratory) finding in a small pilot study, your effect size estimate is probably upward biased (because you would have ignored the pilot study if there hadn't been a significant result). + +So, ideally, we base our power estimation on more reliable and/or more conservative prior information, or on theoretical considerations about the smallest effect size of interest. Therefore, we will explore two other ways to set the effect size: *safeguard power analysis* and the *smallest effect size of interest (SESOI)*. + +## Safeguard power analysis + +As sensitivity analysis, we will apply a safeguard power analysis (Perugini et al., [2014](https://doi.org/10.1177/1745691614528519)) that aims for the lower end of a two-sided 60% CI around the parameter of the treatment effect (the intercept is irrelevant). (Of course you can use any other value than 60%, but this is the value (tentatively) mentioned by the inventors of the safeguard power analysis.) + +::: callout-note +If you assume publication bias, another heuristic for aiming at a more realistic population effect size is the "divide-by-2" heuristic. (see [kickoff presentation](https://osf.io/bzxnj)) +::: + +We can use the `ci.smd` function from the `MBESS` package to compute a CI around Cohen's $d$ that we computed for our treatment effect: + +```{r} +ci.smd(smd=-0.55, n.1=50, n.2=50, conf.level=.60) +``` + +However, in the simulated regression equation, we need the *raw* effect size - so we have to backtransform the standardized confidence limits into the original metric. As the assumed effect is negative, we aim for the *upper*, i.e., the more conservative limit. After backtransformation in the raw metric, it is considerably smaller, at -4.1: + +$$d = \frac{M_{diff}}{SD} \Rightarrow M_{diff} = d*SD = -0.377 * \sqrt{117} = -4.08$$ + +Now we can rerun the power simulation with this more conservative value (the only change to the code above is that we changed the treatment effect from -6 to -4.1). + +```{r} +#| results: hide +#| code-fold: true +#| code-summary: "Show the code" + +set.seed(0xBEEF) + +# define all predictor and simulation variables. +iterations <- 1000 +ns <- seq(200, 400, by=20) # test ns between 200 and 400 + +result <- data.frame() + +for (n in ns) { # loop through elements of the vector "ns" + p_values <- replicate(iterations, sim1(n=n, treatment_effect = -4.1)) + + result <- rbind(result, data.frame( + n = n, + power = sum(p_values < .005)/iterations) + ) + + # show the result after each run + print(result) +} +``` + +```{r} +#| echo: false +print(result) +``` + +With that more conservative effect size assumption, we would need around 380 participants, i.e. 190 per group. + +## Smallest effect size of interest (SESOI) + +Many methodologists argue that we should not power for the *expected* effect size, but rather for the smallest effect size of interest (SESOI). In this case, a non-significant result can be interpreted as "We accept the $H_0$, and even if a real effect existed, it most likely is too small to be relevant". + +What change of BDI scores is perceived as "clinically important"? The hard part is to find a convincing theoretical or empirical argument for the chosen SESOI. In the case of the BDI, luckily someone else did that work. + +The [NICE guidance](https://www.nice.org.uk/guidance/qs8) suggest that a change of \>=3 BDI-II points is clinically important. + +However, as you can expect, things are more complicated. Button et al. ([2015](https://doi.org/10.1017/S0033291715001270)) analyzed data sets where patients have been asked, after a treatment, whether they felt "better", "the same" or "worse". With these subjective ratings, they could relate changes in BDI-II scores to perceived improvements. Hence, even when depressive symptoms were measurably reduced in the BDI, patients still might answer "feels the same", which indicates that the reduction did not surpass a threshold of subjective relevant improvement. But the minimal clinical importance depends on the baseline severity: For patients to feel notably better, they need *more* reduction of BDI-II scores if they start from a higher level of depressive symptoms. Following from this analysis, typical SESOIs are *higher* than the NICE guidelines, more in the range of -6 BDI points. + +For our example, let's use the NICE recommendation of -3 BDI points as a lower threshold for our power analysis (anything larger than that will be covered anyway). + +```{r} +#| results: hide +#| code-fold: true +#| code-summary: "Show the code" + +set.seed(0xBEEF) + +# define all predictor and simulation variables. +iterations <- 1000 + +# CHANGE: we adjusted the range of probed sample sizes upwards, as the effect size now is considerably smaller +ns <- seq(600, 800, by=20) + +result <- data.frame() + +for (n in ns) { + p_values <- replicate(iterations, sim1(n=n, treatment_effect = -3)) + + result <- rbind(result, data.frame( + n = n, + power = sum(p_values < .005)/iterations) + ) + + print(result) +} +``` + +```{r} +#| echo: false + +# show the results +print(result) +``` + +Hence, we need around 700 participants to reliably detect this smallest effect size of interest. + +Did you spot the strange pattern in the result? At n=720, the power is 83%, but only 82% with n=740? This is not possible, as power monotonically increases with sample size. It suggests that this is simply Monte Carlo sampling error - 1000 iterations are not enough to get precise estimates. When we increase iterations to 10,000, it takes much longer, but gives more precise results: + +```{r} +#| results: hide +#| code-fold: true +#| code-summary: "Show the code" + +set.seed(0xBEEF) + +# define all predictor and simulation variables. +iterations <- 10000 +ns <- seq(640, 740, by=20) + +result <- data.frame() + +for (n in ns) { + p_values <- replicate(iterations, sim1(n=n, treatment_effect = -3)) + + result <- rbind(result, data.frame( + n = n, + power = sum(p_values < .005)/iterations) + ) + + print(result) +} +``` + +```{r} +#| echo: false + +# show the results +print(result) +``` + +Now power increases monotonically with sample size, as expected.\ +To explore how many Monte-Carlo iterations are necessary to get stable computational results, see the bonus page [Bonus: How many Monte-Carlo iterations are necessary?](https://lmu-osc.github.io/Simulations-for-Advanced-Power-Analyses/how_many_iterations.html) + +# Recap: What did we do? + +These are the steps we did in this part of the tutorial: + +1. **Define the statistical model that you will use to analyze your data** (*in our case: a linear regression*). This sometimes is called the *data generating mechanism* or the *data generating model*. +2. **Find empirical information about the parameters in the model.** These are typically mean values, variances, group differences, regression coefficients, or correlations (*in our case: a mean value (before treatment), a group difference, and an error variance*). These numbers define the assumed state of the population. +3. **Program your regression equation**, which consists of a systematic (i.e., deterministic) part and (one or more) random error terms. +4. **Do the simulation**: + a) Repeatedly sample data from your equation (only the random error changes in each run, the deterministic part stays the same) + b) Compute the index of interest (typically: the p-value of a focal effect) + c) Count how many samples would have detected the effect (i.e., the statistical power) + d) Tune your simulation parameters until the desired level of power is achieved. + +Concerning step 4d, the typical scenario is that you increase sample size until your desired level of power is achieved. But you could imagine other ways to increase power, e.g.: + +- Increase the reliability of a measurement instrument (which will be reflected in a smaller error variance) until desired power is achieved. (This could relate to a trade-off: Do you invest more effort and time in better measurement, or do you keep an unreliable measure and collect more participants?) +- Compare a within- and a between-design in simulations + +# References + +Benjamin, D. J. et al. (2018). Redefine statistical significance. Nature Human Behaviour, 2(1), 6--10. + +Brysbaert, M. (2019). How many participants do we have to include in properly powered experiments? A tutorial of power analysis with reference tables. Journal of Cognition, 2(1), 16. DOI: + +Button, K. S., Kounali, D., Thomas, L., Wiles, N. J., Peters, T. J., Welton, N. J., Ades, A. E., & Lewis, G. (2015). Minimal clinically important difference on the Beck Depression Inventory---II according to the patient's perspective. Psychological Medicine, 45(15), 3269--3279. + +Perugini, M., Gallucci, M., & Costantini, G. (2014). Safeguard power as a protection against imprecise power estimates. Perspectives on Psychological Science, 9(3), 319--332. diff --git a/LM1_cache/html/__packages b/LM1_cache/html/__packages deleted file mode 100644 index c579272..0000000 --- a/LM1_cache/html/__packages +++ /dev/null @@ -1,7 +0,0 @@ -colorDF -prettycode -ggplot2 -ggdist -gghalves -pwr -MBESS diff --git a/LM1_cache/html/unnamed-chunk-10_b18d07efefc915c06b83aac62b22c692.RData b/LM1_cache/html/unnamed-chunk-10_b18d07efefc915c06b83aac62b22c692.RData deleted file mode 100644 index 947a16c..0000000 Binary files a/LM1_cache/html/unnamed-chunk-10_b18d07efefc915c06b83aac62b22c692.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-10_b18d07efefc915c06b83aac62b22c692.rdb b/LM1_cache/html/unnamed-chunk-10_b18d07efefc915c06b83aac62b22c692.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-10_b18d07efefc915c06b83aac62b22c692.rdx b/LM1_cache/html/unnamed-chunk-10_b18d07efefc915c06b83aac62b22c692.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-10_b18d07efefc915c06b83aac62b22c692.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-11_7d94f5ddc83a1e04955cc432f8083a30.RData b/LM1_cache/html/unnamed-chunk-11_7d94f5ddc83a1e04955cc432f8083a30.RData deleted file mode 100644 index fb43d41..0000000 Binary files a/LM1_cache/html/unnamed-chunk-11_7d94f5ddc83a1e04955cc432f8083a30.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-11_7d94f5ddc83a1e04955cc432f8083a30.rdb b/LM1_cache/html/unnamed-chunk-11_7d94f5ddc83a1e04955cc432f8083a30.rdb deleted file mode 100644 index 85bd17a..0000000 Binary files a/LM1_cache/html/unnamed-chunk-11_7d94f5ddc83a1e04955cc432f8083a30.rdb and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-11_7d94f5ddc83a1e04955cc432f8083a30.rdx b/LM1_cache/html/unnamed-chunk-11_7d94f5ddc83a1e04955cc432f8083a30.rdx deleted file mode 100644 index bdcef79..0000000 Binary files a/LM1_cache/html/unnamed-chunk-11_7d94f5ddc83a1e04955cc432f8083a30.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-12_2a372e209b054068e2fc099c421db0fc.RData b/LM1_cache/html/unnamed-chunk-12_2a372e209b054068e2fc099c421db0fc.RData deleted file mode 100644 index a1922a8..0000000 Binary files a/LM1_cache/html/unnamed-chunk-12_2a372e209b054068e2fc099c421db0fc.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-12_2a372e209b054068e2fc099c421db0fc.rdb b/LM1_cache/html/unnamed-chunk-12_2a372e209b054068e2fc099c421db0fc.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-12_2a372e209b054068e2fc099c421db0fc.rdx b/LM1_cache/html/unnamed-chunk-12_2a372e209b054068e2fc099c421db0fc.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-12_2a372e209b054068e2fc099c421db0fc.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-13_be151543c03de5e2c599e69f4f1209aa.RData b/LM1_cache/html/unnamed-chunk-13_be151543c03de5e2c599e69f4f1209aa.RData deleted file mode 100644 index 1f24b3f..0000000 Binary files a/LM1_cache/html/unnamed-chunk-13_be151543c03de5e2c599e69f4f1209aa.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-13_be151543c03de5e2c599e69f4f1209aa.rdb b/LM1_cache/html/unnamed-chunk-13_be151543c03de5e2c599e69f4f1209aa.rdb deleted file mode 100644 index 4a01b96..0000000 Binary files a/LM1_cache/html/unnamed-chunk-13_be151543c03de5e2c599e69f4f1209aa.rdb and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-13_be151543c03de5e2c599e69f4f1209aa.rdx b/LM1_cache/html/unnamed-chunk-13_be151543c03de5e2c599e69f4f1209aa.rdx deleted file mode 100644 index 507df6d..0000000 Binary files a/LM1_cache/html/unnamed-chunk-13_be151543c03de5e2c599e69f4f1209aa.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-14_247647c7f0e4a6bf345e75e3d9489528.RData b/LM1_cache/html/unnamed-chunk-14_247647c7f0e4a6bf345e75e3d9489528.RData deleted file mode 100644 index 1627f94..0000000 Binary files a/LM1_cache/html/unnamed-chunk-14_247647c7f0e4a6bf345e75e3d9489528.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-14_247647c7f0e4a6bf345e75e3d9489528.rdb b/LM1_cache/html/unnamed-chunk-14_247647c7f0e4a6bf345e75e3d9489528.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-14_247647c7f0e4a6bf345e75e3d9489528.rdx b/LM1_cache/html/unnamed-chunk-14_247647c7f0e4a6bf345e75e3d9489528.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-14_247647c7f0e4a6bf345e75e3d9489528.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-15_31aa70270a344a5cba8f503ba13fa52d.RData b/LM1_cache/html/unnamed-chunk-15_31aa70270a344a5cba8f503ba13fa52d.RData deleted file mode 100644 index a767899..0000000 Binary files a/LM1_cache/html/unnamed-chunk-15_31aa70270a344a5cba8f503ba13fa52d.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-15_31aa70270a344a5cba8f503ba13fa52d.rdb b/LM1_cache/html/unnamed-chunk-15_31aa70270a344a5cba8f503ba13fa52d.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-15_31aa70270a344a5cba8f503ba13fa52d.rdx b/LM1_cache/html/unnamed-chunk-15_31aa70270a344a5cba8f503ba13fa52d.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-15_31aa70270a344a5cba8f503ba13fa52d.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-16_15f4a2a67acac5a73198a892068a3990.RData b/LM1_cache/html/unnamed-chunk-16_15f4a2a67acac5a73198a892068a3990.RData deleted file mode 100644 index aa5ebc2..0000000 Binary files a/LM1_cache/html/unnamed-chunk-16_15f4a2a67acac5a73198a892068a3990.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-16_15f4a2a67acac5a73198a892068a3990.rdb b/LM1_cache/html/unnamed-chunk-16_15f4a2a67acac5a73198a892068a3990.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-16_15f4a2a67acac5a73198a892068a3990.rdx b/LM1_cache/html/unnamed-chunk-16_15f4a2a67acac5a73198a892068a3990.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-16_15f4a2a67acac5a73198a892068a3990.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-17_5b1d8ea52a60dca4dad02fcba95ad542.RData b/LM1_cache/html/unnamed-chunk-17_5b1d8ea52a60dca4dad02fcba95ad542.RData deleted file mode 100644 index 766cf7c..0000000 Binary files a/LM1_cache/html/unnamed-chunk-17_5b1d8ea52a60dca4dad02fcba95ad542.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-17_5b1d8ea52a60dca4dad02fcba95ad542.rdb b/LM1_cache/html/unnamed-chunk-17_5b1d8ea52a60dca4dad02fcba95ad542.rdb deleted file mode 100644 index 9aa9933..0000000 Binary files a/LM1_cache/html/unnamed-chunk-17_5b1d8ea52a60dca4dad02fcba95ad542.rdb and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-17_5b1d8ea52a60dca4dad02fcba95ad542.rdx b/LM1_cache/html/unnamed-chunk-17_5b1d8ea52a60dca4dad02fcba95ad542.rdx deleted file mode 100644 index ebb9017..0000000 Binary files a/LM1_cache/html/unnamed-chunk-17_5b1d8ea52a60dca4dad02fcba95ad542.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-18_1f0c12ae0448bc5da09c76e4e6817a43.RData b/LM1_cache/html/unnamed-chunk-18_1f0c12ae0448bc5da09c76e4e6817a43.RData deleted file mode 100644 index 1fb4883..0000000 Binary files a/LM1_cache/html/unnamed-chunk-18_1f0c12ae0448bc5da09c76e4e6817a43.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-18_1f0c12ae0448bc5da09c76e4e6817a43.rdb b/LM1_cache/html/unnamed-chunk-18_1f0c12ae0448bc5da09c76e4e6817a43.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-18_1f0c12ae0448bc5da09c76e4e6817a43.rdx b/LM1_cache/html/unnamed-chunk-18_1f0c12ae0448bc5da09c76e4e6817a43.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-18_1f0c12ae0448bc5da09c76e4e6817a43.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-19_103cbf44403df7f5a487feb8874401b7.RData b/LM1_cache/html/unnamed-chunk-19_103cbf44403df7f5a487feb8874401b7.RData deleted file mode 100644 index 7310d1b..0000000 Binary files a/LM1_cache/html/unnamed-chunk-19_103cbf44403df7f5a487feb8874401b7.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-19_103cbf44403df7f5a487feb8874401b7.rdb b/LM1_cache/html/unnamed-chunk-19_103cbf44403df7f5a487feb8874401b7.rdb deleted file mode 100644 index c265125..0000000 Binary files a/LM1_cache/html/unnamed-chunk-19_103cbf44403df7f5a487feb8874401b7.rdb and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-19_103cbf44403df7f5a487feb8874401b7.rdx b/LM1_cache/html/unnamed-chunk-19_103cbf44403df7f5a487feb8874401b7.rdx deleted file mode 100644 index 9c8a359..0000000 Binary files a/LM1_cache/html/unnamed-chunk-19_103cbf44403df7f5a487feb8874401b7.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-1_cc8cf1cfa90acf63d68a4f064adfba65.RData b/LM1_cache/html/unnamed-chunk-1_cc8cf1cfa90acf63d68a4f064adfba65.RData deleted file mode 100644 index 60a2dab..0000000 Binary files a/LM1_cache/html/unnamed-chunk-1_cc8cf1cfa90acf63d68a4f064adfba65.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-1_cc8cf1cfa90acf63d68a4f064adfba65.rdb b/LM1_cache/html/unnamed-chunk-1_cc8cf1cfa90acf63d68a4f064adfba65.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-1_cc8cf1cfa90acf63d68a4f064adfba65.rdx b/LM1_cache/html/unnamed-chunk-1_cc8cf1cfa90acf63d68a4f064adfba65.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-1_cc8cf1cfa90acf63d68a4f064adfba65.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-20_b66586790e6e1624bbd279148a843a28.RData b/LM1_cache/html/unnamed-chunk-20_b66586790e6e1624bbd279148a843a28.RData deleted file mode 100644 index b6cea61..0000000 Binary files a/LM1_cache/html/unnamed-chunk-20_b66586790e6e1624bbd279148a843a28.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-20_b66586790e6e1624bbd279148a843a28.rdb b/LM1_cache/html/unnamed-chunk-20_b66586790e6e1624bbd279148a843a28.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-20_b66586790e6e1624bbd279148a843a28.rdx b/LM1_cache/html/unnamed-chunk-20_b66586790e6e1624bbd279148a843a28.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-20_b66586790e6e1624bbd279148a843a28.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-21_d69d8c9ad7805101f0ed8d754c9d6dd2.RData b/LM1_cache/html/unnamed-chunk-21_d69d8c9ad7805101f0ed8d754c9d6dd2.RData deleted file mode 100644 index 56d29fb..0000000 Binary files a/LM1_cache/html/unnamed-chunk-21_d69d8c9ad7805101f0ed8d754c9d6dd2.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-21_d69d8c9ad7805101f0ed8d754c9d6dd2.rdb b/LM1_cache/html/unnamed-chunk-21_d69d8c9ad7805101f0ed8d754c9d6dd2.rdb deleted file mode 100644 index 868adf5..0000000 Binary files a/LM1_cache/html/unnamed-chunk-21_d69d8c9ad7805101f0ed8d754c9d6dd2.rdb and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-21_d69d8c9ad7805101f0ed8d754c9d6dd2.rdx b/LM1_cache/html/unnamed-chunk-21_d69d8c9ad7805101f0ed8d754c9d6dd2.rdx deleted file mode 100644 index df88656..0000000 Binary files a/LM1_cache/html/unnamed-chunk-21_d69d8c9ad7805101f0ed8d754c9d6dd2.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-22_b54a810983acc1feecd0efdf73fd98ed.RData b/LM1_cache/html/unnamed-chunk-22_b54a810983acc1feecd0efdf73fd98ed.RData deleted file mode 100644 index 005d9e4..0000000 Binary files a/LM1_cache/html/unnamed-chunk-22_b54a810983acc1feecd0efdf73fd98ed.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-22_b54a810983acc1feecd0efdf73fd98ed.rdb b/LM1_cache/html/unnamed-chunk-22_b54a810983acc1feecd0efdf73fd98ed.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-22_b54a810983acc1feecd0efdf73fd98ed.rdx b/LM1_cache/html/unnamed-chunk-22_b54a810983acc1feecd0efdf73fd98ed.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-22_b54a810983acc1feecd0efdf73fd98ed.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-2_ef9f338e0b025b053a07f86b6cbd68b6.RData b/LM1_cache/html/unnamed-chunk-2_ef9f338e0b025b053a07f86b6cbd68b6.RData deleted file mode 100644 index 067139b..0000000 Binary files a/LM1_cache/html/unnamed-chunk-2_ef9f338e0b025b053a07f86b6cbd68b6.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-2_ef9f338e0b025b053a07f86b6cbd68b6.rdb b/LM1_cache/html/unnamed-chunk-2_ef9f338e0b025b053a07f86b6cbd68b6.rdb deleted file mode 100644 index 6358d31..0000000 Binary files a/LM1_cache/html/unnamed-chunk-2_ef9f338e0b025b053a07f86b6cbd68b6.rdb and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-2_ef9f338e0b025b053a07f86b6cbd68b6.rdx b/LM1_cache/html/unnamed-chunk-2_ef9f338e0b025b053a07f86b6cbd68b6.rdx deleted file mode 100644 index 425832e..0000000 Binary files a/LM1_cache/html/unnamed-chunk-2_ef9f338e0b025b053a07f86b6cbd68b6.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-3_3f0e72b94505c64abd0ad33eb64f9615.RData b/LM1_cache/html/unnamed-chunk-3_3f0e72b94505c64abd0ad33eb64f9615.RData deleted file mode 100644 index cc9d999..0000000 Binary files a/LM1_cache/html/unnamed-chunk-3_3f0e72b94505c64abd0ad33eb64f9615.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-3_3f0e72b94505c64abd0ad33eb64f9615.rdb b/LM1_cache/html/unnamed-chunk-3_3f0e72b94505c64abd0ad33eb64f9615.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-3_3f0e72b94505c64abd0ad33eb64f9615.rdx b/LM1_cache/html/unnamed-chunk-3_3f0e72b94505c64abd0ad33eb64f9615.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-3_3f0e72b94505c64abd0ad33eb64f9615.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-4_fad51ee541e681ba9922b92ae354ff4a.RData b/LM1_cache/html/unnamed-chunk-4_fad51ee541e681ba9922b92ae354ff4a.RData deleted file mode 100644 index bd6d16a..0000000 Binary files a/LM1_cache/html/unnamed-chunk-4_fad51ee541e681ba9922b92ae354ff4a.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-4_fad51ee541e681ba9922b92ae354ff4a.rdb b/LM1_cache/html/unnamed-chunk-4_fad51ee541e681ba9922b92ae354ff4a.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-4_fad51ee541e681ba9922b92ae354ff4a.rdx b/LM1_cache/html/unnamed-chunk-4_fad51ee541e681ba9922b92ae354ff4a.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-4_fad51ee541e681ba9922b92ae354ff4a.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-5_03cc856040a88f2292c51507d25413a9.RData b/LM1_cache/html/unnamed-chunk-5_03cc856040a88f2292c51507d25413a9.RData deleted file mode 100644 index 23502c5..0000000 Binary files a/LM1_cache/html/unnamed-chunk-5_03cc856040a88f2292c51507d25413a9.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-5_03cc856040a88f2292c51507d25413a9.rdb b/LM1_cache/html/unnamed-chunk-5_03cc856040a88f2292c51507d25413a9.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-5_03cc856040a88f2292c51507d25413a9.rdx b/LM1_cache/html/unnamed-chunk-5_03cc856040a88f2292c51507d25413a9.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-5_03cc856040a88f2292c51507d25413a9.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-6_d5d8efe85c45fe3b955780802a702264.RData b/LM1_cache/html/unnamed-chunk-6_d5d8efe85c45fe3b955780802a702264.RData deleted file mode 100644 index 7a99ef0..0000000 Binary files a/LM1_cache/html/unnamed-chunk-6_d5d8efe85c45fe3b955780802a702264.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-6_d5d8efe85c45fe3b955780802a702264.rdb b/LM1_cache/html/unnamed-chunk-6_d5d8efe85c45fe3b955780802a702264.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-6_d5d8efe85c45fe3b955780802a702264.rdx b/LM1_cache/html/unnamed-chunk-6_d5d8efe85c45fe3b955780802a702264.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-6_d5d8efe85c45fe3b955780802a702264.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-7_3f8a9864ad6c63dfe596e9329b94d378.RData b/LM1_cache/html/unnamed-chunk-7_3f8a9864ad6c63dfe596e9329b94d378.RData deleted file mode 100644 index 6995f6b..0000000 Binary files a/LM1_cache/html/unnamed-chunk-7_3f8a9864ad6c63dfe596e9329b94d378.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-7_3f8a9864ad6c63dfe596e9329b94d378.rdb b/LM1_cache/html/unnamed-chunk-7_3f8a9864ad6c63dfe596e9329b94d378.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-7_3f8a9864ad6c63dfe596e9329b94d378.rdx b/LM1_cache/html/unnamed-chunk-7_3f8a9864ad6c63dfe596e9329b94d378.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-7_3f8a9864ad6c63dfe596e9329b94d378.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-8_dace02d1462a67fc3ba6c9ec29397758.RData b/LM1_cache/html/unnamed-chunk-8_dace02d1462a67fc3ba6c9ec29397758.RData deleted file mode 100644 index 0d519af..0000000 Binary files a/LM1_cache/html/unnamed-chunk-8_dace02d1462a67fc3ba6c9ec29397758.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-8_dace02d1462a67fc3ba6c9ec29397758.rdb b/LM1_cache/html/unnamed-chunk-8_dace02d1462a67fc3ba6c9ec29397758.rdb deleted file mode 100644 index eb5093b..0000000 Binary files a/LM1_cache/html/unnamed-chunk-8_dace02d1462a67fc3ba6c9ec29397758.rdb and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-8_dace02d1462a67fc3ba6c9ec29397758.rdx b/LM1_cache/html/unnamed-chunk-8_dace02d1462a67fc3ba6c9ec29397758.rdx deleted file mode 100644 index 9d99acd..0000000 Binary files a/LM1_cache/html/unnamed-chunk-8_dace02d1462a67fc3ba6c9ec29397758.rdx and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-9_b9fca90b2d5f191cb4fcf54ed171b9e9.RData b/LM1_cache/html/unnamed-chunk-9_b9fca90b2d5f191cb4fcf54ed171b9e9.RData deleted file mode 100644 index fb0fa9f..0000000 Binary files a/LM1_cache/html/unnamed-chunk-9_b9fca90b2d5f191cb4fcf54ed171b9e9.RData and /dev/null differ diff --git a/LM1_cache/html/unnamed-chunk-9_b9fca90b2d5f191cb4fcf54ed171b9e9.rdb b/LM1_cache/html/unnamed-chunk-9_b9fca90b2d5f191cb4fcf54ed171b9e9.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM1_cache/html/unnamed-chunk-9_b9fca90b2d5f191cb4fcf54ed171b9e9.rdx b/LM1_cache/html/unnamed-chunk-9_b9fca90b2d5f191cb4fcf54ed171b9e9.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM1_cache/html/unnamed-chunk-9_b9fca90b2d5f191cb4fcf54ed171b9e9.rdx and /dev/null differ diff --git a/LM1_files/figure-html/unnamed-chunk-14-1.png b/LM1_files/figure-html/unnamed-chunk-14-1.png deleted file mode 100644 index b0d7f5e..0000000 Binary files a/LM1_files/figure-html/unnamed-chunk-14-1.png and /dev/null differ diff --git a/LM1_files/figure-html/unnamed-chunk-2-1.png b/LM1_files/figure-html/unnamed-chunk-2-1.png deleted file mode 100644 index 96b496a..0000000 Binary files a/LM1_files/figure-html/unnamed-chunk-2-1.png and /dev/null differ diff --git a/LM1_files/figure-html/unnamed-chunk-9-1.png b/LM1_files/figure-html/unnamed-chunk-9-1.png deleted file mode 100644 index 426bba3..0000000 Binary files a/LM1_files/figure-html/unnamed-chunk-9-1.png and /dev/null differ diff --git a/LM2_cache/html/__packages b/LM2_cache/html/__packages deleted file mode 100644 index f71cb90..0000000 --- a/LM2_cache/html/__packages +++ /dev/null @@ -1,7 +0,0 @@ -colorDF -prettycode -Rcpp -RcppZiggurat -Rfast -ggplot2 -tidyr diff --git a/LM2_cache/html/interaction_plot2_30f477a6580ec5520fde042f33587fd0.RData b/LM2_cache/html/interaction_plot2_30f477a6580ec5520fde042f33587fd0.RData deleted file mode 100644 index ad5f143..0000000 Binary files a/LM2_cache/html/interaction_plot2_30f477a6580ec5520fde042f33587fd0.RData and /dev/null differ diff --git a/LM2_cache/html/interaction_plot2_30f477a6580ec5520fde042f33587fd0.rdb b/LM2_cache/html/interaction_plot2_30f477a6580ec5520fde042f33587fd0.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM2_cache/html/interaction_plot2_30f477a6580ec5520fde042f33587fd0.rdx b/LM2_cache/html/interaction_plot2_30f477a6580ec5520fde042f33587fd0.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM2_cache/html/interaction_plot2_30f477a6580ec5520fde042f33587fd0.rdx and /dev/null differ diff --git a/LM2_cache/html/interaction_plot_c19859404e1642ac7215a4d83f1f89ed.RData b/LM2_cache/html/interaction_plot_c19859404e1642ac7215a4d83f1f89ed.RData deleted file mode 100644 index a4cd709..0000000 Binary files a/LM2_cache/html/interaction_plot_c19859404e1642ac7215a4d83f1f89ed.RData and /dev/null differ diff --git a/LM2_cache/html/interaction_plot_c19859404e1642ac7215a4d83f1f89ed.rdb b/LM2_cache/html/interaction_plot_c19859404e1642ac7215a4d83f1f89ed.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM2_cache/html/interaction_plot_c19859404e1642ac7215a4d83f1f89ed.rdx b/LM2_cache/html/interaction_plot_c19859404e1642ac7215a4d83f1f89ed.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM2_cache/html/interaction_plot_c19859404e1642ac7215a4d83f1f89ed.rdx and /dev/null differ diff --git a/LM2_cache/html/load_data_5ac11d240177351043fce438baab2e9d.RData b/LM2_cache/html/load_data_5ac11d240177351043fce438baab2e9d.RData deleted file mode 100644 index d11f94d..0000000 Binary files a/LM2_cache/html/load_data_5ac11d240177351043fce438baab2e9d.RData and /dev/null differ diff --git a/LM2_cache/html/load_data_5ac11d240177351043fce438baab2e9d.rdb b/LM2_cache/html/load_data_5ac11d240177351043fce438baab2e9d.rdb deleted file mode 100644 index 6358d31..0000000 Binary files a/LM2_cache/html/load_data_5ac11d240177351043fce438baab2e9d.rdb and /dev/null differ diff --git a/LM2_cache/html/load_data_5ac11d240177351043fce438baab2e9d.rdx b/LM2_cache/html/load_data_5ac11d240177351043fce438baab2e9d.rdx deleted file mode 100644 index 425832e..0000000 Binary files a/LM2_cache/html/load_data_5ac11d240177351043fce438baab2e9d.rdx and /dev/null differ diff --git a/LM2_cache/html/plot_0572486709aafa3a8718570ebe7ce344.RData b/LM2_cache/html/plot_0572486709aafa3a8718570ebe7ce344.RData deleted file mode 100644 index a831c88..0000000 Binary files a/LM2_cache/html/plot_0572486709aafa3a8718570ebe7ce344.RData and /dev/null differ diff --git a/LM2_cache/html/plot_0572486709aafa3a8718570ebe7ce344.rdb b/LM2_cache/html/plot_0572486709aafa3a8718570ebe7ce344.rdb deleted file mode 100644 index e680cd0..0000000 Binary files a/LM2_cache/html/plot_0572486709aafa3a8718570ebe7ce344.rdb and /dev/null differ diff --git a/LM2_cache/html/plot_0572486709aafa3a8718570ebe7ce344.rdx b/LM2_cache/html/plot_0572486709aafa3a8718570ebe7ce344.rdx deleted file mode 100644 index 73ab805..0000000 Binary files a/LM2_cache/html/plot_0572486709aafa3a8718570ebe7ce344.rdx and /dev/null differ diff --git a/LM2_cache/html/power_analysis_LM2_e60d7b1ad5777a38e85d60e86002b13f.RData b/LM2_cache/html/power_analysis_LM2_e60d7b1ad5777a38e85d60e86002b13f.RData deleted file mode 100644 index 878510a..0000000 Binary files a/LM2_cache/html/power_analysis_LM2_e60d7b1ad5777a38e85d60e86002b13f.RData and /dev/null differ diff --git a/LM2_cache/html/power_analysis_LM2_e60d7b1ad5777a38e85d60e86002b13f.rdb b/LM2_cache/html/power_analysis_LM2_e60d7b1ad5777a38e85d60e86002b13f.rdb deleted file mode 100644 index 32eff79..0000000 Binary files a/LM2_cache/html/power_analysis_LM2_e60d7b1ad5777a38e85d60e86002b13f.rdb and /dev/null differ diff --git a/LM2_cache/html/power_analysis_LM2_e60d7b1ad5777a38e85d60e86002b13f.rdx b/LM2_cache/html/power_analysis_LM2_e60d7b1ad5777a38e85d60e86002b13f.rdx deleted file mode 100644 index 5266656..0000000 Binary files a/LM2_cache/html/power_analysis_LM2_e60d7b1ad5777a38e85d60e86002b13f.rdx and /dev/null differ diff --git a/LM2_cache/html/rmvnorm_e80a7a76fbce991ce3cd65d4b1c02fa2.RData b/LM2_cache/html/rmvnorm_e80a7a76fbce991ce3cd65d4b1c02fa2.RData deleted file mode 100644 index 7d530b3..0000000 Binary files a/LM2_cache/html/rmvnorm_e80a7a76fbce991ce3cd65d4b1c02fa2.RData and /dev/null differ diff --git a/LM2_cache/html/rmvnorm_e80a7a76fbce991ce3cd65d4b1c02fa2.rdb b/LM2_cache/html/rmvnorm_e80a7a76fbce991ce3cd65d4b1c02fa2.rdb deleted file mode 100644 index 83d5e38..0000000 Binary files a/LM2_cache/html/rmvnorm_e80a7a76fbce991ce3cd65d4b1c02fa2.rdb and /dev/null differ diff --git a/LM2_cache/html/rmvnorm_e80a7a76fbce991ce3cd65d4b1c02fa2.rdx b/LM2_cache/html/rmvnorm_e80a7a76fbce991ce3cd65d4b1c02fa2.rdx deleted file mode 100644 index 32b6a80..0000000 Binary files a/LM2_cache/html/rmvnorm_e80a7a76fbce991ce3cd65d4b1c02fa2.rdx and /dev/null differ diff --git a/LM2_cache/html/setup_7d473be5fc0b5e10e03cc35c3e8765c8.RData b/LM2_cache/html/setup_7d473be5fc0b5e10e03cc35c3e8765c8.RData deleted file mode 100644 index fa47dee..0000000 Binary files a/LM2_cache/html/setup_7d473be5fc0b5e10e03cc35c3e8765c8.RData and /dev/null differ diff --git a/LM2_cache/html/setup_7d473be5fc0b5e10e03cc35c3e8765c8.rdb b/LM2_cache/html/setup_7d473be5fc0b5e10e03cc35c3e8765c8.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM2_cache/html/setup_7d473be5fc0b5e10e03cc35c3e8765c8.rdx b/LM2_cache/html/setup_7d473be5fc0b5e10e03cc35c3e8765c8.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM2_cache/html/setup_7d473be5fc0b5e10e03cc35c3e8765c8.rdx and /dev/null differ diff --git a/LM2_cache/html/sim_7199c85c5f7dac3553b495f048d87be1.RData b/LM2_cache/html/sim_7199c85c5f7dac3553b495f048d87be1.RData deleted file mode 100644 index 1af58c7..0000000 Binary files a/LM2_cache/html/sim_7199c85c5f7dac3553b495f048d87be1.RData and /dev/null differ diff --git a/LM2_cache/html/sim_7199c85c5f7dac3553b495f048d87be1.rdb b/LM2_cache/html/sim_7199c85c5f7dac3553b495f048d87be1.rdb deleted file mode 100644 index 784e8d4..0000000 Binary files a/LM2_cache/html/sim_7199c85c5f7dac3553b495f048d87be1.rdb and /dev/null differ diff --git a/LM2_cache/html/sim_7199c85c5f7dac3553b495f048d87be1.rdx b/LM2_cache/html/sim_7199c85c5f7dac3553b495f048d87be1.rdx deleted file mode 100644 index 6006ed3..0000000 Binary files a/LM2_cache/html/sim_7199c85c5f7dac3553b495f048d87be1.rdx and /dev/null differ diff --git a/LM2_cache/html/treatment_effect_ecbe0d1059c8fa309ee9a1fa760998f0.RData b/LM2_cache/html/treatment_effect_ecbe0d1059c8fa309ee9a1fa760998f0.RData deleted file mode 100644 index 95e56fc..0000000 Binary files a/LM2_cache/html/treatment_effect_ecbe0d1059c8fa309ee9a1fa760998f0.RData and /dev/null differ diff --git a/LM2_cache/html/treatment_effect_ecbe0d1059c8fa309ee9a1fa760998f0.rdb b/LM2_cache/html/treatment_effect_ecbe0d1059c8fa309ee9a1fa760998f0.rdb deleted file mode 100644 index 4d353f1..0000000 Binary files a/LM2_cache/html/treatment_effect_ecbe0d1059c8fa309ee9a1fa760998f0.rdb and /dev/null differ diff --git a/LM2_cache/html/treatment_effect_ecbe0d1059c8fa309ee9a1fa760998f0.rdx b/LM2_cache/html/treatment_effect_ecbe0d1059c8fa309ee9a1fa760998f0.rdx deleted file mode 100644 index c41377a..0000000 Binary files a/LM2_cache/html/treatment_effect_ecbe0d1059c8fa309ee9a1fa760998f0.rdx and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-1_9bf562590b20b1693817602ff99c9e97.RData b/LM2_cache/html/unnamed-chunk-1_9bf562590b20b1693817602ff99c9e97.RData deleted file mode 100644 index e032e7f..0000000 Binary files a/LM2_cache/html/unnamed-chunk-1_9bf562590b20b1693817602ff99c9e97.RData and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-1_9bf562590b20b1693817602ff99c9e97.rdb b/LM2_cache/html/unnamed-chunk-1_9bf562590b20b1693817602ff99c9e97.rdb deleted file mode 100644 index bc28b88..0000000 Binary files a/LM2_cache/html/unnamed-chunk-1_9bf562590b20b1693817602ff99c9e97.rdb and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-1_9bf562590b20b1693817602ff99c9e97.rdx b/LM2_cache/html/unnamed-chunk-1_9bf562590b20b1693817602ff99c9e97.rdx deleted file mode 100644 index a899c55..0000000 Binary files a/LM2_cache/html/unnamed-chunk-1_9bf562590b20b1693817602ff99c9e97.rdx and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-2_bb89c53bb77738a15d8e70b2c7073369.RData b/LM2_cache/html/unnamed-chunk-2_bb89c53bb77738a15d8e70b2c7073369.RData deleted file mode 100644 index c4f8656..0000000 Binary files a/LM2_cache/html/unnamed-chunk-2_bb89c53bb77738a15d8e70b2c7073369.RData and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-2_bb89c53bb77738a15d8e70b2c7073369.rdb b/LM2_cache/html/unnamed-chunk-2_bb89c53bb77738a15d8e70b2c7073369.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM2_cache/html/unnamed-chunk-2_bb89c53bb77738a15d8e70b2c7073369.rdx b/LM2_cache/html/unnamed-chunk-2_bb89c53bb77738a15d8e70b2c7073369.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM2_cache/html/unnamed-chunk-2_bb89c53bb77738a15d8e70b2c7073369.rdx and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-3_164a3ee5170e6d23712cfa3de7451c2f.RData b/LM2_cache/html/unnamed-chunk-3_164a3ee5170e6d23712cfa3de7451c2f.RData deleted file mode 100644 index 9e3fe86..0000000 Binary files a/LM2_cache/html/unnamed-chunk-3_164a3ee5170e6d23712cfa3de7451c2f.RData and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-3_164a3ee5170e6d23712cfa3de7451c2f.rdb b/LM2_cache/html/unnamed-chunk-3_164a3ee5170e6d23712cfa3de7451c2f.rdb deleted file mode 100644 index 63abd33..0000000 Binary files a/LM2_cache/html/unnamed-chunk-3_164a3ee5170e6d23712cfa3de7451c2f.rdb and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-3_164a3ee5170e6d23712cfa3de7451c2f.rdx b/LM2_cache/html/unnamed-chunk-3_164a3ee5170e6d23712cfa3de7451c2f.rdx deleted file mode 100644 index 51c715a..0000000 Binary files a/LM2_cache/html/unnamed-chunk-3_164a3ee5170e6d23712cfa3de7451c2f.rdx and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-4_c5b58f5983eb6693927e1ef103a37895.RData b/LM2_cache/html/unnamed-chunk-4_c5b58f5983eb6693927e1ef103a37895.RData deleted file mode 100644 index e7f79f4..0000000 Binary files a/LM2_cache/html/unnamed-chunk-4_c5b58f5983eb6693927e1ef103a37895.RData and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-4_c5b58f5983eb6693927e1ef103a37895.rdb b/LM2_cache/html/unnamed-chunk-4_c5b58f5983eb6693927e1ef103a37895.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM2_cache/html/unnamed-chunk-4_c5b58f5983eb6693927e1ef103a37895.rdx b/LM2_cache/html/unnamed-chunk-4_c5b58f5983eb6693927e1ef103a37895.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM2_cache/html/unnamed-chunk-4_c5b58f5983eb6693927e1ef103a37895.rdx and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-5_fb3e23a159a50c1d1e671663b57d1064.RData b/LM2_cache/html/unnamed-chunk-5_fb3e23a159a50c1d1e671663b57d1064.RData deleted file mode 100644 index f3dda33..0000000 Binary files a/LM2_cache/html/unnamed-chunk-5_fb3e23a159a50c1d1e671663b57d1064.RData and /dev/null differ diff --git a/LM2_cache/html/unnamed-chunk-5_fb3e23a159a50c1d1e671663b57d1064.rdb b/LM2_cache/html/unnamed-chunk-5_fb3e23a159a50c1d1e671663b57d1064.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LM2_cache/html/unnamed-chunk-5_fb3e23a159a50c1d1e671663b57d1064.rdx b/LM2_cache/html/unnamed-chunk-5_fb3e23a159a50c1d1e671663b57d1064.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LM2_cache/html/unnamed-chunk-5_fb3e23a159a50c1d1e671663b57d1064.rdx and /dev/null differ diff --git a/LM2_files/figure-html/interaction_plot-1.png b/LM2_files/figure-html/interaction_plot-1.png deleted file mode 100644 index 5ab84a8..0000000 Binary files a/LM2_files/figure-html/interaction_plot-1.png and /dev/null differ diff --git a/LM2_files/figure-html/interaction_plot2-1.png b/LM2_files/figure-html/interaction_plot2-1.png deleted file mode 100644 index 968780f..0000000 Binary files a/LM2_files/figure-html/interaction_plot2-1.png and /dev/null differ diff --git a/LM2_files/figure-html/plot-1.png b/LM2_files/figure-html/plot-1.png deleted file mode 100644 index 12f7c6a..0000000 Binary files a/LM2_files/figure-html/plot-1.png and /dev/null differ diff --git a/LM2_files/figure-html/plot-2.png b/LM2_files/figure-html/plot-2.png deleted file mode 100644 index eb0c7d5..0000000 Binary files a/LM2_files/figure-html/plot-2.png and /dev/null differ diff --git a/LMM_cache/html/__packages b/LMM_cache/html/__packages deleted file mode 100644 index 595292e..0000000 --- a/LMM_cache/html/__packages +++ /dev/null @@ -1,12 +0,0 @@ -colorDF -prettycode -Matrix -lme4 -lmerTest -ggplot2 -tidyr -Rcpp -RcppZiggurat -Rfast -future -future.apply diff --git a/LMM_cache/html/power_analysis_LMM_0c934a2bed78ddb68f31f0cb0cc710e4.RData b/LMM_cache/html/power_analysis_LMM_0c934a2bed78ddb68f31f0cb0cc710e4.RData deleted file mode 100644 index 04933bd..0000000 Binary files a/LMM_cache/html/power_analysis_LMM_0c934a2bed78ddb68f31f0cb0cc710e4.RData and /dev/null differ diff --git a/LMM_cache/html/power_analysis_LMM_0c934a2bed78ddb68f31f0cb0cc710e4.rdb b/LMM_cache/html/power_analysis_LMM_0c934a2bed78ddb68f31f0cb0cc710e4.rdb deleted file mode 100644 index f9b2f0a..0000000 Binary files a/LMM_cache/html/power_analysis_LMM_0c934a2bed78ddb68f31f0cb0cc710e4.rdb and /dev/null differ diff --git a/LMM_cache/html/power_analysis_LMM_0c934a2bed78ddb68f31f0cb0cc710e4.rdx b/LMM_cache/html/power_analysis_LMM_0c934a2bed78ddb68f31f0cb0cc710e4.rdx deleted file mode 100644 index 305ccbb..0000000 Binary files a/LMM_cache/html/power_analysis_LMM_0c934a2bed78ddb68f31f0cb0cc710e4.rdx and /dev/null differ diff --git a/LMM_cache/html/power_analysis_LMM_simple_e5fad475274bd20c80b4bb3fb17016ea.RData b/LMM_cache/html/power_analysis_LMM_simple_e5fad475274bd20c80b4bb3fb17016ea.RData deleted file mode 100644 index effd9d6..0000000 Binary files a/LMM_cache/html/power_analysis_LMM_simple_e5fad475274bd20c80b4bb3fb17016ea.RData and /dev/null differ diff --git a/LMM_cache/html/power_analysis_LMM_simple_e5fad475274bd20c80b4bb3fb17016ea.rdb b/LMM_cache/html/power_analysis_LMM_simple_e5fad475274bd20c80b4bb3fb17016ea.rdb deleted file mode 100644 index 15b8bc5..0000000 Binary files a/LMM_cache/html/power_analysis_LMM_simple_e5fad475274bd20c80b4bb3fb17016ea.rdb and /dev/null differ diff --git a/LMM_cache/html/power_analysis_LMM_simple_e5fad475274bd20c80b4bb3fb17016ea.rdx b/LMM_cache/html/power_analysis_LMM_simple_e5fad475274bd20c80b4bb3fb17016ea.rdx deleted file mode 100644 index 6440ad2..0000000 Binary files a/LMM_cache/html/power_analysis_LMM_simple_e5fad475274bd20c80b4bb3fb17016ea.rdx and /dev/null differ diff --git a/LMM_cache/html/setup_e6512767214d08cda28e213902f5798c.RData b/LMM_cache/html/setup_e6512767214d08cda28e213902f5798c.RData deleted file mode 100644 index 96df52e..0000000 Binary files a/LMM_cache/html/setup_e6512767214d08cda28e213902f5798c.RData and /dev/null differ diff --git a/LMM_cache/html/setup_e6512767214d08cda28e213902f5798c.rdb b/LMM_cache/html/setup_e6512767214d08cda28e213902f5798c.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LMM_cache/html/setup_e6512767214d08cda28e213902f5798c.rdx b/LMM_cache/html/setup_e6512767214d08cda28e213902f5798c.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LMM_cache/html/setup_e6512767214d08cda28e213902f5798c.rdx and /dev/null differ diff --git a/LMM_cache/html/sim_d0db54361e499991a120455826af7e8a.RData b/LMM_cache/html/sim_d0db54361e499991a120455826af7e8a.RData deleted file mode 100644 index 60902fe..0000000 Binary files a/LMM_cache/html/sim_d0db54361e499991a120455826af7e8a.RData and /dev/null differ diff --git a/LMM_cache/html/sim_d0db54361e499991a120455826af7e8a.rdb b/LMM_cache/html/sim_d0db54361e499991a120455826af7e8a.rdb deleted file mode 100644 index 03d1c6b..0000000 Binary files a/LMM_cache/html/sim_d0db54361e499991a120455826af7e8a.rdb and /dev/null differ diff --git a/LMM_cache/html/sim_d0db54361e499991a120455826af7e8a.rdx b/LMM_cache/html/sim_d0db54361e499991a120455826af7e8a.rdx deleted file mode 100644 index 5307f62..0000000 Binary files a/LMM_cache/html/sim_d0db54361e499991a120455826af7e8a.rdx and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-1_551adcd25a47f5bd5e1fa21d6ecf4555.RData b/LMM_cache/html/unnamed-chunk-1_551adcd25a47f5bd5e1fa21d6ecf4555.RData deleted file mode 100644 index 3c4b24d..0000000 Binary files a/LMM_cache/html/unnamed-chunk-1_551adcd25a47f5bd5e1fa21d6ecf4555.RData and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-1_551adcd25a47f5bd5e1fa21d6ecf4555.rdb b/LMM_cache/html/unnamed-chunk-1_551adcd25a47f5bd5e1fa21d6ecf4555.rdb deleted file mode 100644 index 6a03f74..0000000 Binary files a/LMM_cache/html/unnamed-chunk-1_551adcd25a47f5bd5e1fa21d6ecf4555.rdb and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-1_551adcd25a47f5bd5e1fa21d6ecf4555.rdx b/LMM_cache/html/unnamed-chunk-1_551adcd25a47f5bd5e1fa21d6ecf4555.rdx deleted file mode 100644 index 5999d07..0000000 Binary files a/LMM_cache/html/unnamed-chunk-1_551adcd25a47f5bd5e1fa21d6ecf4555.rdx and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-2_ba4ef8e2ca95b7738a6c0656366cc623.RData b/LMM_cache/html/unnamed-chunk-2_ba4ef8e2ca95b7738a6c0656366cc623.RData deleted file mode 100644 index 69d8aaa..0000000 Binary files a/LMM_cache/html/unnamed-chunk-2_ba4ef8e2ca95b7738a6c0656366cc623.RData and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-2_ba4ef8e2ca95b7738a6c0656366cc623.rdb b/LMM_cache/html/unnamed-chunk-2_ba4ef8e2ca95b7738a6c0656366cc623.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LMM_cache/html/unnamed-chunk-2_ba4ef8e2ca95b7738a6c0656366cc623.rdx b/LMM_cache/html/unnamed-chunk-2_ba4ef8e2ca95b7738a6c0656366cc623.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LMM_cache/html/unnamed-chunk-2_ba4ef8e2ca95b7738a6c0656366cc623.rdx and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-3_52c6ad6377093a104560e44b06cd3180.RData b/LMM_cache/html/unnamed-chunk-3_52c6ad6377093a104560e44b06cd3180.RData deleted file mode 100644 index 59895de..0000000 Binary files a/LMM_cache/html/unnamed-chunk-3_52c6ad6377093a104560e44b06cd3180.RData and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-3_52c6ad6377093a104560e44b06cd3180.rdb b/LMM_cache/html/unnamed-chunk-3_52c6ad6377093a104560e44b06cd3180.rdb deleted file mode 100644 index 094349f..0000000 Binary files a/LMM_cache/html/unnamed-chunk-3_52c6ad6377093a104560e44b06cd3180.rdb and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-3_52c6ad6377093a104560e44b06cd3180.rdx b/LMM_cache/html/unnamed-chunk-3_52c6ad6377093a104560e44b06cd3180.rdx deleted file mode 100644 index 2de3c44..0000000 Binary files a/LMM_cache/html/unnamed-chunk-3_52c6ad6377093a104560e44b06cd3180.rdx and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-4_3448fcfe350a59277f9f6338f38f69bf.RData b/LMM_cache/html/unnamed-chunk-4_3448fcfe350a59277f9f6338f38f69bf.RData deleted file mode 100644 index 014c195..0000000 Binary files a/LMM_cache/html/unnamed-chunk-4_3448fcfe350a59277f9f6338f38f69bf.RData and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-4_3448fcfe350a59277f9f6338f38f69bf.rdb b/LMM_cache/html/unnamed-chunk-4_3448fcfe350a59277f9f6338f38f69bf.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LMM_cache/html/unnamed-chunk-4_3448fcfe350a59277f9f6338f38f69bf.rdx b/LMM_cache/html/unnamed-chunk-4_3448fcfe350a59277f9f6338f38f69bf.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LMM_cache/html/unnamed-chunk-4_3448fcfe350a59277f9f6338f38f69bf.rdx and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-5_b3be58e17bacd9a881d3245b1cd0aff2.RData b/LMM_cache/html/unnamed-chunk-5_b3be58e17bacd9a881d3245b1cd0aff2.RData deleted file mode 100644 index a6fe984..0000000 Binary files a/LMM_cache/html/unnamed-chunk-5_b3be58e17bacd9a881d3245b1cd0aff2.RData and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-5_b3be58e17bacd9a881d3245b1cd0aff2.rdb b/LMM_cache/html/unnamed-chunk-5_b3be58e17bacd9a881d3245b1cd0aff2.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LMM_cache/html/unnamed-chunk-5_b3be58e17bacd9a881d3245b1cd0aff2.rdx b/LMM_cache/html/unnamed-chunk-5_b3be58e17bacd9a881d3245b1cd0aff2.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LMM_cache/html/unnamed-chunk-5_b3be58e17bacd9a881d3245b1cd0aff2.rdx and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-6_05fb5cf11557f8c774dbe899a2677478.RData b/LMM_cache/html/unnamed-chunk-6_05fb5cf11557f8c774dbe899a2677478.RData deleted file mode 100644 index 4227f18..0000000 Binary files a/LMM_cache/html/unnamed-chunk-6_05fb5cf11557f8c774dbe899a2677478.RData and /dev/null differ diff --git a/LMM_cache/html/unnamed-chunk-6_05fb5cf11557f8c774dbe899a2677478.rdb b/LMM_cache/html/unnamed-chunk-6_05fb5cf11557f8c774dbe899a2677478.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/LMM_cache/html/unnamed-chunk-6_05fb5cf11557f8c774dbe899a2677478.rdx b/LMM_cache/html/unnamed-chunk-6_05fb5cf11557f8c774dbe899a2677478.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/LMM_cache/html/unnamed-chunk-6_05fb5cf11557f8c774dbe899a2677478.rdx and /dev/null differ diff --git a/LMM_files/figure-html/unnamed-chunk-2-1.png b/LMM_files/figure-html/unnamed-chunk-2-1.png deleted file mode 100644 index 1707b7c..0000000 Binary files a/LMM_files/figure-html/unnamed-chunk-2-1.png and /dev/null differ diff --git a/README.md b/README.md index 11dcb7a..5b0fdb0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Simulations-for-Advanced-Power-Analyses -Tutorial: [https://malikaihle.github.io/Simulations-for-Advanced-Power-Analyses/](https://malikaihle.github.io/Simulations-for-Advanced-Power-Analyses/) +Tutorial: [https://lmu-osc.github.io/Simulations-for-Advanced-Power-Analyses/](https://lmu-osc.github.io/Simulations-for-Advanced-Power-Analyses/) This tutorial was created by Felix Schönbrodt and Moritz Fischer, with contributions from Malika Ihle, to be part of the training offering of the Ludwig-Maximilian University Open Science Center in Munich. It was initially commissioned and funded by the University of Hamburg, Faculty of Psychology and Movement Science. diff --git a/Regression_to_mean_cache/html/__packages b/Regression_to_mean_cache/html/__packages deleted file mode 100644 index ac03788..0000000 --- a/Regression_to_mean_cache/html/__packages +++ /dev/null @@ -1,14 +0,0 @@ -base -methods -colorDF -prettycode -datasets -utils -grDevices -graphics -stats -Rcpp -RcppZiggurat -Rfast -tidyr -ggplot2 diff --git a/Regression_to_mean_cache/html/unnamed-chunk-1_2f84d73e3370efa3fb625adc50ef12e8.RData b/Regression_to_mean_cache/html/unnamed-chunk-1_2f84d73e3370efa3fb625adc50ef12e8.RData deleted file mode 100644 index a9250aa..0000000 Binary files a/Regression_to_mean_cache/html/unnamed-chunk-1_2f84d73e3370efa3fb625adc50ef12e8.RData and /dev/null differ diff --git a/Regression_to_mean_cache/html/unnamed-chunk-1_2f84d73e3370efa3fb625adc50ef12e8.rdb b/Regression_to_mean_cache/html/unnamed-chunk-1_2f84d73e3370efa3fb625adc50ef12e8.rdb deleted file mode 100644 index ec056f5..0000000 Binary files a/Regression_to_mean_cache/html/unnamed-chunk-1_2f84d73e3370efa3fb625adc50ef12e8.rdb and /dev/null differ diff --git a/Regression_to_mean_cache/html/unnamed-chunk-1_2f84d73e3370efa3fb625adc50ef12e8.rdx b/Regression_to_mean_cache/html/unnamed-chunk-1_2f84d73e3370efa3fb625adc50ef12e8.rdx deleted file mode 100644 index 6ff080a..0000000 Binary files a/Regression_to_mean_cache/html/unnamed-chunk-1_2f84d73e3370efa3fb625adc50ef12e8.rdx and /dev/null differ diff --git a/Regression_to_mean_files/figure-html/unnamed-chunk-1-1.png b/Regression_to_mean_files/figure-html/unnamed-chunk-1-1.png deleted file mode 100644 index 0f1fe2f..0000000 Binary files a/Regression_to_mean_files/figure-html/unnamed-chunk-1-1.png and /dev/null differ diff --git a/Regression_to_mean_files/figure-html/unnamed-chunk-1-2.png b/Regression_to_mean_files/figure-html/unnamed-chunk-1-2.png deleted file mode 100644 index c0e0599..0000000 Binary files a/Regression_to_mean_files/figure-html/unnamed-chunk-1-2.png and /dev/null differ diff --git a/Regression_to_mean_files/figure-html/unnamed-chunk-1-3.png b/Regression_to_mean_files/figure-html/unnamed-chunk-1-3.png deleted file mode 100644 index 62d524f..0000000 Binary files a/Regression_to_mean_files/figure-html/unnamed-chunk-1-3.png and /dev/null differ diff --git a/Regression_to_mean_files/figure-html/unnamed-chunk-1-4.png b/Regression_to_mean_files/figure-html/unnamed-chunk-1-4.png deleted file mode 100644 index 9465498..0000000 Binary files a/Regression_to_mean_files/figure-html/unnamed-chunk-1-4.png and /dev/null differ diff --git a/Regression_to_mean_files/figure-html/unnamed-chunk-1-5.png b/Regression_to_mean_files/figure-html/unnamed-chunk-1-5.png deleted file mode 100644 index e962eb5..0000000 Binary files a/Regression_to_mean_files/figure-html/unnamed-chunk-1-5.png and /dev/null differ diff --git a/Resources_cache/html/__packages b/Resources_cache/html/__packages deleted file mode 100644 index d44ddce..0000000 --- a/Resources_cache/html/__packages +++ /dev/null @@ -1,7 +0,0 @@ -base -methods -datasets -utils -grDevices -graphics -stats diff --git a/Resources_cache/html/unnamed-chunk-1_3215d5137b35de58b13826c2fc6507d7.RData b/Resources_cache/html/unnamed-chunk-1_3215d5137b35de58b13826c2fc6507d7.RData deleted file mode 100644 index b89640a..0000000 Binary files a/Resources_cache/html/unnamed-chunk-1_3215d5137b35de58b13826c2fc6507d7.RData and /dev/null differ diff --git a/Resources_cache/html/unnamed-chunk-1_3215d5137b35de58b13826c2fc6507d7.rdb b/Resources_cache/html/unnamed-chunk-1_3215d5137b35de58b13826c2fc6507d7.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/Resources_cache/html/unnamed-chunk-1_3215d5137b35de58b13826c2fc6507d7.rdx b/Resources_cache/html/unnamed-chunk-1_3215d5137b35de58b13826c2fc6507d7.rdx deleted file mode 100644 index 7b8489f..0000000 Binary files a/Resources_cache/html/unnamed-chunk-1_3215d5137b35de58b13826c2fc6507d7.rdx and /dev/null differ diff --git a/SEM.qmd b/SEM.qmd index 2e2f135..c84795a 100644 --- a/SEM.qmd +++ b/SEM.qmd @@ -1,603 +1,603 @@ ---- -title: "Ch. 5: Structural Equation Models" -format: html -author: Moritz Fischer -execute: - cache: true -project-type: website ---- - -*Reading/working time: ~50 min.* - -```{r install packages, message=FALSE, warning=FALSE} -#install.packages(c("lavaan", "ggplot2", "Rfast", "MBESS", "future.apply"), dependencies = TRUE) - -library("lavaan") -library("Rfast") -library("ggplot2") -library("dplyr") -library("MBESS") -library(future.apply) -# this initializes parallel processing, for faster code execution -# By default, it uses all available cores -plan(multisession) -``` - -In this chapter, we will focus on some rather simple Structural Equation Models (SEM). The goal is to illustrate how simulations can be used to estimate statistical power to detect a given effect in a SEM. In the context of SEMs, the focal effect may be for instance a fit index (e.g., Chi-Square, Root Mean Square Error of Approximation, etc.) or a model coefficient (e.g., a regression coefficient for the association between two latent factors). In this chapter, we only focus on the latter, that is power analyses for regression coefficients in the context of SEM. Please see the bonus chapter titled "[Power analysis for fit indices in SEM](https://malikaihle.github.io/Simulations-for-Advanced-Power-Analyses/SEM_fit_index.html)" if you're interested in power analyses for fit indices. - -::: {.callout-note} -In this chapter, we'll use the parallelization techniques described in the Chapter ["Bonus: Optimizing R code for speed"](optimizing_code.qmd), otherwise the simulations are too slow. If you wonder about the unknown commands in the code, please read the bonus chapter to learn how to considerably speed up your code! But nonetheless, running some of the chunks in this chapter will require quite some time. We therefore decided to use 500 iterations per simulation only in this chapter; please keep in mind that you should use more iterations in order to obtain more precise estimates! -::: - -# A simulation-based power analysis for a single regression coefficient between latent variables - -Let's consider the following example: We are planning to conduct a study investigating whether people's openness to experience (as conceptualized in the big five personality traits) relates negatively to generalized prejudice, that is a latent variable comprising prejudice towards different social groups (e.g., women, foreigners, homosexuals, disabled people). We could plan to scrutinize this prediction with a SEM in which both openness to experience and generalized prejudice are modeled as latent variables. - -::: {.callout-note} - -Please note that the predictions used as examples in this chapter are not necessarily theoretically meaningful hypotheses, we merely use them to illustrate the mechanics of simulation-based power analyses in the context of SEM. - -::: - - -## Let's get some real data as starting point - -Just like for any other simulation-based power analysis, we first need to come up with plausible estimates of the distribution of the (manifest) variables. For the sake of simplicity, let's assume that there is a published study that measured manifestations of our two latent variables and that the corresponding data set is publicly available. For the purpose of this tutorial, we will draw on a publication by Bergh et al ([2016](https://www.researchgate.net/publication/306939541_Is_group_membership_necessary_for_understanding_generalized_prejudice_A_re-evaluation_of_why_prejudices_are_interrelated)) and the corresponding data set which has been made accessible as part of the `MPsychoR` package. Let's take a look at this data set. - - -```{r load data, message=FALSE} - -#install.packages("MPsychoR") -library(MPsychoR) - -data("Bergh") -attach(Bergh) - -#let's take a look -head(Bergh) -tail(Bergh) - -``` - -This data set comprises `r ncol(Bergh)` variables measured in `r nrow(Bergh)` participants. For now, we will focus on the following measured variables: - -- `EP` is a continuous variable measuring ethnic prejudice. -- `SP` is a continuous variable measuring sexism. -- `HP` is a continuous variable measuring sexual prejudice towards gays and lesbians. -- `DP` is a continuous variable measuring prejudice toward people with disabilities. -- `O1`, `O2`, and `O3` are three items measuring openness to experience. - -To get an impression of this data, we look at the correlations of the variables we're interested in. - -```{r correlations SEM} - -cor(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2) - -``` - -As we have discussed in the previous chapters, the starting point of every simulation-based power analysis is to specify the population parameters of the variables of interest. In our example, we can estimate the population parameters from the study by Bergh et al. (2016). We start by calculating the means of the variables, rounding them generously, and storing them in a vector called `means_vector`. - -```{r mean vector SEM} - -#store means -means_vector <- c(mean(EP), mean(SP), mean(HP), mean(DP), mean(O1), mean(O2), mean(O3)) |> round(2) - -#Let's take a look -means_vector - -``` - -We also need the variance-covariance matrix of our variables in order to simulate data. Luckily, we can estimate this from the Bergh et al. data as well. There are two ways to do this. First, we can use the `cov` function to obtain the variance-covariance matrix. This matrix incorporates the variance of each variable on the diagonal, and the covariances in the remaining cells. - -```{r cov matrix SEM} - -#store covariances -cov_mat <- cov(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2) - -#Let's take a look -cov_mat - -``` - -This works well as long as we have a data set (e.g., from a pilot study or published work) to estimate the variances and covariances. In other cases, however, we might not have access to such a data set. In this case, we might only have a correlation table that was provided in a published paper. But that's no problem either, as we can transform the correlations and standard deviations of the variables of interest into a variance-covariance matrix. The following chunk shows how this works by using the `cor2cov` function from the `MBESS` package. - -```{r sd vector and correlations SEM} - -#store correlation matrix -cor_mat <- cor(cbind(EP, SP, HP, DP, O1, O2, O3)) - -#store standard deviations -sd_vector <- c(sd(EP), sd(SP), sd(HP), sd(DP), sd(O1), sd(O2), sd(O3)) - -#transform correlations and standard deviations into variance-covariance matrix -cov_mat2 <- MBESS::cor2cov(cor.mat = cor_mat, sd = sd_vector) |> as.data.frame() |> round(2) - -#Let's take a look -cov_mat2 - -``` - -Let's do a plausibility check: Did the two ways to estimate the variance-covariance matrix lead to the same results? - -```{r plausibility check SEM} - -cov_mat == cov_mat2 - -``` - -Indeed, this worked! Both procedures lead to the exact same variance-covariance matrix. Now that we have an approximation of the variance-covariance matrix, we use the `rmvnorm` function from the `Rfast` package to simulate data from a multivariate normal distribution. The following code simulates `n = 50` observations from the specified population. - -```{r simulate data SEM} - -#Set seed to make results reproducible -set.seed(21364) - -#simulate data -my_first_simulated_data <- Rfast::rmvnorm(n = 50, mu=means_vector, sigma = cov_mat) |> as.data.frame() - -#Let's take a look -head(my_first_simulated_data) - -``` - -We could now fit a SEM to this simulated data set and check whether the regression coefficient modelling the association between openness to experience and generalized prejudice is significant at an $\alpha$-level of .005. We will work with the `lavaan` package to fit SEMs. - -```{r analyze data SEM} - -#specify SEM -model_sem <- "generalized_prejudice =~ EP + DP + SP + HP - openness =~ O1 + O2 + O3 - generalized_prejudice ~ openness" - -#fit the SEM to the simulated data set -fit_sem <- sem(model_sem, data = my_first_simulated_data) - -#display the results -summary(fit_sem) - -``` - -The results show that in this case, the regression coefficient is `r lavaan::parameterestimates(fit_sem)[8,]$est |> round(2)` which is significant with p = `r lavaan::parameterestimates(fit_sem)[8,]$pvalue |> round(3)`. But, actually, it is not our primary interest to see whether this particular simulated data set results in a significant regression coefficient. Rather, we want to know how many of a theoretically infinite number of simulations yield a significant p-value of the focal regression coefficient. Thus, as in the previous chapters, we now repeatedly simulate data sets of a certain size (say, 50 observations) from the specified population and store the results of the focal test (here: the p-value of the regression coefficient) in a vector called `p_values`. - -```{r multiple iterations SEM, warning=FALSE} - -#Set seed to make results reproducible -set.seed(21364) - -#let's do 500 iterations -iterations <- 500 - -#prepare an empty NA vector with 500 slots -p_values <- rep(NA, iterations) - -#sample size per iteration -n <- 50 - - -#simulate data -for(i in 1:iterations){ - - simulated_data <- Rfast::rmvnorm(n = n, mu = means_vector, sigma = cov_mat) |> as.data.frame() - fit_sem_simulated <- sem(model_sem, data = simulated_data) - - p_values[i] <- parameterestimates(fit_sem_simulated)[8,]$pvalue - -} - -``` - -How many of our 500 virtual samples would have found a significant p-value (i.e., p < .005)? - -```{r results SEM} - -#frequency table -table(p_values < .005) - -#percentage of significant results -sum(p_values < .005)/iterations*100 - -``` - -Only `r round((sum(p_values < .005)*100/iterations),2)`% of samples with the same size of $n=50$ result in a significant p-value. We conclude that $n=50$ observations seems to be insufficient, as the power with these parameters is lower than 80%. - -## Sample size planning: Find the necessary sample size - -But how many observations do we need to find the presumed effect with a power of 80%? Like before, we can now systematically vary certain parameters (e.g., sample size) of our simulation and see how that affects power. We could, for example, vary the sample size in a range from 30 to 200. Running these simulations typically requires quite some computing time. - - -```{r power analysis SEM, warning=FALSE} - -#Set seed to make results reproducible -set.seed(21364) - -#test ns between 30 and 200 -ns_sem <- seq(30, 200, by=10) - -#prepare empty vector to store results -result_sem <- data.frame() - -#set number of iterations -iterations_sem <- 500 - -#write function -sim_sem <- function(n, model, mu, sigma) { - - - simulated_data <- Rfast::rmvnorm(n = n, mu = mu, sigma = sigma) |> as.data.frame() - fit_sem_simulated <- sem(model, data = simulated_data) - p_value_sem <- parameterestimates(fit_sem_simulated)[8,]$pvalue - return(p_value_sem) - - } - - -#replicate function with varying ns -for (n in ns_sem) { - -p_values_sem <- future_replicate(iterations_sem, sim_sem(n = n, model = model_sem, mu = means_vector, sigma = cov_mat), future.seed=TRUE) -result_sem <- rbind(result_sem, data.frame( - n = n, - power = sum(p_values_sem < .005)/iterations_sem) - ) - -#The following line of code can be used to track the progress of the simulations -#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time -#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line -#message(paste("Progress info: Simulations completed for n =", n)) - - -} - -``` - -Let's plot the results: - -```{r plot power curve SEM, warning=FALSE} - -ggplot(result_sem, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red") - -``` - -This graph suggests that we need a sample size of approximately 50 participants to reach a power of 80% with the given population estimates. That's all it takes to run a power analysis for a SEM! - -In the following two sub-paragraphs, we would like to present two alternatives and/or extensions to this first way of doing a power analysis for a SEM. First, we would like to present an alternative way to simulate data for SEMs, i.e. by using a built-in function in the `lavaan` package. Second, we would like to show how a "safeguard" approach to power analysis can be used within the context of SEM. - -## Using the lavaan syntax to simulate data - -In our opening example in this chapter, we used the `rmvnorm` function from the `Rfast`package to simulate data based on the means of the manifest variables as well as their variance-covariance matrix. An alternative to this procedure is to use a built-in function in the `lavaan` package, that is, the `simulateData()` function. The main idea here is to provide this function with a lavaan model that specifies all relevant population parameters and then to use this function to directly simulate data. - -More specifically, we need to incorporate the factor loadings, regression coefficients and (residual) variances of all latent and manifest variables in the lavaan syntax. As these parameters are hardly ever known without a previous study, we will again draw on the results from the data by Bergh et al (2016). Let's again take a look at the results of our SEM when applying it to this data set. - -```{r fit Bergh} - -#fit the SEM to the pilot data set -fit_bergh <- sem(model_sem, data = Bergh) - -#display the results -summary(fit_bergh) - -``` - -In order to use the `simulateData()` function, we can take the estimates from this previous study and plug them into the lavaan syntax in the following chunk. - -```{r specify lavaan syntax} - -#Set seed to make results reproducible -set.seed(21364) - -#specify SEM -model_fully_specified <- - -"generalized_prejudice =~ 1*EP + 0.71*DP + 0.91*SP + 1*HP -openness =~ 1*O1 + 0.93*O2 + 1.14*O3 -generalized_prejudice ~ -0.77*openness - - -generalized_prejudice ~~ 0.19*generalized_prejudice -openness ~~ 0.16*openness -EP ~~ 0.21*EP -DP ~~ 0.14*DP -SP ~~ 0.23*SP -HP ~~ 2.12*HP -O1 ~~ 0.07*O1 -O2 ~~ 0.08*O2 -O3 ~~ 0.05*O3 - -" - -#lets try this -sim_lavaan <- simulateData(model_fully_specified, sample.nobs=100) -head(sim_lavaan) - - -``` - -The next step is to integrate this code into our first simulation-based power analysis in this chapter, that is, to replace the data simulation process using the `rmvnorm` function from the `Rfast` package with this new method using `simulateData()` from the `lavaan` package. To this end, I am adapting the `power analysis SEM` chunk from above accordingly. - -```{r simulateData, warning=FALSE} - -#Set seed to make results reproducible -set.seed(21364) - -#test ns between 30 and 200 -ns_sem <- seq(30, 200, by=10) - -#prepare empty vector to store results -result_sem <- data.frame() - -#set number of iterations -iterations_sem <- 500 - -#write function -sim_sem_lavaan <- function(n, model) { - - - sim_lavaan <- simulateData(model = model, sample.nobs=n) - fit_sem_simulated <- sem(model_sem, data = sim_lavaan) - p_value_sem <- parameterestimates(fit_sem_simulated)[8,]$pvalue - return(p_value_sem) - -} - - -#replicate function with varying ns -for (n in ns_sem) { - - p_values_sem <- future_replicate(iterations_sem, sim_sem_lavaan(n = n, model = model_fully_specified), future.seed=TRUE) - result_sem <- rbind(result_sem, data.frame( - n = n, - power = sum(p_values_sem < .005, na.rm = TRUE)/iterations_sem) - ) - -#The following line of code can be used to track the progress of the simulations -#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time -#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line -#message(paste("Progress info: Simulations completed for n =", n)) - -} - - -``` - -Let's plot this again. - -```{r plot power curve SEM lavaan, warning=FALSE} - -ggplot(result_sem, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red") - -``` - -Here, we conclude that approx. 55 participants would be needed to achieve 80% power. The slight difference compared to the previous power analysis should be explained by the fact that we rounded the numbers that define our statistical populations and that we only used 500 Monte Carlo iterations -- these differences should decrease with an increasing number of iterations. - -::: {.callout-tip} - -Wang and Rhemtulla ([2021](https://doi.org/10.1177/2515245920918253)) developed a shiny app that can do power analyses for SEMs in a similar fashion, but that additionally provides a point-and-click interface. You can use it here, for instance, to replicate the results of this simulation-based power analysis: [https://yilinandrewang.shinyapps.io/pwrSEM/](https://yilinandrewang.shinyapps.io/pwrSEM/) - -::: - -## A safeguard power approach for SEMs - -Of note, the two power analyses for our SEM we have conducted so far used the observed effect size from the Bergh et al. (2016) data set as an estimate of the "true" effect size. But, as this point estimate may be imprecise (e.g., because of publication bias), it seems reasonable to use a more conservative estimate of the true effect size. One more conservative approach in this context is the safeguard power approach (Perugini et al., [2014](https://doi.org/10.1177/1745691614528519)), which we have already applied in Chapter 1 ([Linear Model I: a single dichotomous predictor](LM1.qmd)). - -Basically, all need to do in order to account for variability of observed effect sizes is to calculate a 60%-confidence interval around the point estimate of the observed effect size from our pilot data and to use the more conservative bound of this confidence interval (here: the upper bound) as our new effect size estimate. This can be easily done with the `parameterestimates` function from the `lavaan` package which takes the level of the confidence interval as an input parameter. Let's use this function on our object `fit_bergh` which stores the results of our SEM in the `Bergh` data set. - -```{r safeguard estimation} - -parameterestimates(fit_bergh, level = .60) - -``` - -This output shows that the upper bound of the 60% confidence interval around the focal regression coefficient is `r lavaan::parameterestimates(fit_bergh, level = .60)[8,]$ci.upper |> round(2)`. We can now use this as our new and more conservative effect size estimate. We can for example insert this value into our simulation using the lavaan syntax. For this purpose, we copy the chunk from above and simply replace the previous effect size estimate (`r lavaan::parameterestimates(fit_bergh)[8,]$est |> round(2)`) with the new estimate (`r lavaan::parameterestimates(fit_bergh, level = .60)[8,]$ci.upper |> round(2)`), while keeping all other parameters that define this data set. - - -```{r lavaan model safeguard power} - -#Set seed to make results reproducible -set.seed(21364) - -#specify SEM -model_fully_specified_safeguard <- - -"generalized_prejudice =~ 1*EP + 0.71*DP + 0.91*SP + 1*HP -openness =~ 1*O1 + 0.93*O2 + 1.14*O3 -generalized_prejudice ~ -0.72*openness - - -generalized_prejudice ~~ 0.19*generalized_prejudice -openness ~~ 0.16*openness -EP ~~ 0.21*EP -DP ~~ 0.14*DP -SP ~~ 0.23*SP -HP ~~ 2.12*HP -O1 ~~ 0.07*O1 -O2 ~~ 0.08*O2 -O3 ~~ 0.05*O3 - -" - -#lets try this -sim_lavaan_safeguard <- simulateData(model_fully_specified_safeguard, sample.nobs=100) -head(sim_lavaan_safeguard) - -``` - -Now, everything is ready for the actual safeguard power analysis. We can re-use the `sim_sem_lavaan` function we have defined above. Let's see what we get here! - -```{r safeguard power analysis, warning=FALSE} - -#Set seed to make results reproducible -set.seed(21364) - -#prepare empty vector to store results -result_sem_safeguard <- data.frame() - -#replicate function with varying ns -for (n in ns_sem) { - - p_values_sem_safeguard <- future_replicate(iterations_sem, sim_sem_lavaan(n = n, model = model_fully_specified_safeguard), future.seed=TRUE) - result_sem_safeguard <- rbind(result_sem_safeguard, data.frame( - n = n, - power = sum(p_values_sem_safeguard < .005, na.rm = TRUE)/iterations_sem) - ) - -#The following line of code can be used to track the progress of the simulations -#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time -#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line -#message(paste("Progress info: Simulations completed for n =", n)) - -} - -ggplot(result_sem_safeguard, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red") - -``` -This safeguard power analysis yields a required sample size of ca. 63 participants. - -::: {.callout-note} - -In addition to this safeguard power approach, we would also have liked to derive a smallest effect size of interest (SESOI). However, no prior studies on SESOI in the context of personality/stereotypes were available and the measures/response scales used by Bergh et al (2016) were only vaguely reported in their manuscript, thereby making it difficult to derive a meaningful SESOI. We therefore only report a safeguard approach but no SESOI approach here. - -::: - -# A simulation-based power analysis for a mediation model with latent variables - -Sometimes, researchers not only wish to investigate whether and how two (latent) variables related to each other, but also whether the association between two (manifest or latent) variables is mediated by a third variable. We will run a power analysis for such a latent mediation model, investigating whether gender affects generalized prejudice (fully or partially) through openness to experience, while using the Bergh et al (2016) dataset as a pilot study. We can repeat the same steps as in our previous power analysis, while incorporating gender into our analysis. Specifically, we will follow these steps: - -1. find plausible estimates of the population parameters -2. specify the statistical model -3. simulate data from this population -4. compute the index of interest (e.g., the p-value) and store the results -5. repeat steps 2) and 3) multiple times -6. count how many samples would have detected the specified effect (i.e., compute the statistical power) -7. vary your simulation parameters until the desired level of power (e.g., 80%) is achieved - -We first draw on the Bergh et al (2016) data set to estimate the means and the variance-covariance matrix. In this data set, gender is a factor with two levels, male and female. We first need to transform this categorical variable into an integer variable, for instance coding male = 0 and female = 1. - -```{r means and cov mediation} - -Bergh_int <- Bergh -Bergh_int$gender <- ifelse(Bergh_int$gender == "male", 0, ifelse(Bergh_int$gender == "female", 1, NA)) - -attach(Bergh_int) - -#store means -means_mediation <- c(mean(gender), mean(EP), mean(SP), mean(HP), mean(DP), mean(O1), mean(O2), mean(O3)) |> round(2) - -#store covarainces -cov_mediation <- cov(cbind(gender, EP, SP, HP, DP, O1, O2, O3)) |> round(2) - - -``` - -::: {.callout-tip} - -If we were interested in a mediation model with only manifest variables, we could also use a useful shiny app developed by Schoemann et al. (2017), which can be accessed via [https://schoemanna.shinyapps.io/mc_power_med/](https://schoemanna.shinyapps.io/mc_power_med/). This app currently enables power analyses for some manifest mediation models: Mediation with (i) one mediator, (ii) two parallel mediators, (iii) two serial mediators, (iv) and three parallel mediators. But as we want to incorporate `generalized prejudice` and `openness to experience` as latent variables, we need to write a simulation-based power analyses ourselves. - -::: - - -Now, we specify the statistical mediation model in `lavaan`. - -```{r specify mediation model} - -#specify mediation model -model_mediation <- ' - - # measurement model - generalized_prejudice =~ EP + DP + SP + HP - openness =~ O1 + O2 + O3 - - # direct effect - generalized_prejudice ~ c*gender - - # mediator - openness ~ a*gender - generalized_prejudice ~ b*openness - - - # indirect effect (a*b) - ab := a*b - - # total effect - total := c + (a*b) - -' - -``` - -To verify that this model syntax works properly, we can fit this model to the data set provided by Bergh et al. (2016). - -```{r test mediation model with the pilot data} - -#fit the SEM to the simulated data set -fit_mediation <- sem(model_mediation, data = Bergh_int) - -#display the results -summary(fit_mediation) - -``` - -Now, everything is set up to run the actual power analysis. In the following chunk, we repeatedly simulate data from the specified population and store the p-value of the indirect effect while varying the sample size in a range from 500 to 1500. - -```{r simulate data mediation, cache=TRUE, warning=FALSE} - -#Set seed to make results reproducible -set.seed(21364) - -#test ns between 100 and 1500 -ns_mediation <- seq(500, 1500, by=50) - -#prepare empty vector to store results -result_mediation <- data.frame() - -#iterations -iterations_mediation <- 500 - -#write function -sim_mediation <- function(n, model, mu, sigma) { - - - simulated_data_mediation <- Rfast::rmvnorm(n = n, mu = mu, sigma = sigma) |> as.data.frame() - fit_mediation_simulated <- sem(model, data = simulated_data_mediation) - - p_value_mediation <- parameterestimates(fit_mediation_simulated)[21,]$pvalue - return(p_value_mediation) - } - - -#replicate function with varying ns -for (n in ns_mediation) { - -p_values_mediation <- future_replicate(iterations_mediation, sim_mediation(n = n, model = model_mediation, mu = means_mediation, sigma = cov_mediation), future.seed=TRUE) -result_mediation <- rbind(result_mediation, data.frame( - n = n, - power = sum(p_values_mediation < .005)/iterations_mediation) - ) - -#The following line of code can be used to track the progress of the simulations -#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time -#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line -#message(paste("Progress info: Simulations completed for n =", n)) - -} -``` - -Let's plot the results: - -```{r plot power curve mediation} - -ggplot(result_mediation, aes(x=n, y=power)) + geom_point() + geom_line() + scale_y_continuous(n.breaks = 10) + scale_x_continuous(n.breaks = 20) + geom_hline(yintercept= 0.8, color = "red") - -``` - -This shows that roughly 1,300 to 1,400 participants will be needed to obtain sufficient power under the assumptions we specified. To achieve a more precise estimate, just increase the number of iterations (and get a cup of coffee while you wait for the results 😅) - -# References - -Bergh, R., Akrami, N., Sidanius, J., & Sibley, C. G. (2016). Is group membership necessary for understanding generalized prejudice? A re-evaluation of why prejudices are interrelated. Journal of Personality and Social Psychology, 111(3), 367–395. [https://doi.org/10.1037/pspi0000064](https://www.researchgate.net/publication/306939541_Is_group_membership_necessary_for_understanding_generalized_prejudice_A_re-evaluation_of_why_prejudices_are_interrelated) - -Perugini, M., Gallucci, M., & Costantini, G. (2014). Safeguard power as a protection against imprecise power estimates. Perspectives on Psychological Science, 9(3), 319--332. - -Schoemann, A. M., Boulton, A. J., & Short, S. D. (2017). Determining power and sample size for simple and complex mediation models. Social Psychological and Personality Science, 8(4), 379–386. [https://doi.org/10.1177/1948550617715068](https://www.researchgate.net/publication/317631067_Determining_Power_and_Sample_Size_for_Simple_and_Complex_Mediation_Models) - -Wang, Y. A., & Rhemtulla, M. (2021). Power analysis for parameter estimation in structural equation modeling: A discussion and tutorial. Advances in Methods and Practices in Psychological Science, 4(1), 1–17. [https://doi.org/10.1177/2515245920918253](https://doi.org/10.1177/2515245920918253) - +--- +title: "Ch. 5: Structural Equation Models" +format: html +author: Moritz Fischer +execute: + cache: true +project-type: website +--- + +*Reading/working time: ~50 min.* + +```{r install packages, message=FALSE, warning=FALSE} +#install.packages(c("lavaan", "ggplot2", "Rfast", "MBESS", "future.apply"), dependencies = TRUE) + +library("lavaan") +library("Rfast") +library("ggplot2") +library("dplyr") +library("MBESS") +library(future.apply) +# this initializes parallel processing, for faster code execution +# By default, it uses all available cores +plan(multisession) +``` + +In this chapter, we will focus on some rather simple Structural Equation Models (SEM). The goal is to illustrate how simulations can be used to estimate statistical power to detect a given effect in a SEM. In the context of SEMs, the focal effect may be for instance a fit index (e.g., Chi-Square, Root Mean Square Error of Approximation, etc.) or a model coefficient (e.g., a regression coefficient for the association between two latent factors). In this chapter, we only focus on the latter, that is power analyses for regression coefficients in the context of SEM. Please see the bonus chapter titled "[Power analysis for fit indices in SEM](https://lmu-osc.github.io/Simulations-for-Advanced-Power-Analyses/SEM_fit_index.html)" if you're interested in power analyses for fit indices. + +::: {.callout-note} +In this chapter, we'll use the parallelization techniques described in the Chapter ["Bonus: Optimizing R code for speed"](optimizing_code.qmd), otherwise the simulations are too slow. If you wonder about the unknown commands in the code, please read the bonus chapter to learn how to considerably speed up your code! But nonetheless, running some of the chunks in this chapter will require quite some time. We therefore decided to use 500 iterations per simulation only in this chapter; please keep in mind that you should use more iterations in order to obtain more precise estimates! +::: + +# A simulation-based power analysis for a single regression coefficient between latent variables + +Let's consider the following example: We are planning to conduct a study investigating whether people's openness to experience (as conceptualized in the big five personality traits) relates negatively to generalized prejudice, that is a latent variable comprising prejudice towards different social groups (e.g., women, foreigners, homosexuals, disabled people). We could plan to scrutinize this prediction with a SEM in which both openness to experience and generalized prejudice are modeled as latent variables. + +::: {.callout-note} + +Please note that the predictions used as examples in this chapter are not necessarily theoretically meaningful hypotheses, we merely use them to illustrate the mechanics of simulation-based power analyses in the context of SEM. + +::: + + +## Let's get some real data as starting point + +Just like for any other simulation-based power analysis, we first need to come up with plausible estimates of the distribution of the (manifest) variables. For the sake of simplicity, let's assume that there is a published study that measured manifestations of our two latent variables and that the corresponding data set is publicly available. For the purpose of this tutorial, we will draw on a publication by Bergh et al ([2016](https://www.researchgate.net/publication/306939541_Is_group_membership_necessary_for_understanding_generalized_prejudice_A_re-evaluation_of_why_prejudices_are_interrelated)) and the corresponding data set which has been made accessible as part of the `MPsychoR` package. Let's take a look at this data set. + + +```{r load data, message=FALSE} + +#install.packages("MPsychoR") +library(MPsychoR) + +data("Bergh") +attach(Bergh) + +#let's take a look +head(Bergh) +tail(Bergh) + +``` + +This data set comprises `r ncol(Bergh)` variables measured in `r nrow(Bergh)` participants. For now, we will focus on the following measured variables: + +- `EP` is a continuous variable measuring ethnic prejudice. +- `SP` is a continuous variable measuring sexism. +- `HP` is a continuous variable measuring sexual prejudice towards gays and lesbians. +- `DP` is a continuous variable measuring prejudice toward people with disabilities. +- `O1`, `O2`, and `O3` are three items measuring openness to experience. + +To get an impression of this data, we look at the correlations of the variables we're interested in. + +```{r correlations SEM} + +cor(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2) + +``` + +As we have discussed in the previous chapters, the starting point of every simulation-based power analysis is to specify the population parameters of the variables of interest. In our example, we can estimate the population parameters from the study by Bergh et al. (2016). We start by calculating the means of the variables, rounding them generously, and storing them in a vector called `means_vector`. + +```{r mean vector SEM} + +#store means +means_vector <- c(mean(EP), mean(SP), mean(HP), mean(DP), mean(O1), mean(O2), mean(O3)) |> round(2) + +#Let's take a look +means_vector + +``` + +We also need the variance-covariance matrix of our variables in order to simulate data. Luckily, we can estimate this from the Bergh et al. data as well. There are two ways to do this. First, we can use the `cov` function to obtain the variance-covariance matrix. This matrix incorporates the variance of each variable on the diagonal, and the covariances in the remaining cells. + +```{r cov matrix SEM} + +#store covariances +cov_mat <- cov(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2) + +#Let's take a look +cov_mat + +``` + +This works well as long as we have a data set (e.g., from a pilot study or published work) to estimate the variances and covariances. In other cases, however, we might not have access to such a data set. In this case, we might only have a correlation table that was provided in a published paper. But that's no problem either, as we can transform the correlations and standard deviations of the variables of interest into a variance-covariance matrix. The following chunk shows how this works by using the `cor2cov` function from the `MBESS` package. + +```{r sd vector and correlations SEM} + +#store correlation matrix +cor_mat <- cor(cbind(EP, SP, HP, DP, O1, O2, O3)) + +#store standard deviations +sd_vector <- c(sd(EP), sd(SP), sd(HP), sd(DP), sd(O1), sd(O2), sd(O3)) + +#transform correlations and standard deviations into variance-covariance matrix +cov_mat2 <- MBESS::cor2cov(cor.mat = cor_mat, sd = sd_vector) |> as.data.frame() |> round(2) + +#Let's take a look +cov_mat2 + +``` + +Let's do a plausibility check: Did the two ways to estimate the variance-covariance matrix lead to the same results? + +```{r plausibility check SEM} + +cov_mat == cov_mat2 + +``` + +Indeed, this worked! Both procedures lead to the exact same variance-covariance matrix. Now that we have an approximation of the variance-covariance matrix, we use the `rmvnorm` function from the `Rfast` package to simulate data from a multivariate normal distribution. The following code simulates `n = 50` observations from the specified population. + +```{r simulate data SEM} + +#Set seed to make results reproducible +set.seed(21364) + +#simulate data +my_first_simulated_data <- Rfast::rmvnorm(n = 50, mu=means_vector, sigma = cov_mat) |> as.data.frame() + +#Let's take a look +head(my_first_simulated_data) + +``` + +We could now fit a SEM to this simulated data set and check whether the regression coefficient modelling the association between openness to experience and generalized prejudice is significant at an $\alpha$-level of .005. We will work with the `lavaan` package to fit SEMs. + +```{r analyze data SEM} + +#specify SEM +model_sem <- "generalized_prejudice =~ EP + DP + SP + HP + openness =~ O1 + O2 + O3 + generalized_prejudice ~ openness" + +#fit the SEM to the simulated data set +fit_sem <- sem(model_sem, data = my_first_simulated_data) + +#display the results +summary(fit_sem) + +``` + +The results show that in this case, the regression coefficient is `r lavaan::parameterestimates(fit_sem)[8,]$est |> round(2)` which is significant with p = `r lavaan::parameterestimates(fit_sem)[8,]$pvalue |> round(3)`. But, actually, it is not our primary interest to see whether this particular simulated data set results in a significant regression coefficient. Rather, we want to know how many of a theoretically infinite number of simulations yield a significant p-value of the focal regression coefficient. Thus, as in the previous chapters, we now repeatedly simulate data sets of a certain size (say, 50 observations) from the specified population and store the results of the focal test (here: the p-value of the regression coefficient) in a vector called `p_values`. + +```{r multiple iterations SEM, warning=FALSE} + +#Set seed to make results reproducible +set.seed(21364) + +#let's do 500 iterations +iterations <- 500 + +#prepare an empty NA vector with 500 slots +p_values <- rep(NA, iterations) + +#sample size per iteration +n <- 50 + + +#simulate data +for(i in 1:iterations){ + + simulated_data <- Rfast::rmvnorm(n = n, mu = means_vector, sigma = cov_mat) |> as.data.frame() + fit_sem_simulated <- sem(model_sem, data = simulated_data) + + p_values[i] <- parameterestimates(fit_sem_simulated)[8,]$pvalue + +} + +``` + +How many of our 500 virtual samples would have found a significant p-value (i.e., p < .005)? + +```{r results SEM} + +#frequency table +table(p_values < .005) + +#percentage of significant results +sum(p_values < .005)/iterations*100 + +``` + +Only `r round((sum(p_values < .005)*100/iterations),2)`% of samples with the same size of $n=50$ result in a significant p-value. We conclude that $n=50$ observations seems to be insufficient, as the power with these parameters is lower than 80%. + +## Sample size planning: Find the necessary sample size + +But how many observations do we need to find the presumed effect with a power of 80%? Like before, we can now systematically vary certain parameters (e.g., sample size) of our simulation and see how that affects power. We could, for example, vary the sample size in a range from 30 to 200. Running these simulations typically requires quite some computing time. + + +```{r power analysis SEM, warning=FALSE} + +#Set seed to make results reproducible +set.seed(21364) + +#test ns between 30 and 200 +ns_sem <- seq(30, 200, by=10) + +#prepare empty vector to store results +result_sem <- data.frame() + +#set number of iterations +iterations_sem <- 500 + +#write function +sim_sem <- function(n, model, mu, sigma) { + + + simulated_data <- Rfast::rmvnorm(n = n, mu = mu, sigma = sigma) |> as.data.frame() + fit_sem_simulated <- sem(model, data = simulated_data) + p_value_sem <- parameterestimates(fit_sem_simulated)[8,]$pvalue + return(p_value_sem) + + } + + +#replicate function with varying ns +for (n in ns_sem) { + +p_values_sem <- future_replicate(iterations_sem, sim_sem(n = n, model = model_sem, mu = means_vector, sigma = cov_mat), future.seed=TRUE) +result_sem <- rbind(result_sem, data.frame( + n = n, + power = sum(p_values_sem < .005)/iterations_sem) + ) + +#The following line of code can be used to track the progress of the simulations +#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time +#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line +#message(paste("Progress info: Simulations completed for n =", n)) + + +} + +``` + +Let's plot the results: + +```{r plot power curve SEM, warning=FALSE} + +ggplot(result_sem, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red") + +``` + +This graph suggests that we need a sample size of approximately 50 participants to reach a power of 80% with the given population estimates. That's all it takes to run a power analysis for a SEM! + +In the following two sub-paragraphs, we would like to present two alternatives and/or extensions to this first way of doing a power analysis for a SEM. First, we would like to present an alternative way to simulate data for SEMs, i.e. by using a built-in function in the `lavaan` package. Second, we would like to show how a "safeguard" approach to power analysis can be used within the context of SEM. + +## Using the lavaan syntax to simulate data + +In our opening example in this chapter, we used the `rmvnorm` function from the `Rfast`package to simulate data based on the means of the manifest variables as well as their variance-covariance matrix. An alternative to this procedure is to use a built-in function in the `lavaan` package, that is, the `simulateData()` function. The main idea here is to provide this function with a lavaan model that specifies all relevant population parameters and then to use this function to directly simulate data. + +More specifically, we need to incorporate the factor loadings, regression coefficients and (residual) variances of all latent and manifest variables in the lavaan syntax. As these parameters are hardly ever known without a previous study, we will again draw on the results from the data by Bergh et al (2016). Let's again take a look at the results of our SEM when applying it to this data set. + +```{r fit Bergh} + +#fit the SEM to the pilot data set +fit_bergh <- sem(model_sem, data = Bergh) + +#display the results +summary(fit_bergh) + +``` + +In order to use the `simulateData()` function, we can take the estimates from this previous study and plug them into the lavaan syntax in the following chunk. + +```{r specify lavaan syntax} + +#Set seed to make results reproducible +set.seed(21364) + +#specify SEM +model_fully_specified <- + +"generalized_prejudice =~ 1*EP + 0.71*DP + 0.91*SP + 1*HP +openness =~ 1*O1 + 0.93*O2 + 1.14*O3 +generalized_prejudice ~ -0.77*openness + + +generalized_prejudice ~~ 0.19*generalized_prejudice +openness ~~ 0.16*openness +EP ~~ 0.21*EP +DP ~~ 0.14*DP +SP ~~ 0.23*SP +HP ~~ 2.12*HP +O1 ~~ 0.07*O1 +O2 ~~ 0.08*O2 +O3 ~~ 0.05*O3 + +" + +#lets try this +sim_lavaan <- simulateData(model_fully_specified, sample.nobs=100) +head(sim_lavaan) + + +``` + +The next step is to integrate this code into our first simulation-based power analysis in this chapter, that is, to replace the data simulation process using the `rmvnorm` function from the `Rfast` package with this new method using `simulateData()` from the `lavaan` package. To this end, I am adapting the `power analysis SEM` chunk from above accordingly. + +```{r simulateData, warning=FALSE} + +#Set seed to make results reproducible +set.seed(21364) + +#test ns between 30 and 200 +ns_sem <- seq(30, 200, by=10) + +#prepare empty vector to store results +result_sem <- data.frame() + +#set number of iterations +iterations_sem <- 500 + +#write function +sim_sem_lavaan <- function(n, model) { + + + sim_lavaan <- simulateData(model = model, sample.nobs=n) + fit_sem_simulated <- sem(model_sem, data = sim_lavaan) + p_value_sem <- parameterestimates(fit_sem_simulated)[8,]$pvalue + return(p_value_sem) + +} + + +#replicate function with varying ns +for (n in ns_sem) { + + p_values_sem <- future_replicate(iterations_sem, sim_sem_lavaan(n = n, model = model_fully_specified), future.seed=TRUE) + result_sem <- rbind(result_sem, data.frame( + n = n, + power = sum(p_values_sem < .005, na.rm = TRUE)/iterations_sem) + ) + +#The following line of code can be used to track the progress of the simulations +#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time +#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line +#message(paste("Progress info: Simulations completed for n =", n)) + +} + + +``` + +Let's plot this again. + +```{r plot power curve SEM lavaan, warning=FALSE} + +ggplot(result_sem, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red") + +``` + +Here, we conclude that approx. 55 participants would be needed to achieve 80% power. The slight difference compared to the previous power analysis should be explained by the fact that we rounded the numbers that define our statistical populations and that we only used 500 Monte Carlo iterations -- these differences should decrease with an increasing number of iterations. + +::: {.callout-tip} + +Wang and Rhemtulla ([2021](https://doi.org/10.1177/2515245920918253)) developed a shiny app that can do power analyses for SEMs in a similar fashion, but that additionally provides a point-and-click interface. You can use it here, for instance, to replicate the results of this simulation-based power analysis: [https://yilinandrewang.shinyapps.io/pwrSEM/](https://yilinandrewang.shinyapps.io/pwrSEM/) + +::: + +## A safeguard power approach for SEMs + +Of note, the two power analyses for our SEM we have conducted so far used the observed effect size from the Bergh et al. (2016) data set as an estimate of the "true" effect size. But, as this point estimate may be imprecise (e.g., because of publication bias), it seems reasonable to use a more conservative estimate of the true effect size. One more conservative approach in this context is the safeguard power approach (Perugini et al., [2014](https://doi.org/10.1177/1745691614528519)), which we have already applied in Chapter 1 ([Linear Model I: a single dichotomous predictor](LM1.qmd)). + +Basically, all need to do in order to account for variability of observed effect sizes is to calculate a 60%-confidence interval around the point estimate of the observed effect size from our pilot data and to use the more conservative bound of this confidence interval (here: the upper bound) as our new effect size estimate. This can be easily done with the `parameterestimates` function from the `lavaan` package which takes the level of the confidence interval as an input parameter. Let's use this function on our object `fit_bergh` which stores the results of our SEM in the `Bergh` data set. + +```{r safeguard estimation} + +parameterestimates(fit_bergh, level = .60) + +``` + +This output shows that the upper bound of the 60% confidence interval around the focal regression coefficient is `r lavaan::parameterestimates(fit_bergh, level = .60)[8,]$ci.upper |> round(2)`. We can now use this as our new and more conservative effect size estimate. We can for example insert this value into our simulation using the lavaan syntax. For this purpose, we copy the chunk from above and simply replace the previous effect size estimate (`r lavaan::parameterestimates(fit_bergh)[8,]$est |> round(2)`) with the new estimate (`r lavaan::parameterestimates(fit_bergh, level = .60)[8,]$ci.upper |> round(2)`), while keeping all other parameters that define this data set. + + +```{r lavaan model safeguard power} + +#Set seed to make results reproducible +set.seed(21364) + +#specify SEM +model_fully_specified_safeguard <- + +"generalized_prejudice =~ 1*EP + 0.71*DP + 0.91*SP + 1*HP +openness =~ 1*O1 + 0.93*O2 + 1.14*O3 +generalized_prejudice ~ -0.72*openness + + +generalized_prejudice ~~ 0.19*generalized_prejudice +openness ~~ 0.16*openness +EP ~~ 0.21*EP +DP ~~ 0.14*DP +SP ~~ 0.23*SP +HP ~~ 2.12*HP +O1 ~~ 0.07*O1 +O2 ~~ 0.08*O2 +O3 ~~ 0.05*O3 + +" + +#lets try this +sim_lavaan_safeguard <- simulateData(model_fully_specified_safeguard, sample.nobs=100) +head(sim_lavaan_safeguard) + +``` + +Now, everything is ready for the actual safeguard power analysis. We can re-use the `sim_sem_lavaan` function we have defined above. Let's see what we get here! + +```{r safeguard power analysis, warning=FALSE} + +#Set seed to make results reproducible +set.seed(21364) + +#prepare empty vector to store results +result_sem_safeguard <- data.frame() + +#replicate function with varying ns +for (n in ns_sem) { + + p_values_sem_safeguard <- future_replicate(iterations_sem, sim_sem_lavaan(n = n, model = model_fully_specified_safeguard), future.seed=TRUE) + result_sem_safeguard <- rbind(result_sem_safeguard, data.frame( + n = n, + power = sum(p_values_sem_safeguard < .005, na.rm = TRUE)/iterations_sem) + ) + +#The following line of code can be used to track the progress of the simulations +#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time +#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line +#message(paste("Progress info: Simulations completed for n =", n)) + +} + +ggplot(result_sem_safeguard, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red") + +``` +This safeguard power analysis yields a required sample size of ca. 63 participants. + +::: {.callout-note} + +In addition to this safeguard power approach, we would also have liked to derive a smallest effect size of interest (SESOI). However, no prior studies on SESOI in the context of personality/stereotypes were available and the measures/response scales used by Bergh et al (2016) were only vaguely reported in their manuscript, thereby making it difficult to derive a meaningful SESOI. We therefore only report a safeguard approach but no SESOI approach here. + +::: + +# A simulation-based power analysis for a mediation model with latent variables + +Sometimes, researchers not only wish to investigate whether and how two (latent) variables related to each other, but also whether the association between two (manifest or latent) variables is mediated by a third variable. We will run a power analysis for such a latent mediation model, investigating whether gender affects generalized prejudice (fully or partially) through openness to experience, while using the Bergh et al (2016) dataset as a pilot study. We can repeat the same steps as in our previous power analysis, while incorporating gender into our analysis. Specifically, we will follow these steps: + +1. find plausible estimates of the population parameters +2. specify the statistical model +3. simulate data from this population +4. compute the index of interest (e.g., the p-value) and store the results +5. repeat steps 2) and 3) multiple times +6. count how many samples would have detected the specified effect (i.e., compute the statistical power) +7. vary your simulation parameters until the desired level of power (e.g., 80%) is achieved + +We first draw on the Bergh et al (2016) data set to estimate the means and the variance-covariance matrix. In this data set, gender is a factor with two levels, male and female. We first need to transform this categorical variable into an integer variable, for instance coding male = 0 and female = 1. + +```{r means and cov mediation} + +Bergh_int <- Bergh +Bergh_int$gender <- ifelse(Bergh_int$gender == "male", 0, ifelse(Bergh_int$gender == "female", 1, NA)) + +attach(Bergh_int) + +#store means +means_mediation <- c(mean(gender), mean(EP), mean(SP), mean(HP), mean(DP), mean(O1), mean(O2), mean(O3)) |> round(2) + +#store covarainces +cov_mediation <- cov(cbind(gender, EP, SP, HP, DP, O1, O2, O3)) |> round(2) + + +``` + +::: {.callout-tip} + +If we were interested in a mediation model with only manifest variables, we could also use a useful shiny app developed by Schoemann et al. (2017), which can be accessed via [https://schoemanna.shinyapps.io/mc_power_med/](https://schoemanna.shinyapps.io/mc_power_med/). This app currently enables power analyses for some manifest mediation models: Mediation with (i) one mediator, (ii) two parallel mediators, (iii) two serial mediators, (iv) and three parallel mediators. But as we want to incorporate `generalized prejudice` and `openness to experience` as latent variables, we need to write a simulation-based power analyses ourselves. + +::: + + +Now, we specify the statistical mediation model in `lavaan`. + +```{r specify mediation model} + +#specify mediation model +model_mediation <- ' + + # measurement model + generalized_prejudice =~ EP + DP + SP + HP + openness =~ O1 + O2 + O3 + + # direct effect + generalized_prejudice ~ c*gender + + # mediator + openness ~ a*gender + generalized_prejudice ~ b*openness + + + # indirect effect (a*b) + ab := a*b + + # total effect + total := c + (a*b) + +' + +``` + +To verify that this model syntax works properly, we can fit this model to the data set provided by Bergh et al. (2016). + +```{r test mediation model with the pilot data} + +#fit the SEM to the simulated data set +fit_mediation <- sem(model_mediation, data = Bergh_int) + +#display the results +summary(fit_mediation) + +``` + +Now, everything is set up to run the actual power analysis. In the following chunk, we repeatedly simulate data from the specified population and store the p-value of the indirect effect while varying the sample size in a range from 500 to 1500. + +```{r simulate data mediation, cache=TRUE, warning=FALSE} + +#Set seed to make results reproducible +set.seed(21364) + +#test ns between 100 and 1500 +ns_mediation <- seq(500, 1500, by=50) + +#prepare empty vector to store results +result_mediation <- data.frame() + +#iterations +iterations_mediation <- 500 + +#write function +sim_mediation <- function(n, model, mu, sigma) { + + + simulated_data_mediation <- Rfast::rmvnorm(n = n, mu = mu, sigma = sigma) |> as.data.frame() + fit_mediation_simulated <- sem(model, data = simulated_data_mediation) + + p_value_mediation <- parameterestimates(fit_mediation_simulated)[21,]$pvalue + return(p_value_mediation) + } + + +#replicate function with varying ns +for (n in ns_mediation) { + +p_values_mediation <- future_replicate(iterations_mediation, sim_mediation(n = n, model = model_mediation, mu = means_mediation, sigma = cov_mediation), future.seed=TRUE) +result_mediation <- rbind(result_mediation, data.frame( + n = n, + power = sum(p_values_mediation < .005)/iterations_mediation) + ) + +#The following line of code can be used to track the progress of the simulations +#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time +#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line +#message(paste("Progress info: Simulations completed for n =", n)) + +} +``` + +Let's plot the results: + +```{r plot power curve mediation} + +ggplot(result_mediation, aes(x=n, y=power)) + geom_point() + geom_line() + scale_y_continuous(n.breaks = 10) + scale_x_continuous(n.breaks = 20) + geom_hline(yintercept= 0.8, color = "red") + +``` + +This shows that roughly 1,300 to 1,400 participants will be needed to obtain sufficient power under the assumptions we specified. To achieve a more precise estimate, just increase the number of iterations (and get a cup of coffee while you wait for the results 😅) + +# References + +Bergh, R., Akrami, N., Sidanius, J., & Sibley, C. G. (2016). Is group membership necessary for understanding generalized prejudice? A re-evaluation of why prejudices are interrelated. Journal of Personality and Social Psychology, 111(3), 367–395. [https://doi.org/10.1037/pspi0000064](https://www.researchgate.net/publication/306939541_Is_group_membership_necessary_for_understanding_generalized_prejudice_A_re-evaluation_of_why_prejudices_are_interrelated) + +Perugini, M., Gallucci, M., & Costantini, G. (2014). Safeguard power as a protection against imprecise power estimates. Perspectives on Psychological Science, 9(3), 319--332. + +Schoemann, A. M., Boulton, A. J., & Short, S. D. (2017). Determining power and sample size for simple and complex mediation models. Social Psychological and Personality Science, 8(4), 379–386. [https://doi.org/10.1177/1948550617715068](https://www.researchgate.net/publication/317631067_Determining_Power_and_Sample_Size_for_Simple_and_Complex_Mediation_Models) + +Wang, Y. A., & Rhemtulla, M. (2021). Power analysis for parameter estimation in structural equation modeling: A discussion and tutorial. Advances in Methods and Practices in Psychological Science, 4(1), 1–17. [https://doi.org/10.1177/2515245920918253](https://doi.org/10.1177/2515245920918253) + diff --git a/SEM_cache/html/__packages b/SEM_cache/html/__packages deleted file mode 100644 index 21ecaee..0000000 --- a/SEM_cache/html/__packages +++ /dev/null @@ -1,13 +0,0 @@ -colorDF -prettycode -lavaan -MASS -ggplot2 -dplyr -MBESS -MPsychoR -future -future.apply -Rcpp -RcppZiggurat -Rfast diff --git a/SEM_cache/html/analyze data SEM_8020a3a0c6ce44ac65df04f38cb43898.RData b/SEM_cache/html/analyze data SEM_8020a3a0c6ce44ac65df04f38cb43898.RData deleted file mode 100644 index 0e2cece..0000000 Binary files a/SEM_cache/html/analyze data SEM_8020a3a0c6ce44ac65df04f38cb43898.RData and /dev/null differ diff --git a/SEM_cache/html/analyze data SEM_8020a3a0c6ce44ac65df04f38cb43898.rdb b/SEM_cache/html/analyze data SEM_8020a3a0c6ce44ac65df04f38cb43898.rdb deleted file mode 100644 index e4095f2..0000000 Binary files a/SEM_cache/html/analyze data SEM_8020a3a0c6ce44ac65df04f38cb43898.rdb and /dev/null differ diff --git a/SEM_cache/html/analyze data SEM_8020a3a0c6ce44ac65df04f38cb43898.rdx b/SEM_cache/html/analyze data SEM_8020a3a0c6ce44ac65df04f38cb43898.rdx deleted file mode 100644 index 7bf993d..0000000 Binary files a/SEM_cache/html/analyze data SEM_8020a3a0c6ce44ac65df04f38cb43898.rdx and /dev/null differ diff --git a/SEM_cache/html/correlations SEM_80bd27f354f7f9f600ed79bbb5f0bc68.RData b/SEM_cache/html/correlations SEM_80bd27f354f7f9f600ed79bbb5f0bc68.RData deleted file mode 100644 index 33bf937..0000000 Binary files a/SEM_cache/html/correlations SEM_80bd27f354f7f9f600ed79bbb5f0bc68.RData and /dev/null differ diff --git a/SEM_cache/html/correlations SEM_80bd27f354f7f9f600ed79bbb5f0bc68.rdb b/SEM_cache/html/correlations SEM_80bd27f354f7f9f600ed79bbb5f0bc68.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_cache/html/correlations SEM_80bd27f354f7f9f600ed79bbb5f0bc68.rdx b/SEM_cache/html/correlations SEM_80bd27f354f7f9f600ed79bbb5f0bc68.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_cache/html/correlations SEM_80bd27f354f7f9f600ed79bbb5f0bc68.rdx and /dev/null differ diff --git a/SEM_cache/html/cov matrix SEM_11382f9285e3ddd5bcd28bf70e5a0feb.RData b/SEM_cache/html/cov matrix SEM_11382f9285e3ddd5bcd28bf70e5a0feb.RData deleted file mode 100644 index 7259250..0000000 Binary files a/SEM_cache/html/cov matrix SEM_11382f9285e3ddd5bcd28bf70e5a0feb.RData and /dev/null differ diff --git a/SEM_cache/html/cov matrix SEM_11382f9285e3ddd5bcd28bf70e5a0feb.rdb b/SEM_cache/html/cov matrix SEM_11382f9285e3ddd5bcd28bf70e5a0feb.rdb deleted file mode 100644 index d27dbc4..0000000 Binary files a/SEM_cache/html/cov matrix SEM_11382f9285e3ddd5bcd28bf70e5a0feb.rdb and /dev/null differ diff --git a/SEM_cache/html/cov matrix SEM_11382f9285e3ddd5bcd28bf70e5a0feb.rdx b/SEM_cache/html/cov matrix SEM_11382f9285e3ddd5bcd28bf70e5a0feb.rdx deleted file mode 100644 index c29baac..0000000 Binary files a/SEM_cache/html/cov matrix SEM_11382f9285e3ddd5bcd28bf70e5a0feb.rdx and /dev/null differ diff --git a/SEM_cache/html/fit Bergh_e1afb8caaa85093ec1f253febb6289a2.RData b/SEM_cache/html/fit Bergh_e1afb8caaa85093ec1f253febb6289a2.RData deleted file mode 100644 index 0f88eb8..0000000 Binary files a/SEM_cache/html/fit Bergh_e1afb8caaa85093ec1f253febb6289a2.RData and /dev/null differ diff --git a/SEM_cache/html/fit Bergh_e1afb8caaa85093ec1f253febb6289a2.rdb b/SEM_cache/html/fit Bergh_e1afb8caaa85093ec1f253febb6289a2.rdb deleted file mode 100644 index 369063d..0000000 Binary files a/SEM_cache/html/fit Bergh_e1afb8caaa85093ec1f253febb6289a2.rdb and /dev/null differ diff --git a/SEM_cache/html/fit Bergh_e1afb8caaa85093ec1f253febb6289a2.rdx b/SEM_cache/html/fit Bergh_e1afb8caaa85093ec1f253febb6289a2.rdx deleted file mode 100644 index a5c132e..0000000 Binary files a/SEM_cache/html/fit Bergh_e1afb8caaa85093ec1f253febb6289a2.rdx and /dev/null differ diff --git a/SEM_cache/html/install packages_9b87e864766a9dd9a77f541effbca817.RData b/SEM_cache/html/install packages_9b87e864766a9dd9a77f541effbca817.RData deleted file mode 100644 index 00693c7..0000000 Binary files a/SEM_cache/html/install packages_9b87e864766a9dd9a77f541effbca817.RData and /dev/null differ diff --git a/SEM_cache/html/install packages_9b87e864766a9dd9a77f541effbca817.rdb b/SEM_cache/html/install packages_9b87e864766a9dd9a77f541effbca817.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_cache/html/install packages_9b87e864766a9dd9a77f541effbca817.rdx b/SEM_cache/html/install packages_9b87e864766a9dd9a77f541effbca817.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_cache/html/install packages_9b87e864766a9dd9a77f541effbca817.rdx and /dev/null differ diff --git a/SEM_cache/html/lavaan model safeguard power_551b7645b9ead06b3ed5f6de16c663c0.RData b/SEM_cache/html/lavaan model safeguard power_551b7645b9ead06b3ed5f6de16c663c0.RData deleted file mode 100644 index 42f263b..0000000 Binary files a/SEM_cache/html/lavaan model safeguard power_551b7645b9ead06b3ed5f6de16c663c0.RData and /dev/null differ diff --git a/SEM_cache/html/lavaan model safeguard power_551b7645b9ead06b3ed5f6de16c663c0.rdb b/SEM_cache/html/lavaan model safeguard power_551b7645b9ead06b3ed5f6de16c663c0.rdb deleted file mode 100644 index a89a7cc..0000000 Binary files a/SEM_cache/html/lavaan model safeguard power_551b7645b9ead06b3ed5f6de16c663c0.rdb and /dev/null differ diff --git a/SEM_cache/html/lavaan model safeguard power_551b7645b9ead06b3ed5f6de16c663c0.rdx b/SEM_cache/html/lavaan model safeguard power_551b7645b9ead06b3ed5f6de16c663c0.rdx deleted file mode 100644 index 989b242..0000000 Binary files a/SEM_cache/html/lavaan model safeguard power_551b7645b9ead06b3ed5f6de16c663c0.rdx and /dev/null differ diff --git a/SEM_cache/html/load data_cc0422a3291a8777e36159fc8ad18d71.RData b/SEM_cache/html/load data_cc0422a3291a8777e36159fc8ad18d71.RData deleted file mode 100644 index d40f1c9..0000000 Binary files a/SEM_cache/html/load data_cc0422a3291a8777e36159fc8ad18d71.RData and /dev/null differ diff --git a/SEM_cache/html/load data_cc0422a3291a8777e36159fc8ad18d71.rdb b/SEM_cache/html/load data_cc0422a3291a8777e36159fc8ad18d71.rdb deleted file mode 100644 index 937f4b2..0000000 Binary files a/SEM_cache/html/load data_cc0422a3291a8777e36159fc8ad18d71.rdb and /dev/null differ diff --git a/SEM_cache/html/load data_cc0422a3291a8777e36159fc8ad18d71.rdx b/SEM_cache/html/load data_cc0422a3291a8777e36159fc8ad18d71.rdx deleted file mode 100644 index 904ff79..0000000 Binary files a/SEM_cache/html/load data_cc0422a3291a8777e36159fc8ad18d71.rdx and /dev/null differ diff --git a/SEM_cache/html/mean vector SEM_9fc3ff1170b7d271fa3da42914c47170.RData b/SEM_cache/html/mean vector SEM_9fc3ff1170b7d271fa3da42914c47170.RData deleted file mode 100644 index 0b32ef7..0000000 Binary files a/SEM_cache/html/mean vector SEM_9fc3ff1170b7d271fa3da42914c47170.RData and /dev/null differ diff --git a/SEM_cache/html/mean vector SEM_9fc3ff1170b7d271fa3da42914c47170.rdb b/SEM_cache/html/mean vector SEM_9fc3ff1170b7d271fa3da42914c47170.rdb deleted file mode 100644 index 8a99007..0000000 Binary files a/SEM_cache/html/mean vector SEM_9fc3ff1170b7d271fa3da42914c47170.rdb and /dev/null differ diff --git a/SEM_cache/html/mean vector SEM_9fc3ff1170b7d271fa3da42914c47170.rdx b/SEM_cache/html/mean vector SEM_9fc3ff1170b7d271fa3da42914c47170.rdx deleted file mode 100644 index 6f79705..0000000 Binary files a/SEM_cache/html/mean vector SEM_9fc3ff1170b7d271fa3da42914c47170.rdx and /dev/null differ diff --git a/SEM_cache/html/means and cov mediation_7269ea82b4985ba299ef9fcdbc94db1f.RData b/SEM_cache/html/means and cov mediation_7269ea82b4985ba299ef9fcdbc94db1f.RData deleted file mode 100644 index e938c9d..0000000 Binary files a/SEM_cache/html/means and cov mediation_7269ea82b4985ba299ef9fcdbc94db1f.RData and /dev/null differ diff --git a/SEM_cache/html/means and cov mediation_7269ea82b4985ba299ef9fcdbc94db1f.rdb b/SEM_cache/html/means and cov mediation_7269ea82b4985ba299ef9fcdbc94db1f.rdb deleted file mode 100644 index 9541237..0000000 Binary files a/SEM_cache/html/means and cov mediation_7269ea82b4985ba299ef9fcdbc94db1f.rdb and /dev/null differ diff --git a/SEM_cache/html/means and cov mediation_7269ea82b4985ba299ef9fcdbc94db1f.rdx b/SEM_cache/html/means and cov mediation_7269ea82b4985ba299ef9fcdbc94db1f.rdx deleted file mode 100644 index 5cf6250..0000000 Binary files a/SEM_cache/html/means and cov mediation_7269ea82b4985ba299ef9fcdbc94db1f.rdx and /dev/null differ diff --git a/SEM_cache/html/multiple iterations SEM_64989f959b31778a09d73dbeab916ba2.RData b/SEM_cache/html/multiple iterations SEM_64989f959b31778a09d73dbeab916ba2.RData deleted file mode 100644 index 1928aee..0000000 Binary files a/SEM_cache/html/multiple iterations SEM_64989f959b31778a09d73dbeab916ba2.RData and /dev/null differ diff --git a/SEM_cache/html/multiple iterations SEM_64989f959b31778a09d73dbeab916ba2.rdb b/SEM_cache/html/multiple iterations SEM_64989f959b31778a09d73dbeab916ba2.rdb deleted file mode 100644 index 00d7229..0000000 Binary files a/SEM_cache/html/multiple iterations SEM_64989f959b31778a09d73dbeab916ba2.rdb and /dev/null differ diff --git a/SEM_cache/html/multiple iterations SEM_64989f959b31778a09d73dbeab916ba2.rdx b/SEM_cache/html/multiple iterations SEM_64989f959b31778a09d73dbeab916ba2.rdx deleted file mode 100644 index c01a4d2..0000000 Binary files a/SEM_cache/html/multiple iterations SEM_64989f959b31778a09d73dbeab916ba2.rdx and /dev/null differ diff --git a/SEM_cache/html/plausibility check SEM_3281b4cb285cb4bc1d6c02b6d17929db.RData b/SEM_cache/html/plausibility check SEM_3281b4cb285cb4bc1d6c02b6d17929db.RData deleted file mode 100644 index fabfd7e..0000000 Binary files a/SEM_cache/html/plausibility check SEM_3281b4cb285cb4bc1d6c02b6d17929db.RData and /dev/null differ diff --git a/SEM_cache/html/plausibility check SEM_3281b4cb285cb4bc1d6c02b6d17929db.rdb b/SEM_cache/html/plausibility check SEM_3281b4cb285cb4bc1d6c02b6d17929db.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_cache/html/plausibility check SEM_3281b4cb285cb4bc1d6c02b6d17929db.rdx b/SEM_cache/html/plausibility check SEM_3281b4cb285cb4bc1d6c02b6d17929db.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_cache/html/plausibility check SEM_3281b4cb285cb4bc1d6c02b6d17929db.rdx and /dev/null differ diff --git a/SEM_cache/html/plot power curve SEM lavaan_41231634257cd8768d2ec016ee5bb896.RData b/SEM_cache/html/plot power curve SEM lavaan_41231634257cd8768d2ec016ee5bb896.RData deleted file mode 100644 index b38a48a..0000000 Binary files a/SEM_cache/html/plot power curve SEM lavaan_41231634257cd8768d2ec016ee5bb896.RData and /dev/null differ diff --git a/SEM_cache/html/plot power curve SEM lavaan_41231634257cd8768d2ec016ee5bb896.rdb b/SEM_cache/html/plot power curve SEM lavaan_41231634257cd8768d2ec016ee5bb896.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_cache/html/plot power curve SEM lavaan_41231634257cd8768d2ec016ee5bb896.rdx b/SEM_cache/html/plot power curve SEM lavaan_41231634257cd8768d2ec016ee5bb896.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_cache/html/plot power curve SEM lavaan_41231634257cd8768d2ec016ee5bb896.rdx and /dev/null differ diff --git a/SEM_cache/html/plot power curve SEM_a4628ce36e73e1f25db4394c87681eb2.RData b/SEM_cache/html/plot power curve SEM_a4628ce36e73e1f25db4394c87681eb2.RData deleted file mode 100644 index c649027..0000000 Binary files a/SEM_cache/html/plot power curve SEM_a4628ce36e73e1f25db4394c87681eb2.RData and /dev/null differ diff --git a/SEM_cache/html/plot power curve SEM_a4628ce36e73e1f25db4394c87681eb2.rdb b/SEM_cache/html/plot power curve SEM_a4628ce36e73e1f25db4394c87681eb2.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_cache/html/plot power curve SEM_a4628ce36e73e1f25db4394c87681eb2.rdx b/SEM_cache/html/plot power curve SEM_a4628ce36e73e1f25db4394c87681eb2.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_cache/html/plot power curve SEM_a4628ce36e73e1f25db4394c87681eb2.rdx and /dev/null differ diff --git a/SEM_cache/html/plot power curve mediation_033ccc00643f300d955b911fee594ae3.RData b/SEM_cache/html/plot power curve mediation_033ccc00643f300d955b911fee594ae3.RData deleted file mode 100644 index 52c3da3..0000000 Binary files a/SEM_cache/html/plot power curve mediation_033ccc00643f300d955b911fee594ae3.RData and /dev/null differ diff --git a/SEM_cache/html/plot power curve mediation_033ccc00643f300d955b911fee594ae3.rdb b/SEM_cache/html/plot power curve mediation_033ccc00643f300d955b911fee594ae3.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_cache/html/plot power curve mediation_033ccc00643f300d955b911fee594ae3.rdx b/SEM_cache/html/plot power curve mediation_033ccc00643f300d955b911fee594ae3.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_cache/html/plot power curve mediation_033ccc00643f300d955b911fee594ae3.rdx and /dev/null differ diff --git a/SEM_cache/html/power analysis SEM_09d609cc28140020e938f2d2aae4c4a1.RData b/SEM_cache/html/power analysis SEM_09d609cc28140020e938f2d2aae4c4a1.RData deleted file mode 100644 index dddaa60..0000000 Binary files a/SEM_cache/html/power analysis SEM_09d609cc28140020e938f2d2aae4c4a1.RData and /dev/null differ diff --git a/SEM_cache/html/power analysis SEM_09d609cc28140020e938f2d2aae4c4a1.rdb b/SEM_cache/html/power analysis SEM_09d609cc28140020e938f2d2aae4c4a1.rdb deleted file mode 100644 index 3095f3c..0000000 Binary files a/SEM_cache/html/power analysis SEM_09d609cc28140020e938f2d2aae4c4a1.rdb and /dev/null differ diff --git a/SEM_cache/html/power analysis SEM_09d609cc28140020e938f2d2aae4c4a1.rdx b/SEM_cache/html/power analysis SEM_09d609cc28140020e938f2d2aae4c4a1.rdx deleted file mode 100644 index cd7df36..0000000 Binary files a/SEM_cache/html/power analysis SEM_09d609cc28140020e938f2d2aae4c4a1.rdx and /dev/null differ diff --git a/SEM_cache/html/results SEM_b722f2f6682059f37021fa7e9eb37089.RData b/SEM_cache/html/results SEM_b722f2f6682059f37021fa7e9eb37089.RData deleted file mode 100644 index 111311b..0000000 Binary files a/SEM_cache/html/results SEM_b722f2f6682059f37021fa7e9eb37089.RData and /dev/null differ diff --git a/SEM_cache/html/results SEM_b722f2f6682059f37021fa7e9eb37089.rdb b/SEM_cache/html/results SEM_b722f2f6682059f37021fa7e9eb37089.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_cache/html/results SEM_b722f2f6682059f37021fa7e9eb37089.rdx b/SEM_cache/html/results SEM_b722f2f6682059f37021fa7e9eb37089.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_cache/html/results SEM_b722f2f6682059f37021fa7e9eb37089.rdx and /dev/null differ diff --git a/SEM_cache/html/safeguard estimation_27b97e6c194e1df6cde5ec639a8e22bf.RData b/SEM_cache/html/safeguard estimation_27b97e6c194e1df6cde5ec639a8e22bf.RData deleted file mode 100644 index 25ebcc6..0000000 Binary files a/SEM_cache/html/safeguard estimation_27b97e6c194e1df6cde5ec639a8e22bf.RData and /dev/null differ diff --git a/SEM_cache/html/safeguard estimation_27b97e6c194e1df6cde5ec639a8e22bf.rdb b/SEM_cache/html/safeguard estimation_27b97e6c194e1df6cde5ec639a8e22bf.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_cache/html/safeguard estimation_27b97e6c194e1df6cde5ec639a8e22bf.rdx b/SEM_cache/html/safeguard estimation_27b97e6c194e1df6cde5ec639a8e22bf.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_cache/html/safeguard estimation_27b97e6c194e1df6cde5ec639a8e22bf.rdx and /dev/null differ diff --git a/SEM_cache/html/safeguard power analysis_3e0b88936f27ff1bdafdc395304a1b2a.RData b/SEM_cache/html/safeguard power analysis_3e0b88936f27ff1bdafdc395304a1b2a.RData deleted file mode 100644 index 5f2525f..0000000 Binary files a/SEM_cache/html/safeguard power analysis_3e0b88936f27ff1bdafdc395304a1b2a.RData and /dev/null differ diff --git a/SEM_cache/html/safeguard power analysis_3e0b88936f27ff1bdafdc395304a1b2a.rdb b/SEM_cache/html/safeguard power analysis_3e0b88936f27ff1bdafdc395304a1b2a.rdb deleted file mode 100644 index 6dda871..0000000 Binary files a/SEM_cache/html/safeguard power analysis_3e0b88936f27ff1bdafdc395304a1b2a.rdb and /dev/null differ diff --git a/SEM_cache/html/safeguard power analysis_3e0b88936f27ff1bdafdc395304a1b2a.rdx b/SEM_cache/html/safeguard power analysis_3e0b88936f27ff1bdafdc395304a1b2a.rdx deleted file mode 100644 index 1f3f892..0000000 Binary files a/SEM_cache/html/safeguard power analysis_3e0b88936f27ff1bdafdc395304a1b2a.rdx and /dev/null differ diff --git a/SEM_cache/html/sd vector and correlations SEM_5819dbce04de0a40a78cb66631833abb.RData b/SEM_cache/html/sd vector and correlations SEM_5819dbce04de0a40a78cb66631833abb.RData deleted file mode 100644 index 3c7cd4f..0000000 Binary files a/SEM_cache/html/sd vector and correlations SEM_5819dbce04de0a40a78cb66631833abb.RData and /dev/null differ diff --git a/SEM_cache/html/sd vector and correlations SEM_5819dbce04de0a40a78cb66631833abb.rdb b/SEM_cache/html/sd vector and correlations SEM_5819dbce04de0a40a78cb66631833abb.rdb deleted file mode 100644 index 1885e44..0000000 Binary files a/SEM_cache/html/sd vector and correlations SEM_5819dbce04de0a40a78cb66631833abb.rdb and /dev/null differ diff --git a/SEM_cache/html/sd vector and correlations SEM_5819dbce04de0a40a78cb66631833abb.rdx b/SEM_cache/html/sd vector and correlations SEM_5819dbce04de0a40a78cb66631833abb.rdx deleted file mode 100644 index 452fa00..0000000 Binary files a/SEM_cache/html/sd vector and correlations SEM_5819dbce04de0a40a78cb66631833abb.rdx and /dev/null differ diff --git a/SEM_cache/html/simulate data SEM_52c5cb6e0fb466a83308047303e465af.RData b/SEM_cache/html/simulate data SEM_52c5cb6e0fb466a83308047303e465af.RData deleted file mode 100644 index e099285..0000000 Binary files a/SEM_cache/html/simulate data SEM_52c5cb6e0fb466a83308047303e465af.RData and /dev/null differ diff --git a/SEM_cache/html/simulate data SEM_52c5cb6e0fb466a83308047303e465af.rdb b/SEM_cache/html/simulate data SEM_52c5cb6e0fb466a83308047303e465af.rdb deleted file mode 100644 index f1c4767..0000000 Binary files a/SEM_cache/html/simulate data SEM_52c5cb6e0fb466a83308047303e465af.rdb and /dev/null differ diff --git a/SEM_cache/html/simulate data SEM_52c5cb6e0fb466a83308047303e465af.rdx b/SEM_cache/html/simulate data SEM_52c5cb6e0fb466a83308047303e465af.rdx deleted file mode 100644 index 6598f57..0000000 Binary files a/SEM_cache/html/simulate data SEM_52c5cb6e0fb466a83308047303e465af.rdx and /dev/null differ diff --git a/SEM_cache/html/simulate data mediation_6159232a8169f6d20f5b753ce5b4e4db.RData b/SEM_cache/html/simulate data mediation_6159232a8169f6d20f5b753ce5b4e4db.RData deleted file mode 100644 index 7fbed8d..0000000 Binary files a/SEM_cache/html/simulate data mediation_6159232a8169f6d20f5b753ce5b4e4db.RData and /dev/null differ diff --git a/SEM_cache/html/simulate data mediation_6159232a8169f6d20f5b753ce5b4e4db.rdb b/SEM_cache/html/simulate data mediation_6159232a8169f6d20f5b753ce5b4e4db.rdb deleted file mode 100644 index 3e8dac4..0000000 Binary files a/SEM_cache/html/simulate data mediation_6159232a8169f6d20f5b753ce5b4e4db.rdb and /dev/null differ diff --git a/SEM_cache/html/simulate data mediation_6159232a8169f6d20f5b753ce5b4e4db.rdx b/SEM_cache/html/simulate data mediation_6159232a8169f6d20f5b753ce5b4e4db.rdx deleted file mode 100644 index 87bc9b2..0000000 Binary files a/SEM_cache/html/simulate data mediation_6159232a8169f6d20f5b753ce5b4e4db.rdx and /dev/null differ diff --git a/SEM_cache/html/simulateData_f09bc3613d78f42af6df963a461e8d0e.RData b/SEM_cache/html/simulateData_f09bc3613d78f42af6df963a461e8d0e.RData deleted file mode 100644 index ac1825f..0000000 Binary files a/SEM_cache/html/simulateData_f09bc3613d78f42af6df963a461e8d0e.RData and /dev/null differ diff --git a/SEM_cache/html/simulateData_f09bc3613d78f42af6df963a461e8d0e.rdb b/SEM_cache/html/simulateData_f09bc3613d78f42af6df963a461e8d0e.rdb deleted file mode 100644 index 364dd57..0000000 Binary files a/SEM_cache/html/simulateData_f09bc3613d78f42af6df963a461e8d0e.rdb and /dev/null differ diff --git a/SEM_cache/html/simulateData_f09bc3613d78f42af6df963a461e8d0e.rdx b/SEM_cache/html/simulateData_f09bc3613d78f42af6df963a461e8d0e.rdx deleted file mode 100644 index dadb3d2..0000000 Binary files a/SEM_cache/html/simulateData_f09bc3613d78f42af6df963a461e8d0e.rdx and /dev/null differ diff --git a/SEM_cache/html/specify lavaan syntax_30dad83cb832c6bbd2d9613262769dbe.RData b/SEM_cache/html/specify lavaan syntax_30dad83cb832c6bbd2d9613262769dbe.RData deleted file mode 100644 index 1cc2a13..0000000 Binary files a/SEM_cache/html/specify lavaan syntax_30dad83cb832c6bbd2d9613262769dbe.RData and /dev/null differ diff --git a/SEM_cache/html/specify lavaan syntax_30dad83cb832c6bbd2d9613262769dbe.rdb b/SEM_cache/html/specify lavaan syntax_30dad83cb832c6bbd2d9613262769dbe.rdb deleted file mode 100644 index 32522ab..0000000 Binary files a/SEM_cache/html/specify lavaan syntax_30dad83cb832c6bbd2d9613262769dbe.rdb and /dev/null differ diff --git a/SEM_cache/html/specify lavaan syntax_30dad83cb832c6bbd2d9613262769dbe.rdx b/SEM_cache/html/specify lavaan syntax_30dad83cb832c6bbd2d9613262769dbe.rdx deleted file mode 100644 index b2ce129..0000000 Binary files a/SEM_cache/html/specify lavaan syntax_30dad83cb832c6bbd2d9613262769dbe.rdx and /dev/null differ diff --git a/SEM_cache/html/specify mediation model_5ed110da39703cf137831f36ab75838d.RData b/SEM_cache/html/specify mediation model_5ed110da39703cf137831f36ab75838d.RData deleted file mode 100644 index d9065c2..0000000 Binary files a/SEM_cache/html/specify mediation model_5ed110da39703cf137831f36ab75838d.RData and /dev/null differ diff --git a/SEM_cache/html/specify mediation model_5ed110da39703cf137831f36ab75838d.rdb b/SEM_cache/html/specify mediation model_5ed110da39703cf137831f36ab75838d.rdb deleted file mode 100644 index 9e7ac37..0000000 Binary files a/SEM_cache/html/specify mediation model_5ed110da39703cf137831f36ab75838d.rdb and /dev/null differ diff --git a/SEM_cache/html/specify mediation model_5ed110da39703cf137831f36ab75838d.rdx b/SEM_cache/html/specify mediation model_5ed110da39703cf137831f36ab75838d.rdx deleted file mode 100644 index fcbccab..0000000 Binary files a/SEM_cache/html/specify mediation model_5ed110da39703cf137831f36ab75838d.rdx and /dev/null differ diff --git a/SEM_cache/html/test mediation model with the pilot data_bd58f05d7e2477cde53f0d854ce550c1.RData b/SEM_cache/html/test mediation model with the pilot data_bd58f05d7e2477cde53f0d854ce550c1.RData deleted file mode 100644 index dc2f17a..0000000 Binary files a/SEM_cache/html/test mediation model with the pilot data_bd58f05d7e2477cde53f0d854ce550c1.RData and /dev/null differ diff --git a/SEM_cache/html/test mediation model with the pilot data_bd58f05d7e2477cde53f0d854ce550c1.rdb b/SEM_cache/html/test mediation model with the pilot data_bd58f05d7e2477cde53f0d854ce550c1.rdb deleted file mode 100644 index 060de06..0000000 Binary files a/SEM_cache/html/test mediation model with the pilot data_bd58f05d7e2477cde53f0d854ce550c1.rdb and /dev/null differ diff --git a/SEM_cache/html/test mediation model with the pilot data_bd58f05d7e2477cde53f0d854ce550c1.rdx b/SEM_cache/html/test mediation model with the pilot data_bd58f05d7e2477cde53f0d854ce550c1.rdx deleted file mode 100644 index 4317af8..0000000 Binary files a/SEM_cache/html/test mediation model with the pilot data_bd58f05d7e2477cde53f0d854ce550c1.rdx and /dev/null differ diff --git a/SEM_files/figure-html/graphic-1.png b/SEM_files/figure-html/graphic-1.png deleted file mode 100644 index 1f39163..0000000 Binary files a/SEM_files/figure-html/graphic-1.png and /dev/null differ diff --git a/SEM_files/figure-html/plot cfa-1.png b/SEM_files/figure-html/plot cfa-1.png deleted file mode 100644 index 4b1cc71..0000000 Binary files a/SEM_files/figure-html/plot cfa-1.png and /dev/null differ diff --git a/SEM_files/figure-html/plot power curve SEM lavaan-1.png b/SEM_files/figure-html/plot power curve SEM lavaan-1.png deleted file mode 100644 index 8e534d8..0000000 Binary files a/SEM_files/figure-html/plot power curve SEM lavaan-1.png and /dev/null differ diff --git a/SEM_files/figure-html/plot power curve SEM-1.png b/SEM_files/figure-html/plot power curve SEM-1.png deleted file mode 100644 index edf1b60..0000000 Binary files a/SEM_files/figure-html/plot power curve SEM-1.png and /dev/null differ diff --git a/SEM_files/figure-html/plot power curve mediation-1.png b/SEM_files/figure-html/plot power curve mediation-1.png deleted file mode 100644 index 7fc5d33..0000000 Binary files a/SEM_files/figure-html/plot power curve mediation-1.png and /dev/null differ diff --git a/SEM_files/figure-html/plot power curve-1.png b/SEM_files/figure-html/plot power curve-1.png deleted file mode 100644 index edbe6e4..0000000 Binary files a/SEM_files/figure-html/plot power curve-1.png and /dev/null differ diff --git a/SEM_files/figure-html/plot sem-1.png b/SEM_files/figure-html/plot sem-1.png deleted file mode 100644 index 0050985..0000000 Binary files a/SEM_files/figure-html/plot sem-1.png and /dev/null differ diff --git a/SEM_files/figure-html/results cfa-1.png b/SEM_files/figure-html/results cfa-1.png deleted file mode 100644 index 11e99f9..0000000 Binary files a/SEM_files/figure-html/results cfa-1.png and /dev/null differ diff --git a/SEM_files/figure-html/safeguard power analysis-1.png b/SEM_files/figure-html/safeguard power analysis-1.png deleted file mode 100644 index 28e9535..0000000 Binary files a/SEM_files/figure-html/safeguard power analysis-1.png and /dev/null differ diff --git a/SEM_fit_index_cache/html/__packages b/SEM_fit_index_cache/html/__packages deleted file mode 100644 index 380d2b4..0000000 --- a/SEM_fit_index_cache/html/__packages +++ /dev/null @@ -1,5 +0,0 @@ -future -future.apply -ggplot2 -lavaan -MPsychoR diff --git a/SEM_fit_index_cache/html/define population_4f610b41269ff2e50b115bfac0cd86a5.RData b/SEM_fit_index_cache/html/define population_4f610b41269ff2e50b115bfac0cd86a5.RData deleted file mode 100644 index 98741e7..0000000 Binary files a/SEM_fit_index_cache/html/define population_4f610b41269ff2e50b115bfac0cd86a5.RData and /dev/null differ diff --git a/SEM_fit_index_cache/html/define population_4f610b41269ff2e50b115bfac0cd86a5.rdb b/SEM_fit_index_cache/html/define population_4f610b41269ff2e50b115bfac0cd86a5.rdb deleted file mode 100644 index 5fe18ef..0000000 Binary files a/SEM_fit_index_cache/html/define population_4f610b41269ff2e50b115bfac0cd86a5.rdb and /dev/null differ diff --git a/SEM_fit_index_cache/html/define population_4f610b41269ff2e50b115bfac0cd86a5.rdx b/SEM_fit_index_cache/html/define population_4f610b41269ff2e50b115bfac0cd86a5.rdx deleted file mode 100644 index 761326b..0000000 Binary files a/SEM_fit_index_cache/html/define population_4f610b41269ff2e50b115bfac0cd86a5.rdx and /dev/null differ diff --git a/SEM_fit_index_cache/html/plot power curve_ec5c6e4aa1a79023ec1eb6bfa2112ab7.RData b/SEM_fit_index_cache/html/plot power curve_ec5c6e4aa1a79023ec1eb6bfa2112ab7.RData deleted file mode 100644 index 619d023..0000000 Binary files a/SEM_fit_index_cache/html/plot power curve_ec5c6e4aa1a79023ec1eb6bfa2112ab7.RData and /dev/null differ diff --git a/SEM_fit_index_cache/html/plot power curve_ec5c6e4aa1a79023ec1eb6bfa2112ab7.rdb b/SEM_fit_index_cache/html/plot power curve_ec5c6e4aa1a79023ec1eb6bfa2112ab7.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_fit_index_cache/html/plot power curve_ec5c6e4aa1a79023ec1eb6bfa2112ab7.rdx b/SEM_fit_index_cache/html/plot power curve_ec5c6e4aa1a79023ec1eb6bfa2112ab7.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_fit_index_cache/html/plot power curve_ec5c6e4aa1a79023ec1eb6bfa2112ab7.rdx and /dev/null differ diff --git a/SEM_fit_index_cache/html/power analysis_c4dfb997b34b42681c3af533028546ab.RData b/SEM_fit_index_cache/html/power analysis_c4dfb997b34b42681c3af533028546ab.RData deleted file mode 100644 index abb7182..0000000 Binary files a/SEM_fit_index_cache/html/power analysis_c4dfb997b34b42681c3af533028546ab.RData and /dev/null differ diff --git a/SEM_fit_index_cache/html/power analysis_c4dfb997b34b42681c3af533028546ab.rdb b/SEM_fit_index_cache/html/power analysis_c4dfb997b34b42681c3af533028546ab.rdb deleted file mode 100644 index 19c9b27..0000000 Binary files a/SEM_fit_index_cache/html/power analysis_c4dfb997b34b42681c3af533028546ab.rdb and /dev/null differ diff --git a/SEM_fit_index_cache/html/power analysis_c4dfb997b34b42681c3af533028546ab.rdx b/SEM_fit_index_cache/html/power analysis_c4dfb997b34b42681c3af533028546ab.rdx deleted file mode 100644 index 5cd4989..0000000 Binary files a/SEM_fit_index_cache/html/power analysis_c4dfb997b34b42681c3af533028546ab.rdx and /dev/null differ diff --git a/SEM_fit_index_cache/html/r install packages_255d4228b5be84f31c386d102ad39f8c.RData b/SEM_fit_index_cache/html/r install packages_255d4228b5be84f31c386d102ad39f8c.RData deleted file mode 100644 index af87a26..0000000 Binary files a/SEM_fit_index_cache/html/r install packages_255d4228b5be84f31c386d102ad39f8c.RData and /dev/null differ diff --git a/SEM_fit_index_cache/html/r install packages_255d4228b5be84f31c386d102ad39f8c.rdb b/SEM_fit_index_cache/html/r install packages_255d4228b5be84f31c386d102ad39f8c.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/SEM_fit_index_cache/html/r install packages_255d4228b5be84f31c386d102ad39f8c.rdx b/SEM_fit_index_cache/html/r install packages_255d4228b5be84f31c386d102ad39f8c.rdx deleted file mode 100644 index 613560a..0000000 Binary files a/SEM_fit_index_cache/html/r install packages_255d4228b5be84f31c386d102ad39f8c.rdx and /dev/null differ diff --git a/SEM_fit_index_cache/html/specify model_df5ef1bd478332e1220400c3a252c8e4.RData b/SEM_fit_index_cache/html/specify model_df5ef1bd478332e1220400c3a252c8e4.RData deleted file mode 100644 index 1ffe5f5..0000000 Binary files a/SEM_fit_index_cache/html/specify model_df5ef1bd478332e1220400c3a252c8e4.RData and /dev/null differ diff --git a/SEM_fit_index_cache/html/specify model_df5ef1bd478332e1220400c3a252c8e4.rdb b/SEM_fit_index_cache/html/specify model_df5ef1bd478332e1220400c3a252c8e4.rdb deleted file mode 100644 index 6865063..0000000 Binary files a/SEM_fit_index_cache/html/specify model_df5ef1bd478332e1220400c3a252c8e4.rdb and /dev/null differ diff --git a/SEM_fit_index_cache/html/specify model_df5ef1bd478332e1220400c3a252c8e4.rdx b/SEM_fit_index_cache/html/specify model_df5ef1bd478332e1220400c3a252c8e4.rdx deleted file mode 100644 index 79c834d..0000000 Binary files a/SEM_fit_index_cache/html/specify model_df5ef1bd478332e1220400c3a252c8e4.rdx and /dev/null differ diff --git a/SEM_fit_index_files/figure-html/plot power curve-1.png b/SEM_fit_index_files/figure-html/plot power curve-1.png deleted file mode 100644 index ba868a9..0000000 Binary files a/SEM_fit_index_files/figure-html/plot power curve-1.png and /dev/null differ diff --git a/_quarto.yml b/_quarto.yml index 2618853..d456402 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -1,40 +1,46 @@ -project: - type: website - output-dir: docs - -website: - repo-url: https://github.com/MalikaIhle/Simulations-for-Advanced-Power-Analyses - repo-actions: [edit, issue] - sidebar: - style: "docked" - search: true - logo: images/LMU-OSC_logo_small.jpg - contents: - - index.qmd - - section: "Models" - contents: - - LM1.qmd - - LM2.qmd - - GLM.qmd - - LMM.qmd - - SEM.qmd - - section: "Bonuses" - contents: - - optimizing_code.qmd - - how_many_iterations.qmd - - SEM_fit_index.qmd - - Resources.qmd - -format: - html: - theme: cosmo - css: styles.css - toc: true - html-math-method: katex - include-in-header: - - includes/matomo.html - -execute: - cache: true - +project: + type: website + output-dir: docs + +website: + repo-url: https://github.com/lmu-osc/Simulations-for-Advanced-Power-Analyses + repo-actions: [edit, issue] + favicon: images/LMU-OSC_favicon.jpg + margin-header: | + ![](/images/LMU-OSC_logo.jpg){width="175"} + + sidebar: + style: "docked" + search: true + logo: images/LMU-OSC_logo_small.jpg + contents: + - index.qmd + - section: "Models" + contents: + - LM1.qmd + - LM2.qmd + - GLM.qmd + - LMM.qmd + - SEM.qmd + - section: "Bonuses" + contents: + - optimizing_code.qmd + - how_many_iterations.qmd + - SEM_fit_index.qmd + - Resources.qmd + +format: + html: + theme: + - cosmo + - custom.scss + css: styles.css + toc: true + html-math-method: katex + include-in-header: + - includes/matomo.html + +execute: + cache: true + editor: source \ No newline at end of file diff --git a/custom.scss b/custom.scss new file mode 100644 index 0000000..442fd2b --- /dev/null +++ b/custom.scss @@ -0,0 +1,5 @@ +/*-- scss:defaults --*/ +// Base document colors +$navbar-bg: #009933; +$link-color: #006426; +$sidebar-hl: #006426; diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29..0000000 diff --git a/docs/GLM.html b/docs/GLM.html deleted file mode 100644 index fa61082..0000000 --- a/docs/GLM.html +++ /dev/null @@ -1,675 +0,0 @@ - - - - - - - - - - -Ch. 3: Generalized Linear Models - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Ch. 3: Generalized Linear Models

-
- - - -
- -
-
Author
-
-

Moritz Fischer

-
-
- - -
- - -
- -

Reading/working time: ~35 min.

-

In the previous chapters, we have learned how to conduct simulation-based power analyses for linear regressions with categorical and/or continuous predictor variables. In this chapter, we will turn to generalized linear models, which constitute a generalization of regular linear regressions. This class of statistical models additionally comprise more complex models such as logistic regression and poisson regression. However, as covering all of variants of the generalized linear models would exceed the scope of this workshop, we will only learn how to conduct a simulation-based power analysis for a logistic regression. Recall that a logistic regression is suitable for designs in which you predict a dichotomous outcome with a set of (continuous or categorical) predictors.

-
-
#install.packages(c("ggplot2", "DescTools"))
-library(ggplot2)
-library(DescTools)
-
-
-

A power analysis for a simple logistic regression with one categorical predictor

-

In order to learn how a simulation-based power analysis for logistic regressions work, we build on the previous chapters by continuing to work with the BtheB data set. Please see the first chapter on linear models to get more info about this data set.

-
-

Let’s get some data as a starting point

-
-
# load the data
-data("BtheB", package = "HSAUR")
-
-# show which variables this data set includes
-str(BtheB)
-
-
'data.frame':   100 obs. of  8 variables:
- $ drug     : Factor w/ 2 levels "No","Yes": 1 2 2 1 2 2 2 1 2 2 ...
- $ length   : Factor w/ 2 levels "<6m",">6m": 2 2 1 2 2 1 1 2 1 2 ...
- $ treatment: Factor w/ 2 levels "TAU","BtheB": 1 2 1 2 2 2 1 1 2 2 ...
- $ bdi.pre  : num  29 32 25 21 26 7 17 20 18 20 ...
- $ bdi.2m   : num  2 16 20 17 23 0 7 20 13 5 ...
- $ bdi.4m   : num  2 24 NA 16 NA 0 7 21 14 5 ...
- $ bdi.6m   : num  NA 17 NA 10 NA 0 3 19 20 8 ...
- $ bdi.8m   : num  NA 20 NA 9 NA 0 7 13 11 12 ...
-
-
-

This data set compares the effects of two interventions for depression, that is a so-called “Beat the blues” intervention (BtheB) and a “treatment-as-usual” intervention (TAU). Note that in this chapter we will compare two active treatment conditions with each other - in the other chapters on linear models, we compare an active treatment with a passive waiting group.

-

Unfortunately, this data set does not include any dichotomous variable we could use as an outcome measure. Therefore, we simply dichotomize one of the continuous outcome variables to create a categorical outcome artificially. More specifically, we will dichotomize the bdi.2m variable which contains a follow-up depression measure (i.e., the Beck Depression Inventory score) two months after the intervention.

-
-
-
- -
-
-Note -
-
-
-

Please note that we dichotomize this variable for didactic reasons only. From a statistical point of view, it is preferable to treat a continuous variable as such because dichotomizing leads to a loss of statistical information (Royston et al., 2006).

-
-
-

Let’s start by dichotomizing the bdi.2m variable and storing the result in a new variable which we label bdi.2m.dicho. Here, we take 20 as our cut-off value, as BDIs of 20 or more refer to a moderate or severe depression. BDI values lower than 20, in turn, reflect a minimal or mild depression (see first chapter on linear models for more info). In our new bdi.2m.dicho variable, we code minimal/mild depression as “0” and moderate/severe depression as “1”.

-
-
#dichotomize dv
-BtheB$bdi.2m.dicho <- ifelse(BtheB$bdi.2m < 20, 0, 1)
-
-#show frequencies
-table(BtheB$bdi.2m.dicho, BtheB$treatment)
-
-
   
-    TAU BtheB
-  0  21    37
-  1  24    15
-
-
-

The frequency table shows that there were less patients with moderate/severe depression in the TAU treatment as compared to the BtheB treatment. That’s a first sign that the BtheB intervention might outperform the treatment-as-usual!

-

In this chapter, we focus on a very simple version of a logistic regression: A model in which the dichotomous outcome variable is predicted by one categorical predictor variable, that is, the treatment condition (BtheB vs. TAU). Let’s assume that we plan to set up a study in which we want to scrutinize whether or not the “Beat the blues” intervention really outperforms the “treatment-as-usual” intervention with regard to the BDI scores two months after the end of the interventions. How many participants would we need for such a study to achieve 80% power?

-
-
-

Estimating the population parameters

-

As in the previous chapters, we first need to estimate all population parameters relevant for this study. More specifically, we will need three estimates:

-
    -
  • the proportion of participants in the BtheB condition
  • -
  • the probability of a favorable outcome in the BtheB condition
  • -
  • the probability of a favorable outcome in the TAU condition
  • -
-

The first parameter is pretty easy to estimate. In most cases, researchers will assign half of the participants to the treatment condition and the other half to a control condition. In this case, the probability of being in the BtheB condition would be 50%. Let’s store that in a variable called prop_btheb.

-
-
prop_btheb <- 0.5
-
-

The other two estimates are only slightly more complicated to estimate. The probability of a favorable outcome in each of the conditions basically means: How many of the participants will not have a moderate/severe depression two months after completing the treatment-as-usual? And, how many of the participants will not have a moderate/severe depression two months after completing the Beat the blues intervention?

-

These two probabilities can only be estimated with solid pilot data. In our case, we can estimate both probabilities from the BtheB data set. The following chunk does that, rounds the estimates to two decimals, and stores the estimates in two objects called prop_tau and prop_btheb.

-
-
prob_tau <- round(length(BtheB$treatment[BtheB$treatment == "TAU" & BtheB$bdi.2m.dicho == 0 & !is.na(BtheB$bdi.2m.dicho)]) / length(BtheB$treatment[BtheB$treatment == "TAU" & !is.na(BtheB$bdi.2m.dicho)]),2) 
-prob_tau
-
-
[1] 0.47
-
-
prob_btheb <- round(length(BtheB$treatment[BtheB$treatment == "BtheB" & BtheB$bdi.2m.dicho == 0 & !is.na(BtheB$bdi.2m.dicho)]) / length(BtheB$treatment[BtheB$treatment == "BtheB" & !is.na(BtheB$bdi.2m.dicho)]),2)
-prob_btheb
-
-
[1] 0.71
-
-
-

In the BtheB data set, the probability of not having a moderate/ severe depression in the BtheB condition was 0.71 and the same probability in the TAU condition was 0.47.

-

Now we have all we need to start simulating data. There are multiple ways to simulate this kind of data, but one very simple way is to apply the sample() function. Here, we use it twice: Once to draw for the TAU condition and once for the BtheB condition (which differ in their probabilities of yielding a positive outcome, as we have seen before). For each condition, we draw whether or not the participant has a moderate/severe depression two months after the treatment, while coding “no moderate/severe depression” with 0 and “moderate/severe depression” with 1. Let’s try this by sampling 100 observations per condition.

-
-
set.seed(8526)
-
-#sample from distribution in "TAU" condition
-tau <- cbind(rep("TAU", 100), sample(x = c(0,1), replace = TRUE, prob = c(prob_tau, 1-prob_tau), size = 100))
-
-#sample from distribution in "BtheB" condition
-btheb <- cbind(rep("BtheB", 100), sample(x = c(0,1), replace = TRUE, prob = c(prob_btheb, 1-prob_btheb), size = 100))
-
-

We now have two data frames (labeled tau and btheB), one per condition. In the following chunk, we combine them into one data frame, change the variable names, transform the treatment variables to a factor, transform the outcome variable to an integer variable, and edit the factor levels – all of this is necessary to run our logistic regression on this data set later.

-
-
#combine data frames
-simulated_data <- rbind(tau, btheb) |> as.data.frame()
-
-#set variable names 
-colnames(simulated_data) <- c("treatment", "bdi.2m.dicho")
-
-#change treatment variable to factor
-simulated_data$treatment <- simulated_data$treatment |> as.factor()
-
-#change outcome variable to integer
-simulated_data$bdi.2m.dicho <- simulated_data$bdi.2m.dicho |> as.integer()
-
-#reverse factor levels, this affects the coding of this factor in the regression analysis later
-simulated_data$treatment <- factor(simulated_data$treatment, levels=rev(levels(simulated_data$treatment)))
-
-

With this first simulated data set, we can now perform a first logistic regression.

-
-
simulated_fit <- glm(bdi.2m.dicho ~ treatment, data = simulated_data, family = "binomial")
-
-summary(simulated_fit)
-
-

-Call:
-glm(formula = bdi.2m.dicho ~ treatment, family = "binomial", 
-    data = simulated_data)
-
-Deviance Residuals: 
-    Min       1Q   Median       3Q      Max  
--1.3354  -0.8615  -0.8615   1.0273   1.5305  
-
-Coefficients:
-               Estimate Std. Error z value Pr(>|z|)    
-(Intercept)      0.3640     0.2033   1.790   0.0734 .  
-treatmentBtheB  -1.1641     0.2968  -3.922 8.78e-05 ***
----
-Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-(Dispersion parameter for binomial family taken to be 1)
-
-    Null deviance: 275.26  on 199  degrees of freedom
-Residual deviance: 259.19  on 198  degrees of freedom
-AIC: 263.19
-
-Number of Fisher Scoring iterations: 4
-
-
-

Here, we find a negative and significant regression weight for the treatment predictor of -1.16, indicating that the BtheB treatment led to less moderate/severe depressions as compared to the TAU treatment. But, actually, it is not our central interest to test whether this particular simulated data set results in a significant treatment effect. What we really want to know is: How many of a theoretically infinite number of simulations yield a significant p-value of this effect? Thus, as in the previous chapters, we now repeatedly simulate data sets of a certain size from the specified population and store the results of the focal test (here: the p-value of the regression coefficient) in a vector called p_values.

-
-
-

Let’s do the power analysis

-
-
#write a function to automize the data simulation process
-simulation <- function(n, p0, p1, prop = .50){
-  
-  #sample from distribution in "TAU" condition
-  tau <- cbind(rep("TAU", n*prop), sample(x = c(0,1), replace = TRUE, prob = c(p0, 1-p0), size = n*prop))
-  
-  #sample from distribution in "BtheB" condition
-  btheb <- cbind(rep("BtheB", n*prop), sample(x = c(0,1), replace = TRUE, prob = c(p1, 1-p1), size = n*prop))
-  
-  #combine both data sets and do some preprocessing
-  simulated_data <- rbind(tau, btheb) |> as.data.frame()
-  colnames(simulated_data) <- c("treatment", "bdi.2m.dicho")
-  simulated_data$treatment <- simulated_data$treatment |> as.factor()
-  simulated_data$bdi.2m.dicho <- simulated_data$bdi.2m.dicho |> as.numeric()
-  simulated_data$treatment <-factor(simulated_data$treatment, levels=rev(levels(simulated_data$treatment)))
-  return(simulated_data)
-}
-
-
-#prepare empty vector to store the p-values
-p_value <- NULL
-
-#prepare empty vector to store the results (i.e., the power per sample size)
-results <- data.frame()
-
-
-# write function to store results of simulation
-sim <- function(n, p0, p1, prop = .50){
-  
-  #simulate data
-  data <- simulation(n = n, p0 = p0, p1 = p1, prop = .50)
-  
-  #run regression
-  simulated_fit <- glm(bdi.2m.dicho ~ treatment, data = data, family = "binomial")
-  
-  #store p-value
-  p_value <- coef(summary(simulated_fit))[2,4]
-  
-  #return p-value
-  return(p_value)
-
-}
-
-# set range of sample sizes
-ns <- seq(from = 20, to = 500, by = 10)
-
-# set number of iterations
-iterations <- 1000
-
-# perform power analysis
-for(n in ns){
-  
-p_values <- replicate(iterations, sim(n, p0 = prob_tau, p1 = prob_btheb, prop = prop))  
-results <- rbind(results, data.frame(
-    n = n,
-    power = sum(p_values < .005)/iterations)
-  )
-}
-
-

Let’s visualize the results.

-
-
ggplot(results, aes(x=n, y=power)) + geom_point() + geom_line() + scale_y_continuous(n.breaks = 10) + scale_x_continuous(n.breaks = 20) + geom_hline(yintercept= 0.8, color = "red")
-
-

-
-
-

This plot shows that we need approx. 220 participants to achieve 80% under the given assumptions. Note that this is the overall sample size, not the size per condition! That’s it - we have performed a simulation-based power analysis for a logistic regression!

-
-
-

Verification with the pwr2ppl package

-

Luckily, there are also R packages that can perform these kinds of power analyses, for example the pwr2ppl package (Aberson, 2019). We can use this package to verify our results. Does the pwr2ppl package yield the same result? Note that you will need to install this package if you haven’t used it before.

-
-
#install.packages("devtools")
-#devtools::install_github("chrisaberson/pwr2ppl")
-pwr2ppl::LRcat(p0 = prob_tau, p1 = prob_btheb, prop = .50,alpha = .005, power = .80)
-
-
Sample Size = 221 for Odds Ratio = 2.761
-
-
-

That’s basically the same result, well done!

-
-
-

Using a safeguard power approach

-

Of note, our effect size estimates (i.e, the estimates of the probabilities of not having a moderate/severe depression two months after the BtheB or the TAU treatment) were so far based on a pilot study. However, this pilot study might not have yielded a precise estimate of these effect sizes. Thus, in order to consider uncertainty in these effect size estimates, it has been suggested to perform a safeguard power analysis (see chapter first chapter on linear models for more info) instead of a power analysis using the observed effect size. The main idea behind the safeguard power analysis is to compute a 60% confidence interval around the observed effect size, and to take the lower bound of this confidence interval as an effect size estimate (see Perugini et al., 2014). Let’s try this here.

-

Let’s assume that we view the probability of not having a moderate/severe depression after the TAU treatment to be probably accurate, but that we want to account for uncertainty in the estimation of the probability of not having a moderate/severe depression after the BtheB treatment. We then simply calculate the 60% confidence interval around this effect size. We can use the BinomCI function from the DescTools package to do this. We need to provide this function with the number of successes (here: 37, see table above) and the number of observations (here: 52, see above).

-
-
DescTools::BinomCI(37, 52, conf.level = 0.60)
-
-
           est    lwr.ci   upr.ci
-[1,] 0.7115385 0.6560993 0.761292
-
-
-

This gives us an estimate of 0.66 for the lower bound of the 60% confidence interval of the probability of not having a moderate/severe depression after the BtheB treatment, while the point estimate for this effect size was 0.71. We can now redo our power analysis from above with this new effect size estimation. I am copying the chunk from above, while replacing the prob_btheb value with 0.66.

-
-
#prepare empty vector to store the p-values
-p_value <- NULL
-
-#prepare empty vector to store the results (i.e., the power per sample size)
-results <- data.frame()
-
-# set range of sample sizes
-ns <- seq(from = 20, to = 500, by = 10)
-
-# set number of iterations
-iterations <- 1000
-
-# perform power analysis
-for(n in ns){
-  
-p_values <- replicate(iterations, sim(n, p0 = prob_tau, p1 = 0.66, prop = prop))  
-results <- rbind(results, data.frame(
-    n = n,
-    power = sum(p_values < .005)/iterations)
-  )
-}
-
-#let's plot this
-ggplot(results, aes(x=n, y=power)) + geom_point() + geom_line() + scale_y_continuous(n.breaks = 10) + scale_x_continuous(n.breaks = 20) + geom_hline(yintercept= 0.8, color = "red")
-
-

-
-
-

Here, we get a total sample size of ca. 360 participants in order to ensure 80% power with our safeguard estimation.

-
-
-
-

References

-

Aberson, C. L. (2019). Applied power analysis for the behavioral sciences. Routledge.

-

Perugini, M., Gallucci, M., & Costantini, G. (2014). Safeguard power as a protection against imprecise power estimates. Perspectives on Psychological Science, 9(3), 319-332. https://journals.sagepub.com/doi/10.1177/1745691614528519

-

Royston, P., Altman, D. G., & Sauerbrei, W. (2006). Dichotomizing continuous predictors in multiple regression: A bad idea. Statistics in Medicine, 25(1), 127–141. https://doi.org/10.1002/sim.2331

- - -
- -
- -
- - - - \ No newline at end of file diff --git a/docs/GLM_files/figure-html/plot-1.png b/docs/GLM_files/figure-html/plot-1.png deleted file mode 100644 index d5fc6e2..0000000 Binary files a/docs/GLM_files/figure-html/plot-1.png and /dev/null differ diff --git a/docs/GLM_files/figure-html/safeguard power analysis-1.png b/docs/GLM_files/figure-html/safeguard power analysis-1.png deleted file mode 100644 index feecc44..0000000 Binary files a/docs/GLM_files/figure-html/safeguard power analysis-1.png and /dev/null differ diff --git a/docs/LM1.html b/docs/LM1.html deleted file mode 100644 index efd70c7..0000000 --- a/docs/LM1.html +++ /dev/null @@ -1,950 +0,0 @@ - - - - - - - - - - -Ch. 1: Linear Model 1: A single dichotomous predictor - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Ch. 1: Linear Model 1: A single dichotomous predictor

-
- - - -
- -
-
Author
-
-

Felix Schönbrodt

-
-
- - -
- - -
- -

Reading/working time: ~50 min.

-
-
# Preparation: Install and load all necessary packages
-
-#install.packages(c("ggplot2", "ggdist", "gghalves", "pwr", "MBESS"))
-
-library(ggplot2)
-library(ggdist)
-library(gghalves)
-library(pwr)
-library(MBESS)
-
-

We start with the simplest possible linear model: (a) a continuous outcome variable is predicted by a single dichotomous predictor. This model actually rephrases a t-test as a linear model! Then we build up increasingly complex models: (b) a single continuous predictor and (c) multiple continuous predictors (i.e., multiple regression).

-
-

Data = Model + Error

-

The general equation for a linear model is:

-

y = b_0 + b_1*x_1 + b_2*x_2 + ... + e

-

where y is the continuous outcome variable, b_0 is the intercept, x_1 is the first predictor variable with its associated regression weight b_1, etc. Finally, e is the error term which captures all variability in the outcome that is not explained by the predictor variables in the model. It is assumed that the error term is independently and identically distributed (iid) following a normal distribution with a mean of zero and a variance of \sigma^2:

-

e \mathop{\sim}\limits^{\mathrm{iid}} N(mean=0, var=\sigma^2)

-

This assumption of the distribution of the errors is important for our simulations, as we will simulate the error according to that assumption. In an actual statistical analysis, the variance of the errors, \sigma^2, is estimated from the data.

-

“i.i.d” means that the error of each observation is independent from all other observations. This assumption is violated, for example, when we look at romantic couples. If one partner is exceptionally distressed, then the other partner presumably will also have higher stress levels. In this case, the errors of both partners are correlated, and not independent any more. (See the section on modelling multilevel data for a statistical way of handling such interdependences).

-
-
-

A concrete example

-

Let’s fill the abstract symbols of Eq. 1 with some concrete content. Assume that we want to analyze the treatment effect of an intervention supposed to reduce depressive symptoms. In the study, a sample of participants with a diagnosed depression are randomized to either a control group or the treatment group.

-

We have the following two variables in our data set:

-
    -
  • BDI is the continuous outcome variable representing the severity of depressive symptoms after the treatment, assessed with the BDI-II inventory (higher values mean higher depression scores).
  • -
  • treatment is our dichotomous predictor defining the random group assignment. We assign the following numbers: 0 = control group (e.g., a waiting list group), 1 = treatment group.
  • -
-

This is an opportunity to think about the actual scale of our variables. At the end, when we simulate data we need to insert concrete numbers into our equations:

-
    -
  • What BDI values would we expect on average in our sample (before treatment)?
  • -
  • What variability would we expect in our sample?
  • -
  • What average treatment effect would we expect?
  • -
-

All of these values are needed to be able to simulate data, and the chosen numbers imply a certain effect size.

-
-

Get some real data as starting point

-
-
-
- -
-
-Note -
-
-
-

The creators of this tutorial are no experts in clinical psychology; we opportunistically selected open data sets based on their availability. Usually, we would look for meta-analyses - ideally bias-corrected - for more comprehensive evidence.

-
-
-

The R package HSAUR contains open data on 100 depressive patients, where 50 received treatment-as-usual (TAU) and 50 received a new treatment (“Beat the blues”; BtheB). Data was collected in a pre-post-design with several follow-up measurements. For the moment, we focus on the pre-treatment baseline value (bdi.pre) and the first post-treatment value (bdi.2m). We will use that data set as a “pilot study” for our power analysis.

-

Note that this pilot data does not contain an inactive control group, such as the waiting list group that we assume for our planned study. Both the BtheB and the TAU group are active treatment groups. Nonetheless, we will be able to infer our treatment effect (vs. an inactive control group) from that data by looking at the pre-treatment data.

-
-
# the data can be found in the HSAUR package, must be installed first
-#install.packages("HSAUR")
-
-# load the data
-data("BtheB", package = "HSAUR")
-
-# get some information about the data set:
-?HSAUR::BtheB
-
-hist(BtheB$bdi.pre)
-
-

-
-
-

The standardized cutoffs for the BDI are:

-
    -
  • 0–13: minimal depression
  • -
  • 14–19: mild depression
  • -
  • 20–28: moderate depression
  • -
  • 29–63: severe depression.
  • -
-

Returning to our questions from above:

-

What BDI values would we expect on average in our sample before treatment?

-
-
# we take the pre-score here:
-mean(BtheB$bdi.pre)
-
-
[1] 23.33
-
-
-

The average BDI score before treatment was 23, corresponding to a “moderate depression”.

-
    -
  • What variability would we expect in our sample?
  • -
-
-
var(BtheB$bdi.pre)
-
-
[1] 117.5163
-
-
-
    -
  • What average treatment effect would we expect?
  • -
-
-
# we take the 2 month follow-up measurement, 
-# separately for the  "treatment as usual" and 
-# the "Beat the blues" group:
-mean(BtheB$bdi.2m[BtheB$treatment == "TAU"], na.rm=TRUE)
-
-
[1] 19.46667
-
-
mean(BtheB$bdi.2m[BtheB$treatment == "BtheB"])
-
-
[1] 14.71154
-
-
-

Hence, the two treatments reduced BDI scores from an average of 23 to 19 (TAU) and 15 (BtheB). Based on that data set, we can conclude that a typical treatment effect is somewhere between a 4 and a 8-point reduction of BDI scores.1

-

For our purpose, we compute the average treatment effect combined for both treatments. The average post-treatment score is:

-
-
mean(BtheB$bdi.2m, na.rm=TRUE)
-
-
[1] 16.91753
-
-
-

So, the average reduction across both treatments is 23-17=6. In the following scripts, we’ll use that value as our assumed treatment effect.

-
-
-

Enter specific values for the model parameters

-

Let’s rewrite the abstract equation with the specific variable names. We first write the equation for the systematic part (without the error term). This also represents the predicted value:

-

\widehat{\text{BDI}} = b_0 + b_1*\text{treatment}

-

We use the notation \widehat{\text{BDI}} (with a hat) to denote the predicted BDI score.

-

The predicted score for the control group then simply is the intercept of the model, as the second term is erased by entering the value “0” for the control group:

-

\widehat{\text{BDI}} = b_0 + b_1*0 = b_0

-

The predicted score for the treatment group is the value for the control group plus the regression weight:

-

\widehat{\text{BDI}} = b_0 + b_1*1 Hence, the regression weight (aka. “slope parameter”) b_1 estimates the mean difference between both groups, which is the treatment effect.

-

With our knowledge from the open BDI data, we insert plausible values for the intercept b_0 and the treatment effect b_1. We expect a reduction of the depression score, so the treatment effect is assumed to be negative. We take the combined treatment effect of the two pilot treatments. And as power analysis is not rocket science, we generously round the values:

-

\widehat{\text{BDI}} = 23 - 6*treatment

-

Hence, the predicted value is 23 - 6*0 = 23 for the control group, and 23 - 6*1 = 17 for the treatment group.

-

With the current model, all participants in the control group have the same predicted value (23), as do all participants in the treatment group (17).

-

As a final step, we add the random noise to the model, based on the variance in the pilot data:

-

\text{BDI} = 23 - 6*treatment + e; e \sim N(0, var=117)

-

That’s our final equation with assumed population parameters! With that equation, we assume a certain state of reality and can sample “virtual participants”.

-
-
-

What is the effect size in the model?

-

Researchers often have been trained to think in standardized effect sizes, such as Cohen’s d, a correlation r, or other indices such as f^2 or partial \eta^2. In the simulation approach, we typical work on the raw scale of variables.

-

The raw effect size is simply the treatment effect on the original BDI scale (i.e., the group difference in the outcome variable). In our case we assume that the treatment lowers the BDI score by 6 points, on average. Defining the raw effect requires some domain knowledge - you need to know your measurement scale, and you need to know what the values (and differences between values) mean. In our example, a reduction of 6 BDI points means that the average patient moves from a moderate depression (23 points) to a mild depression (17 points). Working with raw effect sizes forces you to think about your actual data (instead of plugging in content-free default standardized effect sizes), and enables you to do plausibility checks on your simulation.

-

The standardized effect size relates the raw effect size to the unexplained error variance. In the two-group example, this can be expressed as Cohen’s d, which is the mean difference divided by the standard deviation (SD):

-

d = \frac{M_{treat} - M_{control}}{SD} = \frac{17 - 23}{\sqrt{117}} = -0.55

-

The standardized effect size always relates two components: The raw effect size (here: 6 points difference) and the error variance. Hence, you can increase the standardized effect size by (a) increasing the raw treatment effect, or (b) reducing the error variance.

-
-
-
- -
-
-Note -
-
-
-

If you look up the formula of Cohen’s d, it typically uses the pooled SD from both groups. As we assumed that both groups have the same SD, we simply took that value.

-
-
-
-
-
-

Let’s simulate!

-

Once we committed to an equation with concrete parameter values, we can simulate data for a sample of, say, n=100 participants. Simply write down the equation and add random normal noise with the rnorm function. Note that the rnorm function takes the standard deviation (not the variance). Finally, set a seed so that the generated random numbers are reproducible:

-
-
set.seed(0xBEEF)
-
-# define all simulation parameters and predictor variables:
-n <- 100
-treatment <- c(rep(0, n/2), rep(1, n/2)) # first 50 control, then 50 treatment
-
-# Write down the equation
-BDI <- 23 - 6*treatment + rnorm(n, mean=0, sd=sqrt(117))
-
-# combine all variables in one data frame
-df <- data.frame(treatment, BDI)
-
-head(df)
-
-
  treatment      BDI
-1         0 29.37241
-2         0 25.49819
-3         0 18.80709
-4         0 14.52574
-5         0 11.79615
-6         0 10.98850
-
-
tail(df)
-
-
    treatment      BDI
-95          1 22.14261
-96          1 25.40398
-97          1 15.76692
-98          1 21.04766
-99          1 20.15683
-100         1 20.68332
-
-
-

Let’s plot the simulated data with raincloud plots (see here for a tutorial):

-
-
ggplot(df, aes(x=as.factor(treatment), y=BDI)) + 
-  ggdist::stat_halfeye(adjust = .5, width = .3, .width = 0, 
-        justification = -.3, point_colour = NA) + 
-  geom_boxplot(width = .1, outlier.shape = NA) +
-  gghalves::geom_half_point(side = "l", range_scale = .4, 
-        alpha = .5)
-
-

-
-
-

This graph gives us an impression of the plausibility of our simulation: In the control group, we have a median BDI score of 26, and 50% of participants, represented by the box of the boxplot, are roughly between 18 and 32 points (i.e., in the range of a moderate depression). After the simulated treatment, the median BDI score is at 18 (mild depression).

-

The plot also highlights an unrealistic aspect of our simulations: In reality, the BDI score is bounded to be >=0; but our random data (sampled from a normal distribution) can generate values below zero. For our current power simulations this can be neglected (as negative values are quite rare), but in real data collection such floor or ceiling effects can impose a range restriction that might lower the statistical power.

-

Let’s assume that we collected and analyzed this specific sample - what would have been the results?

-
-
summary(lm(BDI ~ treatment, data=df))
-
-

-Call:
-lm(formula = BDI ~ treatment, data = df)
-
-Residuals:
-     Min       1Q   Median       3Q      Max 
--24.8539  -7.9693   0.5002   7.7587  23.7909 
-
-Coefficients:
-            Estimate Std. Error t value Pr(>|t|)    
-(Intercept)   24.975      2.318  10.776 2.07e-14 ***
-treatment     -7.787      3.278  -2.376   0.0216 *  
----
-Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-Residual standard error: 11.59 on 48 degrees of freedom
-Multiple R-squared:  0.1052,    Adjusted R-squared:  0.08656 
-F-statistic: 5.643 on 1 and 48 DF,  p-value: 0.02156
-
-
-

Here, and in the entire tutorial, we assume an \alpha-level of .005, as this provides more reasonable false positive rates and stronger evidence for new claims (Benjamin et al., 2018).

-

As you can see, in this simulated sample (based on a specific seed), the treatment effect is not significant (p > .02). But how likely are we to detect an effect with a sample of this size?

-
-

Doing the power analysis

-

Now we need to repeatedly draw many samples and see how many of the analyses would have detected the existing effect. To do this, we put the code from above into a function called sim. We coded the function to either return the focal p-value (as default) or to print a model summary (helpful for debugging and testing the function). This function takes two parameters:

-
    -
  • n defines the required sample size
  • -
  • treatment_effect defines the treatment effect in the raw scale (i.e., reduction in BDI points)
  • -
-

We then use the replicate function to repeatedly call the sim function for 1000 iterations.

-
-
set.seed(0xBEEF)
-
-iterations <- 1000 # the number of Monte Carlo repetitions
-n <- 100 # the size of our simulated sample
-
-sim1 <- function(n=100, treatment_effect=-6, print=FALSE) {
-  treatment <- c(rep(0, n/2), rep(1, n/2))
-  BDI <- 23 + treatment_effect*treatment + rnorm(n, mean=0, sd=sqrt(117))
-  
-  # this lm() call should be exactly the function that you use
-  # to analyse your real data set
-  res <- lm(BDI ~ treatment)
-  p_value <- summary(res)$coefficients["treatment", "Pr(>|t|)"]
-  
-  if (print==TRUE) print(summary(res))
-  else return(p_value)
-}
-
-# now run the sim() function a 1000 times and store the p-values in a vector:
-p_values <- replicate(iterations, sim1(n=100))
-
-

How many of our 1000 virtual samples would have found the effect?

-
-
table(p_values < .005)
-
-

-FALSE  TRUE 
-  535   465 
-
-
-

Only 46% of samples with the same size of n=100 result in a significant p-value.

-

46% - that is our power for \alpha = .005, Cohen’s d=.55, and n=100.

-
-
-

Sample size planning: Find the necessary sample size

-

Now we know that a sample size of 100 does not lead to a sufficient power. But what sample size would we need to achieve a power of at least 80%? In the simulation approach you need to test different ns until you find the necessary sample size. We do this by wrapping the simulation code into a loop that continuously increases the n. We then store the computed power for each n.

-
-
set.seed(0xBEEF)
-
-# define all predictor and simulation variables.
-iterations <- 1000
-ns <- seq(100, 300, by=20) # test ns between 100 and 300
-
-result <- data.frame()
-
-for (n in ns) {  # loop through elements of the vector "ns"
-  p_values <- replicate(iterations, sim1(n=n))
-  
-  result <- rbind(result, data.frame(
-    n = n,
-    power = sum(p_values < .005)/iterations)
-  )
-  
-  # show the result after each run (not shown here in the tutorial)
-  print(result)
-}
-
-

Let’s plot there result:

-
-
ggplot(result, aes(x=n, y=power)) + geom_point() + geom_line()
-
-

-
-
-

Hence, with n=180 (90 in each group), we have a 80% chance to detect the effect.

-

🥳 Congratulations! You did your first power analysis by simulation. 🎉

-

For these simple models, we can also compute analytic solutions. Let’s verify our results with the pwr package - a linear regression with a single dichotomous predictor is equivalent to a t-test:

-
-
pwr.t.test(d = 0.55, sig.level = 0.005, power = .80)
-
-

-     Two-sample t test power calculation 
-
-              n = 90.00212
-              d = 0.55
-      sig.level = 0.005
-          power = 0.8
-    alternative = two.sided
-
-NOTE: n is number in *each* group
-
-
-

Exactly the same result - phew 😅

-
-
-
-

Sensitivity analyses

-

Be aware that “pilot studies are next to worthless to estimate effect sizes” (see Brysbaert, 2019, which has a section with that title). When we entered the specific numbers into our equations, we used point estimates from a relatively small pilot sample. As there is considerable uncertainty around these estimates, the true population value could be much smaller or larger. Furthermore, if the very reason for running a study is a significant (exploratory) finding in a small pilot study, your effect size estimate is probably upward biased (because you would have ignored the pilot study if there hadn’t been a significant result).

-

So, ideally, we base our power estimation on more reliable and/or more conservative prior information, or on theoretical considerations about the smallest effect size of interest. Therefore, we will explore two other ways to set the effect size: safeguard power analysis and the smallest effect size of interest (SESOI).

-
-

Safeguard power analysis

-

As sensitivity analysis, we will apply a safeguard power analysis (Perugini et al., 2014) that aims for the lower end of a two-sided 60% CI around the parameter of the treatment effect (the intercept is irrelevant). (Of course you can use any other value than 60%, but this is the value (tentatively) mentioned by the inventors of the safeguard power analysis.)

-
-
-
- -
-
-Note -
-
-
-

If you assume publication bias, another heuristic for aiming at a more realistic population effect size is the “divide-by-2” heuristic. (see kickoff presentation)

-
-
-

We can use the ci.smd function from the MBESS package to compute a CI around Cohen’s d that we computed for our treatment effect:

-
-
ci.smd(smd=-0.55, n.1=50, n.2=50, conf.level=.60)
-
-
$Lower.Conf.Limit.smd
-[1] -0.7201263
-
-$smd
-[1] -0.55
-
-$Upper.Conf.Limit.smd
-[1] -0.377061
-
-
-

However, in the simulated regression equation, we need the raw effect size - so we have to backtransform the standardized confidence limits into the original metric. As the assumed effect is negative, we aim for the upper, i.e., the more conservative limit. After backtransformation in the raw metric, it is considerably smaller, at -4.1:

-

d = \frac{M_{diff}}{SD} \Rightarrow M_{diff} = d*SD = -0.377 * \sqrt{117} = -4.08

-

Now we can rerun the power simulation with this more conservative value (the only change to the code above is that we changed the treatment effect from -6 to -4.1).

-
-
-Show the code -
set.seed(0xBEEF)
-
-# define all predictor and simulation variables.
-iterations <- 1000
-ns <- seq(200, 400, by=20) # test ns between 200 and 400
-
-result <- data.frame()
-
-for (n in ns) {  # loop through elements of the vector "ns"
-  p_values <- replicate(iterations, sim1(n=n, treatment_effect = -4.1))
-  
-  result <- rbind(result, data.frame(
-    n = n,
-    power = sum(p_values < .005)/iterations)
-  )
-  
-  # show the result after each run
-  print(result)
-}
-
-
-
-
-
     n power
-1  200 0.448
-2  220 0.466
-3  240 0.549
-4  260 0.584
-5  280 0.646
-6  300 0.664
-7  320 0.708
-8  340 0.744
-9  360 0.790
-10 380 0.813
-11 400 0.822
-
-
-

With that more conservative effect size assumption, we would need around 380 participants, i.e. 190 per group.

-
-
-

Smallest effect size of interest (SESOI)

-

Many methodologists argue that we should not power for the expected effect size, but rather for the smallest effect size of interest (SESOI). In this case, a non-significant result can be interpreted as “We accept the H_0, and even if a real effect existed, it most likely is too small to be relevant”.

-

What change of BDI scores is perceived as “clinically important”? The hard part is to find a convincing theoretical or empirical argument for the chosen SESOI. In the case of the BDI, luckily someone else did that work.

-

The NICE guidance suggest that a change of >=3 BDI-II points is clinically important.

-

However, as you can expect, things are more complicated. Button et al. (2015) analyzed data sets where patients have been asked, after a treatment, whether they felt “better”, “the same” or “worse”. With these subjective ratings, they could relate changes in BDI-II scores to perceived improvements. Hence, even when depressive symptoms were measurably reduced in the BDI, patients still might answer “feels the same”, which indicates that the reduction did not surpass a threshold of subjective relevant improvement. But the minimal clinical importance depends on the baseline severity: For patients to feel notably better, they need more reduction of BDI-II scores if they start from a higher level of depressive symptoms. Following from this analysis, typical SESOIs are higher than the NICE guidelines, more in the range of -6 BDI points.

-

For our example, let’s use the NICE recommendation of -3 BDI points as a lower threshold for our power analysis (anything larger than that will be covered anyway).

-
-
-Show the code -
set.seed(0xBEEF)
-
-# define all predictor and simulation variables.
-iterations <- 1000
-
-# CHANGE: we adjusted the range of probed sample sizes upwards, as the effect size now is considerably smaller
-ns <- seq(600, 800, by=20)
-
-result <- data.frame()
-
-for (n in ns) {
-  p_values <- replicate(iterations, sim1(n=n, treatment_effect = -3))
-  
-  result <- rbind(result, data.frame(
-    n = n,
-    power = sum(p_values < .005)/iterations)
-  )
-
-  print(result)
-}
-
-
-
-
-
     n power
-1  600 0.728
-2  620 0.738
-3  640 0.756
-4  660 0.781
-5  680 0.791
-6  700 0.791
-7  720 0.827
-8  740 0.820
-9  760 0.852
-10 780 0.866
-11 800 0.871
-
-
-

Hence, we need around 700 participants to reliably detect this smallest effect size of interest.

-

Did you spot the strange pattern in the result? At n=720, the power is 83%, but only 82% with n=740? This is not possible, as power monotonically increases with sample size. It suggests that this is simply Monte Carlo sampling error - 1000 iterations are not enough to get precise estimates. When we increase iterations to 10,000, it takes much longer, but gives more precise results:

-
-
-Show the code -
set.seed(0xBEEF)
-
-# define all predictor and simulation variables.
-iterations <- 10000
-ns <- seq(640, 740, by=20)
-
-result <- data.frame()
-
-for (n in ns) {
-  p_values <- replicate(iterations, sim1(n=n, treatment_effect = -3))
-  
-  result <- rbind(result, data.frame(
-    n = n,
-    power = sum(p_values < .005)/iterations)
-  )
-
-  print(result)
-}
-
-
-
-
-
    n  power
-1 640 0.7583
-2 660 0.7758
-3 680 0.7865
-4 700 0.8060
-5 720 0.8192
-6 740 0.8273
-
-
-

Now power increases monotonically with sample size, as expected.
-To explore how many Monte-Carlo iterations are necessary to get stable computational results, see the bonus page Bonus: How many Monte-Carlo iterations are necessary?

-
-
-
-

Recap: What did we do?

-

These are the steps we did in this part of the tutorial:

-
    -
  1. Define the statistical model that you will use to analyze your data (in our case: a linear regression). This sometimes is called the data generating mechanism or the data generating model.
  2. -
  3. Find empirical information about the parameters in the model. These are typically mean values, variances, group differences, regression coefficients, or correlations (in our case: a mean value (before treatment), a group difference, and an error variance). These numbers define the assumed state of the population.
  4. -
  5. Program your regression equation, which consists of a systematic (i.e., deterministic) part and (one or more) random error terms.
  6. -
  7. Do the simulation: -
      -
    1. Repeatedly sample data from your equation (only the random error changes in each run, the deterministic part stays the same)
    2. -
    3. Compute the index of interest (typically: the p-value of a focal effect)
    4. -
    5. Count how many samples would have detected the effect (i.e., the statistical power)
    6. -
    7. Tune your simulation parameters until the desired level of power is achieved.
    8. -
  8. -
-

Concerning step 4d, the typical scenario is that you increase sample size until your desired level of power is achieved. But you could imagine other ways to increase power, e.g.:

-
    -
  • Increase the reliability of a measurement instrument (which will be reflected in a smaller error variance) until desired power is achieved. (This could relate to a trade-off: Do you invest more effort and time in better measurement, or do you keep an unreliable measure and collect more participants?)
  • -
  • Compare a within- and a between-design in simulations
  • -
-
-
-

References

-

Benjamin, D. J. et al. (2018). Redefine statistical significance. Nature Human Behaviour, 2(1), 6–10. https://doi.org/10.1038/s41562-017-0189-z

-

Brysbaert, M. (2019). How many participants do we have to include in properly powered experiments? A tutorial of power analysis with reference tables. Journal of Cognition, 2(1), 16. DOI: http://doi.org/10.5334/joc.72

-

Button, K. S., Kounali, D., Thomas, L., Wiles, N. J., Peters, T. J., Welton, N. J., Ades, A. E., & Lewis, G. (2015). Minimal clinically important difference on the Beck Depression Inventory—II according to the patient’s perspective. Psychological Medicine, 45(15), 3269–3279. https://doi.org/10.1017/S0033291715001270

-

Perugini, M., Gallucci, M., & Costantini, G. (2014). Safeguard power as a protection against imprecise power estimates. Perspectives on Psychological Science, 9(3), 319–332. https://doi.org/10.1177/1745691614528519

- - -
- - -

Footnotes

- -
    -
  1. By comparing only the post-treatment scores we used an unbiased causal model for the treatment effect. Later we will increase the precision of the estimate by including the baseline score for each participant into the model.↩︎

  2. -
-
- -
- - - - \ No newline at end of file diff --git a/docs/LM1_files/figure-html/unnamed-chunk-14-1.png b/docs/LM1_files/figure-html/unnamed-chunk-14-1.png deleted file mode 100644 index b0d7f5e..0000000 Binary files a/docs/LM1_files/figure-html/unnamed-chunk-14-1.png and /dev/null differ diff --git a/docs/LM1_files/figure-html/unnamed-chunk-2-1.png b/docs/LM1_files/figure-html/unnamed-chunk-2-1.png deleted file mode 100644 index 96b496a..0000000 Binary files a/docs/LM1_files/figure-html/unnamed-chunk-2-1.png and /dev/null differ diff --git a/docs/LM1_files/figure-html/unnamed-chunk-9-1.png b/docs/LM1_files/figure-html/unnamed-chunk-9-1.png deleted file mode 100644 index 426bba3..0000000 Binary files a/docs/LM1_files/figure-html/unnamed-chunk-9-1.png and /dev/null differ diff --git a/docs/LM2.html b/docs/LM2.html deleted file mode 100644 index f86cf6d..0000000 --- a/docs/LM2.html +++ /dev/null @@ -1,903 +0,0 @@ - - - - - - - - - - -Ch. 2: Linear Model 2: Multiple predictors - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Ch. 2: Linear Model 2: Multiple predictors

-
- - - -
- -
-
Author
-
-

Felix Schönbrodt

-
-
- - -
- - -
- -

Reading/working time: ~50 min.

-
-
# Preparation: Install and load all necessary packages
-#install.packages(c("ggplot2", "Rfast"))
-
-library(Rfast)
-library(ggplot2)
-library(tidyr)
-
-

In the first chapter on linear models, we had the simplest possible linear model: a continuous outcome variable is predicted by a single dichotomous predictor. In this chapter, we build up increasingly complex models by (a) adding a single continuous predictor and (b) modeling an interaction.

-
-

Adding a continuous predictor

-

We can improve our causal inference by including the pre-treatment baseline scores of depression into the model: This way, participants are “their own control group” and we can explain a lot of (formerly) unexplained error variance by controlling for this between-person variance.

-
-
-
- -
-
-Note -
-
-
-

As a side-note: Many statistical models have been proposed for analyzing a pre-post control-treatment design. There is growing consensus that a model with the baseline (pre) measurement as covariate is the most appropriate model (see, for example, Senn, 2006 or Wan, 2021).

-
-
-

The raw effect size stays the same - the treatment still is expected to lead to a 6-point decrease in depression scores on average. But we expect that the residual (i.e., unexplained) variance in the model decreases by controlling for pre-treatment differences between participants. But how much will the residual variance be reduced? This depends on the pre-post-correlation.

-
-
-
- -
-
-Note -
-
-
-

If you have absolutely no idea about the pre-post-correlation, a typical default value is r=.5. But such generic defaults are always the very last resort - when you run a scientific study, we hope that you bring at least some domain knowledge 🤓

-
-
-
-

Get some real data as starting point

-

Instead of guessing the necessary quantities - in the current case, the pre-post-correlation - let’s look at real data. The “Beat the blues” (BtheB) data set from the HSAUR R package contains pre-treatment baseline values (bdi.pre), along with multiple post-treatment values. Here we focus on the first post-treatment assessment, 2 months after the treatment (bdi.2m).

-
-
# load the data
-data("BtheB", package = "HSAUR")
-
-# pre-post-correlation
-cor(BtheB$bdi.pre, BtheB$bdi.2m, use="p")
-
-
[1] 0.6142207
-
-
-

In our pilot data set, the pre-post-correlation is around r=.6. We will use this value in our simulations.

-
-
-

Update the sim() function

-

Now we add the continuous predictor into our sim() function. In order to simulate a correlated variable, we need to slightly change the workflow in our simulation.

-

We use the rmvnorm function from the Rfast package to create correlated, normally distributed variables. It takes three parameters:

-
    -
  • n: The number of random observations
  • -
  • mu: A vector of mean values (one for each random variable)
  • -
  • sigma: The variance-covariance-matrix of the random variables.
  • -
-

For mu, we use the value 23 for both the pre and the post value. You can imagine that we only look at the control group: The mean value is not supposed to change systematically from pre to post (although each individual person can go somewhat up or down).

-

The variance-covariance-matrix defines two properties at once: The variance of each variable, and the covariance to all other variables. In the two-variable case, the general matrix looks like:

-

-\begin{bmatrix} -var_x & cov_{xy}\\ -cov_{xy} & var_y -\end{bmatrix} -

-

Note that the covariance in the diagonal has the same values, as cov_{xy} = cov_{yx}. As we need to enter the covariance into sigma, we need to convert the correlation into a covariance. Here’s the formula:

-

cor_{xy} = \frac{cov_{xy}}{\sigma_x\sigma_y}

-

(Note: The denominator contains the standard deviation, not the variance.) Solved for the covariance yields:

-

cov_{xy} = cor_{xy}\sigma_X\sigma_y = 0.6 * \sqrt{117} * \sqrt{117} = 70.2

-

Hence, the specific variance-covariance-matrix sigma is in our case (generously rounded):

-

-\begin{bmatrix} -117 & 70\\ -70 & 117 -\end{bmatrix} -

-

Put it all together:

-
-
set.seed(0xBEEF)
-mu <- c(23, 23) # the mean values of both variables
-sigma <- matrix(
-    c(117 , 70, 
-       70, 117), nrow=2, byrow=TRUE)
-df <- rmvnorm(n=10000, mu=mu, sigma=sigma) |> data.frame()
-names(df) <- c("BDI_pre", "BDI_post0")
-
-# Check: in a large sample the correlation should be close to .6
-cor(df)
-
-
            BDI_pre BDI_post0
-BDI_pre   1.0000000 0.5978088
-BDI_post0 0.5978088 1.0000000
-
-
-
-
-
- -
-
-Note -
-
-
-

A very nice and user-friendly alternative for simulating correlated variables is the rnorm_multi function from the faux package.

-
-
-

We now have the correlated BDI_{pre} and BDI_{post} scores. Finally, we have to impose the treatment effect onto the post variable: In the control group, the mean value stays constant at 23 (what we already have simulated), in the treatment group, the BDI is 6 points lower:

-
-
# add treatment predictor variables
-df$treatment <- rep(c(0, 1), times=nrow(df)/2)
-  
-# add the treatment effect to the BDI_post0 variable
-df$BDI_post <- df$BDI_post0 + -6*df$treatment
-
-

We do not need to add an explicit intercept, as this is already encoded in the mean value of the simulated variables. (Alternatively, we could have simulated them centered on zero and then explicitly add the intercept in the model equation).

-

Let’s make a plausibility check by plotting the simulated variables. We first have to convert them into long format for a nicer plot:

-
-
# reduce to 100 cases for plotting; define factors
-df2 <- df[1:400, ]
-df2$id <- 1:nrow(df2)
-df2$BDI_post0 <- NULL
-
-df_long <- pivot_longer(df2, cols=c(BDI_pre, BDI_post), names_to="time")
-df_long$time <- factor(df_long$time, levels=c("BDI_pre", "BDI_post"))
-df_long$treatment <- factor(df_long$treatment, levels=c(0, 1), labels=c("Control", "Treatment"))
-
-ggplot(df_long, aes(x=time, y=value, group=id)) + geom_point() + geom_line() + facet_wrap(~treatment)
-
-

-
-
ggplot(df_long, aes(x=time, y=value, group=time)) + geom_boxplot() + facet_wrap(~treatment)
-
-

-
-
-

Looks good - reasonable BDI values, same means in control group, same variance. The treatment effect of -6 is visible.

-

Let’s put that together in the new sim function:

-
-
sim2 <- function(n=100, treatment_effect=-6, pre_post_cor = 0.6, err_var = 117, print=FALSE) {
-  library(Rfast)
-  
-  # Here we simulate correlated BDI pre and post scores
-  mu <- c(23, 23) # the mean values of both variables
-  sigma <- matrix(
-    c(err_var, pre_post_cor*sqrt(err_var)*sqrt(err_var), 
-      pre_post_cor*sqrt(err_var)*sqrt(err_var), err_var), 
-    nrow=2, byrow=TRUE)
-  df <- rmvnorm(n, mu, sigma) |> data.frame()
-  names(df) <- c("BDI_pre", "BDI_post0")
-  
-  # add treatment predictor variables
-  df$treatment <- c(rep(0, n/2), rep(1, n/2))
-  
-  # center the BDI_pre value for better interpretability
-  df$BDI_pre.c <- df$BDI_pre - mean(df$BDI_pre)
-  
-  # add the treatment effect to the BDI_post0 variable
-  # We do not need to add an intercept, as this is already encoded
-  # in the mean value of the simulated variables.
-  df$BDI_post <- df$BDI_post0 + df$treatment*treatment_effect
-  
-  # fit the model
-  res <- lm(BDI_post ~ BDI_pre.c + treatment, data=df)
-  summary(res)
-  p_value <- summary(res)$coefficients["treatment", "Pr(>|t|)"]
-  
-  if (print==TRUE) print(summary(res))
-  else return(p_value) 
-}
-
-

Let’s test the plausibility of the new sim2() function by simulating a very large sample - this should give estimates close to the true values. We also vary the assumed pre-post-correlation. When this is 0, the results should be identical to the simpler model from Chapter 1 (except one df that we lost due to the additional predictor). When the pre-post-correlation is > 0, the error variance should be reduced (see Residual standard error at the bottom of each lm output).

-
-
set.seed(0xBEEF)
-
-# without pre_post_cor, the result should be the same
-sim2(n=100000, pre_post_cor=0, print=TRUE)
-
-

-Call:
-lm(formula = BDI_post ~ BDI_pre.c + treatment, data = df)
-
-Residuals:
-    Min      1Q  Median      3Q     Max 
--50.580  -7.401   0.021   7.381  47.636 
-
-Coefficients:
-             Estimate Std. Error t value Pr(>|t|)    
-(Intercept) 23.039486   0.048743 472.676   <2e-16 ***
-BDI_pre.c   -0.002105   0.003178  -0.662    0.508    
-treatment   -6.091742   0.068933 -88.372   <2e-16 ***
----
-Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-Residual standard error: 10.9 on 99997 degrees of freedom
-Multiple R-squared:  0.07244,   Adjusted R-squared:  0.07242 
-F-statistic:  3905 on 2 and 99997 DF,  p-value: < 2.2e-16
-
-
# with pre_post_cor, the residual error should be reduced
-sim2(n=100000, pre_post_cor=0.6, print=TRUE)
-
-

-Call:
-lm(formula = BDI_post ~ BDI_pre.c + treatment, data = df)
-
-Residuals:
-    Min      1Q  Median      3Q     Max 
--35.915  -5.846   0.005   5.807  35.457 
-
-Coefficients:
-             Estimate Std. Error t value Pr(>|t|)    
-(Intercept) 22.942640   0.038783   591.6   <2e-16 ***
-BDI_pre.c    0.597809   0.002538   235.5   <2e-16 ***
-treatment   -5.956531   0.054847  -108.6   <2e-16 ***
----
-Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-Residual standard error: 8.672 on 99997 degrees of freedom
-Multiple R-squared:  0.4017,    Adjusted R-squared:  0.4017 
-F-statistic: 3.357e+04 on 2 and 99997 DF,  p-value: < 2.2e-16
-
-
-

Indeed, with pre_post_cor = 0 the parameter estimates are identical, and with non-zero pre-post-correlation the error term gets reduced.

-
- -
-
-

We assume independence of both predictor variables (BDI_pre and treatment). This is plausible, because the treatment was randomized and therefore independent from the baseline. In this case, the variance that each predictor explains in the dependent variable is additive. The pre-measurement explains r^2 = .6^2 = 36\% of the variance in the post-measurement. As this variance is unrelated to the treatment factor, it reduces the variance of the error term (which was at 117) by 36%:

-

\sigma^2_{err} = 117 * (1-0.36) = 74.88

-

The square root of this unexplained error variance is the Residual standard error from the lm output: \sqrt{74.88} = 8.65.

-
-
-
-
-
-

Do the power analysis

-

The next step is the same as always: use the replicate function to repeatedly call the sim function for many iterations, and increase the simulated sample size until the desired power level is achieved.

-
-
-Code -
set.seed(0xBEEF)
-
-# define all predictor and simulation variables.
-iterations <- 2000
-ns <- seq(90, 180, by=10) # ns are already adjusted to cover the relevant range
-
-result <- data.frame()
-
-for (n in ns) {  # loop through elements of the vector "ns"
-  p_values <- replicate(iterations, sim2(n=n, pre_post_cor = 0.6))
-  
-  result <- rbind(result, data.frame(
-      n = n,
-      power = sum(p_values < .005)/iterations
-    )
-  )
-  
-  # show the result after each run (not shown here in the tutorial)
-  print(result)
-}
-
-
-
-
-
     n  power
-1   90 0.6690
-2  100 0.7245
-3  110 0.7705
-4  120 0.8185
-5  130 0.8765
-6  140 0.8890
-7  150 0.9335
-8  160 0.9335
-9  170 0.9465
-10 180 0.9675
-
-
-

In the original analysis, we needed n=180 (90 in each group) for 80% power. Including the baseline covariate (which explains r^2 = .6^2 = 36\% of the variance in post scores) reduces that number to around n=115.

-

Let’s check the plausibility of our power simulation. Borm et al (2007, p. 1237) propose a simple method on how to arrive at a planned sample size when switching from a simple t-test (comparing post-treatment groups) to a model that controls for the baseline:

-
-

“We propose a simple method for the sample size calculation when ANCOVA is used: multiply the number of subjects required for the t-test by (1-r^2) and add one extra subject per group.

-
-

When we enter our assumed pre-post-correlation into that formula, we arrive a n=117 - very close to our value:

-

180 * (1 - .6^2) + 2 = 117

-
-
-
-

Modeling an interaction

-

We now include the interaction between the baseline score \text{BDI}_{pre} and the \text{treatment} factor. Such an interaction would suggest that the treatment is more effective at a certain baseline severity. There is some evidence that depression treatment works better at higher baseline severity, compared to lower baseline severity1.

-

To this end, we add the interaction term to the equation. We use the centered baseline value \text{BDI}_{pre.c} for better interpretability:

-

\text{BDI}_{post} = b_0 + b_1*\text{BDI}_{pre.c} + b_2*\text{treatment} + b_3*\text{BDI}_{pre.c}*\text{treatment} + e

-

When we rearrange the equation by factoring out the treatment term, the interaction is easier to interpret:

-

\text{BDI}_{post} = b_0 + b_1*\text{BDI}_{pre.c} + (b_2 + b_3*\text{BDI}_{pre.c})*\text{treatment} + e

-

Hence, the size of the treatment effect is b_2 + b_3*\text{BDI}_{pre}. Remember that we centered BDI_{pre} for better interpretability - this is the place where it gets relevant: When we enter the value 0 for BDI_{pre.c} (which corresponds to a typical pre-treatment depression score of around 23), the treatment effect equals the main effect b_2. This conditional treatment effect gets larger or smaller, depending on the value of BDI_{pre.c} and the regression weight b_3. The meaning of b_3 is: How much does the treatment effect increase for every 1 unit increase in BDI_{pre.c}?

-

What are plausible values for b_3? First, we need to decide on the sign. We expect that the treatment effect gets more negative (as we expect a decrease in BDI scores) with increasing baseline severity, so the coefficient must be negative. For the moment, let’s assume that the treatment effect gets 0.4 more negative for every unit increase in BDI_{pre.c}:

-

\text{BDI}_{post} = 23 + 0.6*\text{BDI}_{pre.c} - 6*\text{treatment} - 0.4*\text{BDI}_{pre.c}*\text{treatment} + e

-

We can plot the predicted values of that equation as two separate regression lines: simply enter either 0 or 1 for treatment:

-

\text{Control group}: \widehat{\text{BDI}_{post}} = 23 + 0.6*\text{BDI}_{pre.c} - 6*0 - 0.4*\text{BDI}_{pre.c}*0 = 23 + 0.6*\text{BDI}_{pre.c}

-

\text{Treatment group}: \widehat{\text{BDI}_{post}} = 23 + 0.6*\text{BDI}_{pre.c} - 6*1 - 0.4*\text{BDI}_{pre.c}*1 = 17 + 0.2*\text{BDI}_{pre.c}

-

In the following plot, the limits of the x-axis are chosen to reflect the possible range of the BDI_{pre} score: It can go from 0 to 63, which corresponds to -23 to 40 on the centered scale. The actual maximum in our pilot data was 49 (i.e., 26 on the centered scale), marked with a dashed line.

-
-
-Code -
par(mar = c(7, 4, 4, 2) + 0.1)
-plot(NA, xlim=c(-23, 40), ylim=c(0, 63), xlab="", ylab="BDI_post", main="An implausible interaction effect")
-abline(a=23, b=0.6, col="red")
-abline(a=17, b=0.2, col="blue")
-abline(v=26, lty="dotted")
-legend("topleft", legend=c("Control group", "Treatment group"), 
-      col=c("red", "blue"), lty=1, lwd=2, cex=.8)
-
-# secondary axis with non-centered BDI scores
-title(xlab = "BDI_pre.c (upper axis), BDI_pre (lower axis)", line = 2) 
-axis(1, at = seq(-23, 40, by=5), labels=seq(0, 63, by=5), line = 3.5)
-
-# treatment effect at average baseline value
-segments(x0=0, y0=23, x1=0, y1=17, col="darkgreen", lwd=3)
-
-# treatment effect at smallest possible value (a person with BDI_pre=0)
-segments(x0=-23, y0=9.2, x1=-23, y1=12.4, col="darkgreen", lwd=3)
-
-# treatment effect at smallest possible value (a person with BDI_pre=0)
-segments(x0=26, y0=38.6, x1=26, y1=22.2, col="darkgreen", lwd=3)
-
-
-

-
-
-

With that strenth of the interaction effect, the predicted treatment effect of a person with the highest observed \text{BDI}_{pre} score of 49 would be 16.4, nearly three times as large as the treatment effect at average baseline severity (see the long green line in the plot). While this seems quite high, it might still be plausible.

-

However, the interaction creates an implausible situation at the lower end of baseline severity. The red and the blue line cross at a BDI_{pre} value of around 8: For such persons with a minimal depression, it makes zero difference whether they are in the control or the treatment group. But the effect even reverses for patients with very low BDI values below 8: For them, the treatment leads to worse outcomes than being in the control group.

-

As this is implausible, we want to calibrate the interaction effect in a way that the treatment …

-
    -
  • makes no difference for the lowest \text{BDI}_{pre} values, and
  • -
  • reduces the BDI by 6 points for average depression scores.
  • -
-

(Alternatively, we have to give up the assumption of a linear interaction effect and create a non-linear interaction model. But you don’t want to go that route …).

-

By setting the interaction effect to -0.2, we achieve a plausible interaction plot:

-
-
-Code -
par(mar = c(7, 4, 4, 2) + 0.1)
-plot(NA, xlim=c(-23, 40), ylim=c(0, 63), xlab="", ylab="BDI_post", main="A plausible interaction effect")
-abline(a=23, b=0.6, col="red")
-abline(a=17, b=0.4, col="blue")
-abline(v=26, lty="dotted")
-legend("topleft", legend=c("Control group", "Treatment group"), 
-      col=c("red", "blue"), lty=1, lwd=2, cex=.8)
-
-# secondary axis with non-centered BDI scores
-title(xlab = "BDI_pre.c (upper axis), BDI_pre (lower axis)", line = 2) 
-axis(1, at = seq(-23, 40, by=5), labels=seq(0, 63, by=5), line = 3.5)
-
-# treatment effect at average baseline value
-segments(x0=0, y0=23, x1=0, y1=17, col="darkgreen", lwd=3)
-
-# treatment effect at smallest possible value (a person with BDI_pre=0)
-segments(x0=-23, y0=9.2, x1=-23, y1=7.8, col="darkgreen", lwd=3)
-
-# treatment effect at smallest possible value (a person with BDI_pre=0)
-segments(x0=26, y0=38.6, x1=26, y1=27.4, col="darkgreen", lwd=3)
-
-
-

-
-
-

Now the treatment makes no difference for persons who are not depressed anyway; and for highly depressed persons the treatment effect is -11.2, roughly double the size of a typical treatment effect.

-

We calibrated the interaction effect size by looking at a boundary case. We’d consider this interaction effect to be an upper limit (larger interactions make no sense), but the true interaction effect could be smaller of course.

-
-

Thinking visually

-

Arriving at plausible guesses for interaction effects can be tricky. We strongly recommend to visualize your assumed interaction effect in a plot - first drawn by hand: Do you expect a disordinal (i.e., cross-over) interaction, or an ordinal one where the effect is amplified (but not reversed) by the moderating variable?

-

Look at a reasonable maximum and minimum of your variables - how large would you expect the effect to be there?

-

Evaluate the effect size in subgroups: For example, imagine a group of really severely depressed patients. And then assume that the therapy works exceptionally well for them - what would be a realistic outcome for them? Would you expect them all to be at BDI<10? Probably not. Or would a very good outcome simply be that they move from a “severe depression” to a “moderate depression”? This gives you an estimate of the upper limit of your effect size. After defining upper limits, you should ask: What effect size would be plausible, given your background knowledge of typical effects in your field?

-

Only when you have drawn a reasonable plot by hand (and validated that with colleagues), start to work out the parameter values that you need to enter in the regression equations in order to arrive at the desired interaction plot.

-
-
-

Do the power analysis

-

Now that we have our desired parameter values, we update our sim() function by:

-
    -
  • Imposing the interaction effect onto our dependent variable
  • -
  • Adding the interaction effect to our lm analysis model
  • -
  • Extracting two p-values: We now want to compute the power both for the treatment main effect and for the interaction term.
  • -
-
-
# CHANGE: Add interaction_effect
-sim3 <- function(n=100, treatment_effect=-6, pre_post_cor = 0.6, 
-                 interaction_effect = -.2, err_var = 117, print=FALSE) {
-  library(Rfast)
-  mu <- c(23, 23) # the mean values of both variables
-  sigma <- matrix(
-    c(err_var, pre_post_cor*sqrt(err_var)*sqrt(err_var), 
-      pre_post_cor*sqrt(err_var)*sqrt(err_var), err_var), 
-    nrow=2, byrow=TRUE)
-  df <- rmvnorm(n, mu, sigma) |> data.frame()
-  names(df) <- c("BDI_pre", "BDI_post0")
-  
-  # add treatment predictor variables
-  df$treatment <- c(rep(0, n/2), rep(1, n/2))
-  
-  # center the BDI_pre value for better interpretability
-  df$BDI_pre.c <- df$BDI_pre - mean(df$BDI_pre)
-  
-  # CHANGE: add the treatment main effect and the interaction to the BDI_post variable
-  df$BDI_post <- df$BDI_post0 + treatment_effect*df$treatment + interaction_effect*df$treatment*df$BDI_pre.c
-  
-  # CHANGE: Add interaction effect to analysis model
-  res <- lm(BDI_post ~ BDI_pre.c * treatment, data=df)
-  
-  # CHANGE: extract both focal p-values
-  p_values <- summary(res)$coefficients[c("treatment", "BDI_pre.c:treatment"), "Pr(>|t|)"]
-  
-  if (print==TRUE) print(summary(res))
-  else return(p_values) 
-}
-
-
-
set.seed(0xBEEF)
-
-# define all predictor and simulation variables.
-iterations <- 1000
-ns <- seq(100, 400, by=20)
-
-result <- data.frame()
-
-for (n in ns) {  # loop through elements of the vector "ns"
-  p_values <- replicate(iterations, sim3(n=n, pre_post_cor = 0.6, interaction_effect = -.2))
-  
-  # CHANGE: Analyze both sets of p-values separately
-  # The p-values for the main effect are stored in the first row,
-  # the p-values for the interaction effect are stored in the 2nd row
-  
-  result <- rbind(result, data.frame(
-      n = n,
-      power_treatment = sum(p_values[1, ] < .005)/iterations,
-      power_interaction = sum(p_values[2, ] < .005)/iterations
-    )
-  )
-  
-  # show the result after each run (not shown here in the tutorial)
-  print(result)
-}
-
-
-
-
     n power_treatment power_interaction
-1  100           0.709             0.058
-2  120           0.830             0.069
-3  140           0.900             0.084
-4  160           0.932             0.118
-5  180           0.962             0.122
-6  200           0.991             0.129
-7  220           0.987             0.158
-8  240           0.997             0.210
-9  260           0.996             0.189
-10 280           0.999             0.212
-11 300           0.999             0.262
-12 320           1.000             0.257
-13 340           0.998             0.293
-14 360           1.000             0.338
-15 380           1.000             0.385
-16 400           1.000             0.370
-
-
-

While the power for detecting the main effect quickly approaches 100%, even 400 participants are by far not enough to detect the interaction effect reliably. (In particular when you recall that we set the assumed interaction effect to the largest plausible value).

-
-
-
-

References

-

Hieronymus, F., Lisinski, A., Nilsson, S., & Eriksson, E. (2019). Influence of baseline severity on the effects of SSRIs in depression: An item-based, patient-level post-hoc analysis. The Lancet Psychiatry, 6(9), 745–752. https://doi.org/10.1016/S2215-0366(19)30216-0

-

Senn, S. (2006). Change from baseline and analysis of covariance revisited. Statistics in Medicine, 25(24), 4334–4344. https://doi.org/10.1002/sim.2682

-

Wan, F. (2021). Statistical analysis of two arm randomized pre-post designs with one post-treatment measurement. BMC Medical Research Methodology, 21(1), 150. https://doi.org/10.1186/s12874-021-01323-9

- - -
- - -

Footnotes

- -
    -
  1. Although this might be due to a floor effect of the measurement instrument: If a questionnaire item is already at the lowest possible value at mild depression, there is no more room for improvement (Hieronymus et al., 2019).↩︎

  2. -
-
- -
- - - - \ No newline at end of file diff --git a/docs/LM2_files/figure-html/interaction_plot-1.png b/docs/LM2_files/figure-html/interaction_plot-1.png deleted file mode 100644 index 5ab84a8..0000000 Binary files a/docs/LM2_files/figure-html/interaction_plot-1.png and /dev/null differ diff --git a/docs/LM2_files/figure-html/interaction_plot2-1.png b/docs/LM2_files/figure-html/interaction_plot2-1.png deleted file mode 100644 index 968780f..0000000 Binary files a/docs/LM2_files/figure-html/interaction_plot2-1.png and /dev/null differ diff --git a/docs/LM2_files/figure-html/plot-1.png b/docs/LM2_files/figure-html/plot-1.png deleted file mode 100644 index 12f7c6a..0000000 Binary files a/docs/LM2_files/figure-html/plot-1.png and /dev/null differ diff --git a/docs/LM2_files/figure-html/plot-2.png b/docs/LM2_files/figure-html/plot-2.png deleted file mode 100644 index eb0c7d5..0000000 Binary files a/docs/LM2_files/figure-html/plot-2.png and /dev/null differ diff --git a/docs/LMM.html b/docs/LMM.html deleted file mode 100644 index a11eb05..0000000 --- a/docs/LMM.html +++ /dev/null @@ -1,901 +0,0 @@ - - - - - - - - - - -Ch. 4: Linear Mixed Models / Multilevel models - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Ch. 4: Linear Mixed Models / Multilevel models

-
- - - -
- -
-
Author
-
-

Felix Schönbrodt

-
-
- - -
- - -
- -

Reading/working time: ~60 min.

-
-
# Preparation: Install and load all necessary packages
-# install.packages(c("lme4", "lmerTest", "ggplot2", "tidyr", "Rfast", "future.apply"))
-
-library(lme4)          # for estimating LMMs
-library(lmerTest)      # to get p-values for lme4
-library(ggplot2)       # for plotting
-library(tidyr)         # for reshaping data from wide to long
-library(Rfast)         # fast creation of correlated variables
-library(future.apply)  # for parallel processing
-
-# plan() initializes parallel processing, 
-# for faster code execution
-# By default, it uses all available cores
-plan(multisession)
-
-

In order to keep this chapter at a reasonable length, we focus on how to simulate the mixed effects/multi-level model and spend less time discussing how to arrive at plausible parameter values. Please read Chapters 1 and 2 for multiple examples where the parameter values are derived from (a) the literature (e.g., bias-corrected meta-analyses), (b) pilot data, or (c) plausibility constraints.

-

Furthermore, we’ll use the techniques described in the Chapter “Bonus: Optimizing R code for speed”, otherwise the simulations are too slow. If you wonder about the unknown commands in the code, please read the bonus chapter to learn how to considerably speed up your code!

-

We continue to work with the “Beat the blues” (BtheB) data set from the HSAUR R package. As there are multiple post-treatment measurements (after 2, 3, 6, and 8 months) we can create a nice longitudinal multilevel data set, with measurement points on level 1 (L1) and persons on level 2 (L2). We will work with the lme4 package to run the mixed-effect models.

-

For that, we first have to transform the pilot data set into the long format:

-
-
# load the data
-data("BtheB", package = "HSAUR")
-BtheB$person_id <- 1:nrow(BtheB)
-
-BtheB_long <- pivot_longer(BtheB, cols=matches("^bdi\\.\\d"), values_to = "BDI", names_to = "variable")
-BtheB_long$time <- substr(BtheB_long$variable, 5, 5) |> as.integer()
-
-

Here we plot the trajectory of the first 20 participants. As you can see, there are missing values; not all participants have all follow-up values:

-
-
ggplot(BtheB_long[BtheB_long$person_id <=20, ], aes(x=time, y=BDI, group=person_id)) + geom_point() + geom_line() +
-  facet_wrap(~person_id)
-
-

-
-
-
-

A random-intercept, fixed-slope model

-

We start with a simple model and analyze the trajectory of the BDI score across time (we ignore the treatment factor for the moment). Hence, the question for this model is: Is there a linear trend over time in BDI scores?

-
-

The formulas

-
    -
  • i = index for time points
  • -
  • j = index for persons
  • -
-

Level 1 equation:

-

-\text{BDI}_{ij} = \beta_{0j} + \beta_{1j} time_{ij} + e_{ij} -

-

Level 2 equations:

-

-\beta_{0j} = \gamma_{00} + u_{0j}\\ -\beta_{1j} = \gamma_{10} -

-

Combined equation:

-

-\text{BDI}_{ij} = \gamma_{00} + \gamma_{10} time_{ij} + u_{0j} + e_{ij} \\ -\\ -e_{ij} \mathop{\sim}\limits^{\mathrm{iid}} N(mean=0, var=\sigma^2) \\ -u_{0j} \mathop{\sim}\limits^{\mathrm{iid}} N(mean=0, var=\tau_{00}) -

-
-
-

Analysis in pilot data

-

First, let’s run the analysis model in the pilot data. To make the intercept more interpretable, we center it on the first post-measurement by subtracting 2 (i.e., month “2” becomes month “0” etc.).

-
-
BtheB_long$time.c <- BtheB_long$time - 2
-
-l0 <- lmer(BDI ~ 1 + time.c + (1|person_id), data=BtheB_long)
-summary(l0)
-
-
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
-lmerModLmerTest]
-Formula: BDI ~ 1 + time.c + (1 | person_id)
-   Data: BtheB_long
-
-REML criterion at convergence: 1929.4
-
-Scaled residuals: 
-    Min      1Q  Median      3Q     Max 
--2.6308 -0.4698 -0.0810  0.3536  3.7142 
-
-Random effects:
- Groups    Name        Variance Std.Dev.
- person_id (Intercept) 97.15    9.857   
- Residual              25.48    5.048   
-Number of obs: 280, groups:  person_id, 97
-
-Fixed effects:
-            Estimate Std. Error       df t value Pr(>|t|)    
-(Intercept)  16.9691     1.0990 110.4143  15.441  < 2e-16 ***
-time.c       -0.6869     0.1486 192.8764  -4.623 6.91e-06 ***
----
-Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-Correlation of Fixed Effects:
-       (Intr)
-time.c -0.267
-
-
# Compute ICC
-97.15 / (97.15 + 25.48)
-
-
[1] 0.7922205
-
-
-

Hence, in the pilot data there is a significant negative trend - with each month, participants have a decrease of 0.7 BDI points. But remember that in this data set all of the participants have been treated (with one of two treatments). For a passive control group we probably would expect no, or at least a much smaller negative trend over time. (There could be spontaneous remissions, but if this effect is assumed to be very strong, there would be no need for psychotherapy). The ICC is around 79%, hence a huge amount of random variance can be attributed to differences between persons.

-

The grand intercept \gamma_{00} is 17. This is also the average post-treatment value that we assumed in our previous models in Chapters 1 and 2.

-

For the random intercept variance and the residual variance, we take generously rounded estimates from the pilot data: \tau_{00} = 100 and \sigma^2 = 25.

-
-
-
- -
-
-Default values for ICCs? -
-
-
-

Sometimes the random intercept variance is parametrized via the ICC. Some publications discuss “typical” ICCs (see also Scherbaum & Ferreter, 2008):

-
    -
  • „ICC values typically range between .05 and .20“ (Snijders & Bosker, 1999; Bliese, 2000)
  • -
  • „values greater than .30 will be rare“ (Bliese, 2000)
  • -
  • „median value of .12; this is likely an overestimate“ (James, 1982)
  • -
  • „a value between .10 and .15 will provide a conservative estimate of the ICC when it cannot be precisely computed“
  • -
-

But: there are some research fields where ICCs > 30% are not uncommon - as you can see in our own pilot data, where the ICC was 79%! Again, defaults based on “typical values in the literature” might be better than blind guessing, but nothing beats domain knowledge.

-
-
-
-
-

Let’s simulate

-

The next script shows how to simulate multilevel data with a random intercept. We first create a L2 data set (where each row is once participant). This contains a person id (which is necessary to merge the L1 and the L2 data set) and the random intercepts (i.e., the random deviations of persons from the fixed intercept). In more complex models, this also contains all L2 predictors.

-

Next we create a L1 data set, and then merge both into one long format data set. We closely simulate the situation of the pilot data: All participants are treated and show a negative trend.

-
-
#------  Setting the model parameters ---------------
-# between-person random intercept variance: var(u_0j) = tau_00
-tau_00 <- 100
-
-gamma_00 <- 17    # the grand (fixed) intercept
-gamma_10 <- -0.7  # the fixed slope for time
-residual_var <- 25
-
-n_persons <- 100
-
-# Create a L2 (person-level) data set; each row is one person
-df_L2 <- data.frame(
-  person_id = 1:n_persons,
-  u_0j = rnorm(n_persons, mean=0, sd=sqrt(tau_00))
-)
-
-# Create a L1 (measurement-point-level) data set; each row is one measurement
-df_L1 <- data.frame(
-  person_id = rep(1:n_persons, each=4),  # create 4 rows for each person (as we have 4 measurements)  
-  time.c = rep(c(0, 2, 4, 6), times=n_persons)  
-)
-
-# combine to a long format data set  
-df_long <- merge(df_L1, df_L2, by="person_id")
-  
-# compute the DV by writing down the combined equation
-df_long <- within(df_long, {
-  BDI = gamma_00 + gamma_10*time.c + u_0j + rnorm(n_persons*4, mean=0, sd=sqrt(residual_var))
-})
-
-l1 <- lmer(BDI ~ 1 + time.c + (1|person_id), data=df_long)
-summary(l1)
-
-
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
-lmerModLmerTest]
-Formula: BDI ~ 1 + time.c + (1 | person_id)
-   Data: df_long
-
-REML criterion at convergence: 2681.8
-
-Scaled residuals: 
-     Min       1Q   Median       3Q      Max 
--2.87552 -0.58921 -0.04505  0.61462  2.50725 
-
-Random effects:
- Groups    Name        Variance Std.Dev.
- person_id (Intercept) 129.04   11.359  
- Residual               21.43    4.629  
-Number of obs: 400, groups:  person_id, 100
-
-Fixed effects:
-            Estimate Std. Error       df t value Pr(>|t|)    
-(Intercept)  16.5616     1.2001 113.5218  13.800  < 2e-16 ***
-time.c       -0.7500     0.1035 299.0000  -7.246 3.66e-12 ***
----
-Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-Correlation of Fixed Effects:
-       (Intr)
-time.c -0.259
-
-
-

Now we put this data generating script into a function, and run our power analysis:

-
-
# Note: This code could be more optimized for speed, but this way is easier to understand
-sim4 <- function(n_persons = 100, tau_00 = 100, gamma_00 = 17, gamma_10 = -0.7, 
-                 residual_var = 25, print=FALSE) {
-
-  df_L2 <- data.frame(
-    person_id = 1:n_persons,
-    u_0j = rnorm(n_persons, mean=0, sd=sqrt(tau_00))
-  )
-  
-  df_L1 <- data.frame(
-    person_id = rep(1:n_persons, each=4),  # create 4 rows for each person (as we have 4 measurements)
-    time.c = rep(c(0, 2, 4, 6), times=n_persons)  
-  )
-  
-  # combine to a long format data set  
-  df_long <- merge(df_L1, df_L2, by="person_id")
-  
-  # simulate response variable, based on combined equation  
-  df_long <- within(df_long, {
-    BDI = gamma_00 + gamma_10*time.c + u_0j + rnorm(n_persons*4, mean=0, sd=sqrt(residual_var))
-  })
-  
-  # compute p-values
-  # these options might speed up code execution a little bit:
-  # , control=lmerControl(optimizer="bobyqa", calc.derivs = FALSE)
-  l1 <- lmer(BDI ~ 1 + time.c + (1|person_id), data=df_long)
-  
-  if (print==TRUE) print(summary(l1))
-  else return(summary(l1)$coefficients[, "Pr(>|t|)"])
-}
-
-
-set.seed(0xBEEF)
-iterations <- 1000
-ns <- seq(30, 50, by=5)
-
-result <- data.frame()
-
-for (n_persons in ns) {
-  system.time({
-    p_values <- future_replicate(iterations, sim4(n_persons=n_persons), future.seed = TRUE)
-  })
-  
-  result <- rbind(result, data.frame(
-      n = n_persons,
-      power_intercept = sum(p_values[1, ] < .005)/iterations,
-      power_time.c    = sum(p_values[2, ] < .005)/iterations
-    )
-  )
-  
-  # show the result after each run (not shown here in the tutorial)
-  print(result)
-}
-
-
-
-
   n power_intercept power_time.c
-1 30               1        0.700
-2 35               1        0.786
-3 40               1        0.874
-4 45               1        0.912
-5 50               1        0.937
-
-
-

Hence, we need around 36 persons to achieve a power of 80% to detect this slope.

-
-
-

Comparison with other approaches

-

Murayama and colleagues (2022) recently released an approximate but easy approach for LMM power analysis based on pilot data. They also provide an interactive Shiny app.

-

Go to the tab “Level-1 predictor -> Planning L2 sample size only” and enter the values from the pilot study into the fields in the left panel:

-
    -
  • (Absolute) t value of the focal level-1 predictor = 4.623
  • -
  • Level-2 sample size = 97
  • -
  • Number of cross-level interactions related to the focal level-1 predictor = 0
  • -
-

This approximation yields very close values to our simulation:

-
-
-

-

Screenshot from Murayama app

-
-
-

Another option is the app “PowerAnalysisIL” by Lafit (2021). In that app you would need to choose “Model 4: Effect of a level-1 continuous predictor (fixed slope)” and set “Autocorrelation of level-1 errors” to zero to estimate an equivalent model to ours. This app, however, is much slower than our own simulations, and cannot perfectly mirror our model, as the continuous L1 predictor cannot be defined exactly as we need it.

-

Nonetheless, the estimates are not far away from ours:

-
-
-

-

Screenshot from Lafit app

-
-
-
-
-
-

A random-intercept, random-slope model with cross-level interaction

-

We now make the model more complex by (a) adding a L2 predictor (treatment vs. control), (b) allowing random slopes for the time effect, and (c) model a cross-level interaction (i.e., does the slope differ between treatment and control group?).

-

The main effect for treatment reveals group differences when the other predictor, time, is zero (i.e., at the first post-treatment measurement). The main effect for time now is a conditional effect, as it models the time trend for the control group (i.e., when treatment is zero).

-
-

The formulas

-
    -
  • i = index for time points
  • -
  • j = index for persons
  • -
-

Level 1 equation:

-

-\text{BDI}_{ij} = \beta_{0j} + \beta_{1j} time_{ij} + e_{ij} -

-

Level 2 equations:

-

-\beta_{0j} = \gamma_{00} + \gamma_{01} \text{treatment}_j + u_{0j}\\ -\beta_{1j} = \gamma_{10} + \gamma_{11} \text{treatment}_j + u_{1j} -

-

Combined equation:

-

-\text{BDI}_{ij} = \gamma_{00} + \gamma_{01} \text{treatment}_j + \gamma_{10} time_{ij} + \gamma_{11} \text{treatment}_j time_{ij} + u_{1j} time_{ij} + u_{0j} + e_{ij} \\ -\\ -e_{ij} \mathop{\sim}\limits^{\mathrm{iid}} N(mean=0, var=\sigma^2) \\ -u_{0j} \mathop{\sim}\limits^{\mathrm{iid}} N(mean=0, var=\tau_{00}) \\ -u_{1j} \mathop{\sim}\limits^{\mathrm{iid}} N(mean=0, var=\tau_{11}) \\ -cov(u_{0j}, u_{1j}) = \tau_{01} -

-
-
-

Setting the population parameter values

-

We need to assume four additional population values, namely: \tau_{11} (the variance of random slopes), \tau_{01} (the intercept-slope-covariance), \gamma_{01} (the treatment main effect), and \gamma_{11} (the interaction effect).

-

Fixed effects:

-
    -
  • The grand intercept \gamma_{00} now is set to 23.
  • -
  • The treatment effect \gamma_{01} is set to -6 (as before). In this more complex model, the treatment effect refers to a difference between treatment and control group directly after treatment.
  • -
  • As mentioned above, it is reasonable to assume that there should be no marked trend in a passive control group. Therefore, we set the conditional main effect for time, \gamma_{10} to 0.
  • -
  • For the interaction effect, we expect a steeper decline in the treatment group (e.g., because the treatment “unfolds” its effect over time). If we set the interaction effect to -0.7, this translates to a predicted slope of -0.7 in the treatment group: \gamma_{10} time_{ij} + \gamma_{11} \text{treatment}_j time_{ij} = (\gamma_{10} + \gamma_{11} \text{treatment}_j) time_{ij}.
  • -
-

Is this slope of -0.7, estimated from the pilot data, reasonable? Let’s extrapolate the trend: Treated patients have an average BDI score of 17 (directly after treatment). If they decline 0.7 points per month, they would have a BDI difference of 12*-0.7 = -8.4 after one year, and are at around 9 BDI points. With that decline, they would have moved from a mild depression to a minimal depression. This seems plausible.

-

Random terms:

-
    -
  • Assume that ~95% of slopes in the treatment group lie between -0.7 ± 0.3 (i.e., between -0.4 and -1). This corresponds to a standard deviation of 0.15, and consequently a variance of \tau_{11} = 0.15^2 = 0.0225.
  • -
  • We assume no random effect correlation, so \tau_{01} = 0.
  • -
-

As recommended in the last chapter, we visualize this assumed interaction effect by a hand drawing:

-
-
-

-

hand-drawn interaction plot

-
-
-

Looks good!

-
-
-

Let’s simulate

-
-
sim5 <- function(n_persons = 100, tau_00 = 100, tau_11 = 0.0225, tau_01 = 0, 
-                 gamma_00 = 23, gamma_01 = -6, gamma_10 = 0, gamma_11 = -0.7, 
-                 residual_var = 25, print=FALSE) {
-
-  # create (correlated) random effect structure
-  mu <- c(0 ,0)
-  sigma <- matrix(
-    c(tau_00, tau_01, 
-      tau_01, tau_11), nrow=2, byrow=TRUE)
-  RE <- rmvnorm(n=n_persons, mu=mu, sigma=sigma) |> data.frame()
-  names(RE) <- c("u_0j", "u_1j")
-  
-  df_L1 <- data.frame(
-    person_id = 1:n_persons,
-    u_0j = RE$u_0j,
-    u_1j = RE$u_1j,
-    treatment = rep(c(0, 1), times=n_persons/2)
-  )
-  
-  df_L2 <- data.frame(
-    person_id = rep(1:n_persons, each=4),  # create 4 rows for each person (as we have 4 measurements)
-    time.c = rep(c(0, 2, 4, 6), times=n_persons)  
-  )
-  
-  # combine to a long format data set  
-  df_long <- merge(df_L1, df_L2, by="person_id")
-  
-  # simulate response variable, based on combined equation  
-  df_long <- within(df_long, {
-      BDI = gamma_00 + # intercept
-      gamma_10*time.c + gamma_01*treatment + gamma_11*time.c*treatment +  # fixed terms
-      u_0j + u_1j*time.c + rnorm(n_persons*4, mean=0, sd=sqrt(residual_var))  # random terms
-  })
-  
-  # for debugging: plot some simulated participants
-  #ggplot(df_long[df_long$person_id <=20, ], aes(x=time.c, y=BDI, color=factor(treatment), group=person_id)) +
-  # geom_point() + geom_line() + facet_wrap(~person_id)
-  
-  # ggplot(df_long, aes(x=time.c, y=BDI, color=factor(treatment))) +
-  # stat_summary(fun.data=mean_cl_normal, geom = "pointrange")
-  
-  # compute p-values
-  # these options might speed up code execution a little bit:
-  # , control=lmerControl(optimizer="bobyqa", calc.derivs = FALSE)
-  l1 <- lmer(BDI ~ 1 + time.c*treatment + (1 + time.c|person_id), data=df_long)
-  
-  if (print==TRUE) print(summary(l1))
-  else return(summary(l1)$coefficients[, "Pr(>|t|)"])
-}
-
-
-set.seed(0xBEEF)
-iterations <- 1000
-ns <- seq(60, 200, by=10) # must be dividable by 2
-
-result <- data.frame()
-
-for (n_persons in ns) {
-  system.time({
-    p_values <- future_replicate(iterations, sim5(n_persons=n_persons), future.seed = TRUE)
-  })
-  
-  result <- rbind(result, data.frame(
-      n = n_persons,
-      power_intercept = sum(p_values[1, ] < .005)/iterations,
-      power_time.c    = sum(p_values[2, ] < .005)/iterations,
-      power_treatment = sum(p_values[3, ] < .005)/iterations,
-      power_IA        = sum(p_values[4, ] < .005)/iterations
-    )
-  )
-  
-  # show the result after each run (not shown here in the tutorial)
-  print(result)
-}
-
-
-
-
     n power_intercept power_time.c power_treatment power_IA
-1   60               1        0.004           0.267    0.306
-2   70               1        0.006           0.302    0.422
-3   80               1        0.002           0.413    0.439
-4   90               1        0.003           0.386    0.514
-5  100               1        0.002           0.449    0.563
-6  110               1        0.007           0.462    0.628
-7  120               1        0.007           0.596    0.702
-8  130               1        0.003           0.567    0.768
-9  140               1        0.005           0.629    0.797
-10 150               1        0.005           0.720    0.844
-11 160               1        0.006           0.768    0.835
-12 170               1        0.003           0.790    0.865
-13 180               1        0.005           0.787    0.888
-14 190               1        0.004           0.815    0.913
-15 200               1        0.006           0.904    0.937
-
-
-

You will see a lot of warnings about boundary (singular) fit - you can generally ignore these; they typically occur when one of the random variances is exactly zero; but the fixed effect estimates (which are our focus here) are still valid. You will also see some rare warnings about Model failed to converge with max|grad|. In this case, the optimizer did not converge with the required precision, but typically still is very close. Finally, there are some warnings Model failed to converge with 1 negative eigenvalue. These instances give wrong results. However, if in our thousands of simulations some very few models did not converge, this makes no noticeable difference. If, however, the majority of your models does not converge this is a hint that your simulation has some errors.

-

Concerning the computed power, we see that we need around 180 participants for the treatment main effect (which mirrors the result from Chapter 1), and around 140 participants for detecting the interaction effect. As there is no main effect for time.c (it has been simulated as zero), the power stays always around the \alpha-level.

-
-
-
-

Ways forward

-

While this power analysis could be approximated with existing apps, there are many useful extensions of the simulations that will not be possible in existing apps. For example, we know from the pilot data that there is a lot of drop-out. We could easily simulate that drop-out (e.g., missing completely at random, MCAR) by setting, say, 10% of all BDI values to NA from t_2 on, additional 10% from t_3 on, etc. Furthermore, we could simulate floor effects: Currently, the simulated BDI scores can drop below zero. A more realistic data generating model would cap these values at zero. This restricts the range, diminishes the mean difference, and therefore lowers the power.

-
-
-

Other packages

-

The faux package offers an elegant interface to simulate multilevel data:

-
-
# install.packages("devtools")
-# devtools::install_github("debruine/faux")
-library(faux)
-
-sim_faux <- function(n_persons=100, print=FALSE) {
-  df <- add_random(person_id = n_persons)  %>%
-    add_within("person_id", time.c = 0:3) %>% 
-    add_between("person_id", group = c("treatment", "control")) %>% 
-    add_recode("group", "condition", control = 0, treatment = 1) %>%
-    add_ranef("person_id", u0j = sqrt(100)) %>% 
-    add_ranef(sigma = sqrt(25))
-  
-  df$time.c <- as.integer(as.character(df$time.c))*2
-  
-  # compute DV
-  df$BDI = 17 - 0.7*df$time.c + df$u0j + df$sigma
-  
-  l1 <- lmer(BDI ~ 1 + time.c + (1|person_id), data=df)
-  
-  if (print==TRUE) print(summary(l1))
-  else return(summary(l1)$coefficients[, "Pr(>|t|)"])
-}
-
-

When you plug this simulation function into our power analysis loop, you will get the same results.

-
-

References

-

Scherbaum, C. A., & Ferreter, J. M. (2008). Estimating Statistical Power and Required Sample Sizes for Organizational Research Using Multilevel Modeling. Organizational Research Methods, 12(2), 347–367. http://doi.org/10.1177/1094428107308906

- - -
-
- -
- -
- - - - \ No newline at end of file diff --git a/docs/LMM_files/figure-html/unnamed-chunk-2-1.png b/docs/LMM_files/figure-html/unnamed-chunk-2-1.png deleted file mode 100644 index 1707b7c..0000000 Binary files a/docs/LMM_files/figure-html/unnamed-chunk-2-1.png and /dev/null differ diff --git a/docs/Regression_to_mean.html b/docs/Regression_to_mean.html deleted file mode 100644 index 69ec8a5..0000000 --- a/docs/Regression_to_mean.html +++ /dev/null @@ -1,525 +0,0 @@ - - - - - - - - - -Regression to the mean in pre-post-designs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Regression to the mean in pre-post-designs

-
- - - -
- - - -
- - -
- -

Assumption: In a pre-post-treatment-control design, I would assume that the regression weight from Pre -> Post is 1:

-

BDI_{post} = b_0 + 1*BDI_{pre} + e

-

BDI_{post} = b_0 + b_1*BDI_{pre} + b_2*treatment + e

-

Hence, the pre value simply is carried forward to the post value. We add random error, as participants of course go somewhat up and down, but there is no systematic trend.

-
-
  library(Rfast)
-
-
Lade nötiges Paket: Rcpp
-
-
-
Lade nötiges Paket: RcppZiggurat
-
-
  n <- 10000
-  pre_post_cor <- 0.9
-  mu <- c(23, 23)
-  sigma <- matrix(c(117, pre_post_cor*sqrt(117)*sqrt(117), pre_post_cor*sqrt(117)*sqrt(117), 117), nrow=2, byrow=TRUE)
-  BDI <- rmvnorm(n, mu, sigma) |> data.frame()
-  
-  
-  xi <- rnorm(n, mean=23, sd=sqrt(117))
-  pre <-  1*xi + rnorm(n, mean=0, sd=2)
-  post <- 1*xi + rnorm(n, mean=0, sd=2)
-  
-  cor(pre, post)
-
-
[1] 0.9679785
-
-
  colMeans(BDI)
-
-
      X1       X2 
-22.92187 22.95114 
-
-
  var(BDI)
-
-
         X1       X2
-X1 118.0560 106.2575
-X2 106.2575 118.1335
-
-
  names(BDI) <- c("pre", "post")
-  BDI$id <- 1:nrow(BDI)
-  summary(lm(post~pre, BDI))
-
-

-Call:
-lm(formula = post ~ pre, data = BDI)
-
-Residuals:
-     Min       1Q   Median       3Q      Max 
--16.0754  -3.2091   0.0313   3.2705  19.4456 
-
-Coefficients:
-            Estimate Std. Error t value Pr(>|t|)    
-(Intercept) 2.320078   0.110740   20.95   <2e-16 ***
-pre         0.900060   0.004366  206.17   <2e-16 ***
----
-Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-Residual standard error: 4.743 on 9998 degrees of freedom
-Multiple R-squared:  0.8096,    Adjusted R-squared:  0.8096 
-F-statistic: 4.251e+04 on 1 and 9998 DF,  p-value: < 2.2e-16
-
-
  library(tidyr)
-  library(ggplot2)
-  BDI_long <- pivot_longer(BDI, c(pre, post), names_to = "time")
-  ggplot(BDI_long, aes(x=time, y=value, group=id)) + geom_line()
-
-

-
-
  # typically RTM pattern: The pre-post difference is
-  BDI$diff <- BDI$post-BDI$pre
-  BDI$absdiff <- abs(BDI$post-BDI$pre)
-  hist(BDI$diff)
-
-

-
-
  mean(BDI$diff)
-
-
[1] 0.02927231
-
-
  summary(lm(diff~pre, BDI))
-
-

-Call:
-lm(formula = diff ~ pre, data = BDI)
-
-Residuals:
-     Min       1Q   Median       3Q      Max 
--16.0754  -3.2091   0.0313   3.2705  19.4456 
-
-Coefficients:
-             Estimate Std. Error t value Pr(>|t|)    
-(Intercept)  2.320078   0.110740   20.95   <2e-16 ***
-pre         -0.099940   0.004366  -22.89   <2e-16 ***
----
-Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-Residual standard error: 4.743 on 9998 degrees of freedom
-Multiple R-squared:  0.04981,   Adjusted R-squared:  0.04971 
-F-statistic: 524.1 on 1 and 9998 DF,  p-value: < 2.2e-16
-
-
  ggplot(BDI, aes(x=pre, y=absdiff)) + geom_point() + geom_smooth()
-
-
`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'
-
-
-

-
-
  ggplot(BDI, aes(x=pre, y=post)) + geom_point() + geom_smooth()
-
-
`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'
-
-
-

-
-
  BDI$predicted <- predict(lm(post~pre, BDI))
-  mean(BDI$predicted)
-
-
[1] 22.95114
-
-
  var(BDI$predicted)
-
-
[1] 95.63817
-
-
  # The predicted values have the same mean (so no systematic treatment effect, as expected)
-  # but much smaller variance:
-  
-  BDI_long2 <- pivot_longer(BDI, c(pre, post, predicted), names_to = "cat")
-  
-  library(ggplot2)
-ggplot(BDI_long2, aes(x=as.factor(cat), y=value)) + 
-  ggdist::stat_halfeye(adjust = .5, width = .3, .width = 0, justification = -.3, point_colour = NA) + 
-  geom_boxplot(width = .1, outlier.shape = NA) +
-  gghalves::geom_half_point(side = "l", range_scale = .4, alpha = .5)
-
-

-
-
-

Assumed that we use the predicted scores at t2 (post) and predict the next scores at t3 - will it shrink ever more, until all data points are at the mean? But this is not substantively reflected in the raw scores. Is it an artifact of regression?

- - - -
- -
- - - - \ No newline at end of file diff --git a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-1.png b/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-1.png deleted file mode 100644 index 0f1fe2f..0000000 Binary files a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-1.png and /dev/null differ diff --git a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-2.png b/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-2.png deleted file mode 100644 index c0e0599..0000000 Binary files a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-2.png and /dev/null differ diff --git a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-3.png b/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-3.png deleted file mode 100644 index 62d524f..0000000 Binary files a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-3.png and /dev/null differ diff --git a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-4.png b/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-4.png deleted file mode 100644 index 9465498..0000000 Binary files a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-4.png and /dev/null differ diff --git a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-5.png b/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-5.png deleted file mode 100644 index e962eb5..0000000 Binary files a/docs/Regression_to_mean_files/figure-html/unnamed-chunk-1-5.png and /dev/null differ diff --git a/docs/Resources.html b/docs/Resources.html deleted file mode 100644 index 1f04a7a..0000000 --- a/docs/Resources.html +++ /dev/null @@ -1,315 +0,0 @@ - - - - - - - - - -Resources - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Resources

-
- - - -
- - - -
- - -
- -

These are other resources on power analysis by simulation:

-
    -
  • SuperpowerBook by Aaron R. Caldwell, Daniël Lakens, Chelsea M. Parlett-Pelleriti, Guy Prochilo, Frederik Aust. This teaches how to use the superpower R package to simulate factorial designs and calculate power.
  • -
  • Blog post series “Power Analysis by Data Simulation in R” by Julian Quandt (see Parts I to IV). These blog posts provide a very detailed step-by-step explanation of the necessary steps.
  • -
  • “Simulation-based power analysis for regression” by Andrew Hales (Youtube video, OSF Material). In this video, Andrew explains the steps to you.
  • -
  • faux package by Lisa DeBruine. Simulate data for factorial and mixed designs.
  • -
- - - -
- -
- - - - \ No newline at end of file diff --git a/docs/SEM.html b/docs/SEM.html deleted file mode 100644 index 662f2b3..0000000 --- a/docs/SEM.html +++ /dev/null @@ -1,1263 +0,0 @@ - - - - - - - - - - -Ch. 5: Structural Equation Models - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Ch. 5: Structural Equation Models

-
- - - -
- -
-
Author
-
-

Moritz Fischer

-
-
- - -
- - -
- -

Reading/working time: ~50 min.

-
-
#install.packages(c("lavaan", "ggplot2", "Rfast", "MBESS", "future.apply"), dependencies = TRUE)
-
-library("lavaan")
-library("Rfast")
-library("ggplot2")
-library("dplyr")
-library("MBESS")
-library(future.apply)
-# this initializes parallel processing, for faster code execution
-# By default, it uses all available cores
-plan(multisession)
-
-

In this chapter, we will focus on some rather simple Structural Equation Models (SEM). The goal is to illustrate how simulations can be used to estimate statistical power to detect a given effect in a SEM. In the context of SEMs, the focal effect may be for instance a fit index (e.g., Chi-Square, Root Mean Square Error of Approximation, etc.) or a model coefficient (e.g., a regression coefficient for the association between two latent factors). In this chapter, we only focus on the latter, that is power analyses for regression coefficients in the context of SEM. Please see the bonus chapter titled “Power analysis for fit indices in SEM” if you’re interested in power analyses for fit indices.

-
-
-
- -
-
-Note -
-
-
-

In this chapter, we’ll use the parallelization techniques described in the Chapter “Bonus: Optimizing R code for speed”, otherwise the simulations are too slow. If you wonder about the unknown commands in the code, please read the bonus chapter to learn how to considerably speed up your code! But nonetheless, running some of the chunks in this chapter will require quite some time. We therefore decided to use 500 iterations per simulation only in this chapter; please keep in mind that you should use more iterations in order to obtain more precise estimates!

-
-
-
-

A simulation-based power analysis for a single regression coefficient between latent variables

-

Let’s consider the following example: We are planning to conduct a study investigating whether people’s openness to experience (as conceptualized in the big five personality traits) relates negatively to generalized prejudice, that is a latent variable comprising prejudice towards different social groups (e.g., women, foreigners, homosexuals, disabled people). We could plan to scrutinize this prediction with a SEM in which both openness to experience and generalized prejudice are modeled as latent variables.

-
-
-
- -
-
-Note -
-
-
-

Please note that the predictions used as examples in this chapter are not necessarily theoretically meaningful hypotheses, we merely use them to illustrate the mechanics of simulation-based power analyses in the context of SEM.

-
-
-
-

Let’s get some real data as starting point

-

Just like for any other simulation-based power analysis, we first need to come up with plausible estimates of the distribution of the (manifest) variables. For the sake of simplicity, let’s assume that there is a published study that measured manifestations of our two latent variables and that the corresponding data set is publicly available. For the purpose of this tutorial, we will draw on a publication by Bergh et al (2016) and the corresponding data set which has been made accessible as part of the MPsychoR package. Let’s take a look at this data set.

-
-
#install.packages("MPsychoR")
-library(MPsychoR)
-
-data("Bergh")
-attach(Bergh)
-
-#let's take a look
-head(Bergh)
-
-
        EP    SP  HP       DP     A1       A2       A3     O1       O2       O3
-1 2.666667 3.125 1.4 2.818182 3.4375 3.600000 3.352941 2.8750 3.400000 3.176471
-2 2.666667 3.250 1.4 2.545455 2.3125 2.666667 3.117647 4.4375 3.866667 4.470588
-3 1.000000 1.625 2.7 2.000000 3.5625 4.600000 3.941176 4.2500 3.666667 3.705882
-4 2.666667 2.750 1.8 2.818182 2.7500 3.200000 3.352941 2.8750 3.400000 3.117647
-5 2.888889 3.250 2.7 3.000000 3.2500 4.200000 3.764706 3.9375 4.400000 4.294118
-6 2.000000 2.375 1.7 2.181818 3.2500 3.333333 2.941176 3.8125 3.066667 3.411765
-  gender
-1   male
-2   male
-3   male
-4   male
-5   male
-6   male
-
-
tail(Bergh)
-
-
          EP    SP  HP       DP     A1       A2       A3     O1       O2
-856 2.000000 2.500 1.0 2.363636 3.3125 3.800000 3.705882 3.6875 3.733333
-857 1.555556 1.875 1.1 1.818182 2.6250 3.733333 3.000000 3.3125 2.800000
-858 3.000000 2.750 1.0 1.909091 3.3125 3.533333 3.882353 4.0000 3.533333
-859 1.444444 1.250 1.0 1.000000 3.8750 3.800000 3.529412 3.9375 3.533333
-860 1.222222 1.625 1.0 2.363636 2.8750 3.600000 3.411765 2.8125 3.600000
-861 1.555556 1.750 1.0 1.090909 3.3750 4.266667 4.058824 3.3750 2.800000
-          O3 gender
-856 4.411765 female
-857 3.529412 female
-858 3.823529 female
-859 3.411765 female
-860 3.058824 female
-861 3.588235 female
-
-
-

This data set comprises 11 variables measured in 861 participants. For now, we will focus on the following measured variables:

-
    -
  • EP is a continuous variable measuring ethnic prejudice.
  • -
  • SP is a continuous variable measuring sexism.
  • -
  • HP is a continuous variable measuring sexual prejudice towards gays and lesbians.
  • -
  • DP is a continuous variable measuring prejudice toward people with disabilities.
  • -
  • O1, O2, and O3 are three items measuring openness to experience.
  • -
-

To get an impression of this data, we look at the correlations of the variables we’re interested in.

-
-
cor(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2)
-
-
      EP    SP    HP    DP    O1    O2    O3
-EP  1.00  0.53  0.25  0.53 -0.35 -0.36 -0.41
-SP  0.53  1.00  0.22  0.53 -0.33 -0.31 -0.33
-HP  0.25  0.22  1.00  0.24 -0.23 -0.30 -0.29
-DP  0.53  0.53  0.24  1.00 -0.30 -0.33 -0.34
-O1 -0.35 -0.33 -0.23 -0.30  1.00  0.66  0.74
-O2 -0.36 -0.31 -0.30 -0.33  0.66  1.00  0.71
-O3 -0.41 -0.33 -0.29 -0.34  0.74  0.71  1.00
-
-
-

As we have discussed in the previous chapters, the starting point of every simulation-based power analysis is to specify the population parameters of the variables of interest. In our example, we can estimate the population parameters from the study by Bergh et al. (2016). We start by calculating the means of the variables, rounding them generously, and storing them in a vector called means_vector.

-
-
#store means
-means_vector <- c(mean(EP), mean(SP), mean(HP), mean(DP), mean(O1), mean(O2), mean(O3)) |> round(2)
-
-#Let's take a look
-means_vector
-
-
[1] 1.99 2.11 1.22 2.06 3.55 3.49 3.61
-
-
-

We also need the variance-covariance matrix of our variables in order to simulate data. Luckily, we can estimate this from the Bergh et al. data as well. There are two ways to do this. First, we can use the cov function to obtain the variance-covariance matrix. This matrix incorporates the variance of each variable on the diagonal, and the covariances in the remaining cells.

-
-
#store covariances
-cov_mat <- cov(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2)
-
-#Let's take a look
-cov_mat
-
-
      EP    SP    HP    DP    O1    O2    O3
-EP  0.51  0.26  0.28  0.20 -0.12 -0.12 -0.15
-SP  0.26  0.47  0.24  0.19 -0.11 -0.10 -0.12
-HP  0.28  0.24  2.44  0.20 -0.18 -0.22 -0.23
-DP  0.20  0.19  0.20  0.28 -0.08 -0.08 -0.09
-O1 -0.12 -0.11 -0.18 -0.08  0.23  0.15  0.18
-O2 -0.12 -0.10 -0.22 -0.08  0.15  0.22  0.17
-O3 -0.15 -0.12 -0.23 -0.09  0.18  0.17  0.26
-
-
-

This works well as long as we have a data set (e.g., from a pilot study or published work) to estimate the variances and covariances. In other cases, however, we might not have access to such a data set. In this case, we might only have a correlation table that was provided in a published paper. But that’s no problem either, as we can transform the correlations and standard deviations of the variables of interest into a variance-covariance matrix. The following chunk shows how this works by using the cor2cov function from the MBESS package.

-
-
#store correlation matrix 
-cor_mat <- cor(cbind(EP, SP, HP, DP, O1, O2, O3)) 
-
-#store standard deviations
-sd_vector <- c(sd(EP), sd(SP), sd(HP), sd(DP), sd(O1), sd(O2), sd(O3))
-
-#transform correlations and standard deviations into variance-covariance matrix
-cov_mat2 <- MBESS::cor2cov(cor.mat = cor_mat, sd = sd_vector) |> as.data.frame() |> round(2)
-
-#Let's take a look
-cov_mat2
-
-
      EP    SP    HP    DP    O1    O2    O3
-EP  0.51  0.26  0.28  0.20 -0.12 -0.12 -0.15
-SP  0.26  0.47  0.24  0.19 -0.11 -0.10 -0.12
-HP  0.28  0.24  2.44  0.20 -0.18 -0.22 -0.23
-DP  0.20  0.19  0.20  0.28 -0.08 -0.08 -0.09
-O1 -0.12 -0.11 -0.18 -0.08  0.23  0.15  0.18
-O2 -0.12 -0.10 -0.22 -0.08  0.15  0.22  0.17
-O3 -0.15 -0.12 -0.23 -0.09  0.18  0.17  0.26
-
-
-

Let’s do a plausibility check: Did the two ways to estimate the variance-covariance matrix lead to the same results?

-
-
cov_mat == cov_mat2
-
-
     EP   SP   HP   DP   O1   O2   O3
-EP TRUE TRUE TRUE TRUE TRUE TRUE TRUE
-SP TRUE TRUE TRUE TRUE TRUE TRUE TRUE
-HP TRUE TRUE TRUE TRUE TRUE TRUE TRUE
-DP TRUE TRUE TRUE TRUE TRUE TRUE TRUE
-O1 TRUE TRUE TRUE TRUE TRUE TRUE TRUE
-O2 TRUE TRUE TRUE TRUE TRUE TRUE TRUE
-O3 TRUE TRUE TRUE TRUE TRUE TRUE TRUE
-
-
-

Indeed, this worked! Both procedures lead to the exact same variance-covariance matrix. Now that we have an approximation of the variance-covariance matrix, we use the rmvnorm function from the Rfast package to simulate data from a multivariate normal distribution. The following code simulates n = 50 observations from the specified population.

-
-
#Set seed to make results reproducible
-set.seed(21364)
-
-#simulate data
-my_first_simulated_data <- Rfast::rmvnorm(n = 50, mu=means_vector, sigma = cov_mat) |> as.data.frame()
-
-#Let's take a look
-head(my_first_simulated_data)
-
-
        EP        SP         HP       DP       O1       O2       O3
-1 1.175231 2.1563276 -0.4498951 1.535653 3.520299 3.768441 3.877645
-2 2.615520 1.7527931 -0.2546117 1.728349 3.048912 2.816513 2.743648
-3 1.849380 2.0383562 -0.3877381 2.055153 3.516697 3.354251 3.792305
-4 1.901085 2.5438523  2.0475777 2.150921 3.824178 3.631459 3.947051
-5 2.182503 2.1326429 -0.2088395 2.674773 3.948864 3.642066 4.005110
-6 1.691124 0.9746677  1.9193559 1.652397 4.184485 3.236144 3.669261
-
-
-

We could now fit a SEM to this simulated data set and check whether the regression coefficient modelling the association between openness to experience and generalized prejudice is significant at an \alpha-level of .005. We will work with the lavaan package to fit SEMs.

-
-
#specify SEM
-model_sem <- "generalized_prejudice =~ EP + DP + SP + HP
-              openness =~ O1 + O2 + O3
-              generalized_prejudice ~ openness"
-
-#fit the SEM to the simulated data set
-fit_sem <- sem(model_sem, data = my_first_simulated_data)
-
-#display the results
-summary(fit_sem)
-
-
lavaan 0.6.15 ended normally after 36 iterations
-
-  Estimator                                         ML
-  Optimization method                           NLMINB
-  Number of model parameters                        15
-
-  Number of observations                            50
-
-Model Test User Model:
-                                                      
-  Test statistic                                20.528
-  Degrees of freedom                                13
-  P-value (Chi-square)                           0.083
-
-Parameter Estimates:
-
-  Standard errors                             Standard
-  Information                                 Expected
-  Information saturated (h1) model          Structured
-
-Latent Variables:
-                           Estimate  Std.Err  z-value  P(>|z|)
-  generalized_prejudice =~                                    
-    EP                        1.000                           
-    DP                        0.891    0.298    2.989    0.003
-    SP                        1.156    0.383    3.022    0.003
-    HP                        0.321    0.677    0.474    0.636
-  openness =~                                                 
-    O1                        1.000                           
-    O2                        0.857    0.155    5.544    0.000
-    O3                        1.370    0.225    6.086    0.000
-
-Regressions:
-                          Estimate  Std.Err  z-value  P(>|z|)
-  generalized_prejudice ~                                    
-    openness                -0.259    0.204   -1.270    0.204
-
-Variances:
-                   Estimate  Std.Err  z-value  P(>|z|)
-   .EP                0.352    0.082    4.277    0.000
-   .DP                0.084    0.037    2.292    0.022
-   .SP                0.165    0.064    2.577    0.010
-   .HP                2.475    0.496    4.990    0.000
-   .O1                0.065    0.019    3.433    0.001
-   .O2                0.067    0.017    3.996    0.000
-   .O3                0.045    0.027    1.655    0.098
-   .generlzd_prjdc    0.138    0.078    1.766    0.077
-    openness          0.116    0.036    3.182    0.001
-
-
-

The results show that in this case, the regression coefficient is -0.26 which is significant with p = 0.204. But, actually, it is not our primary interest to see whether this particular simulated data set results in a significant regression coefficient. Rather, we want to know how many of a theoretically infinite number of simulations yield a significant p-value of the focal regression coefficient. Thus, as in the previous chapters, we now repeatedly simulate data sets of a certain size (say, 50 observations) from the specified population and store the results of the focal test (here: the p-value of the regression coefficient) in a vector called p_values.

-
-
#Set seed to make results reproducible
-set.seed(21364)
-
-#let's do 500 iterations
-iterations <- 500
-
-#prepare an empty NA vector with 500 slots
-p_values <- rep(NA, iterations)
-
-#sample size per iteration
-n <- 50
-
-
-#simulate data
-for(i in 1:iterations){
-
-  simulated_data <- Rfast::rmvnorm(n = n, mu = means_vector, sigma = cov_mat) |> as.data.frame()
-  fit_sem_simulated <- sem(model_sem, data = simulated_data)
-  
-  p_values[i] <- parameterestimates(fit_sem_simulated)[8,]$pvalue
-  
-}
-
-

How many of our 500 virtual samples would have found a significant p-value (i.e., p < .005)?

-
-
#frequency table
-table(p_values < .005)
-
-

-FALSE  TRUE 
-  144   356 
-
-
#percentage of significant results
-sum(p_values < .005)/iterations*100
-
-
[1] 71.2
-
-
-

Only 71.2% of samples with the same size of n=50 result in a significant p-value. We conclude that n=50 observations seems to be insufficient, as the power with these parameters is lower than 80%.

-
-
-

Sample size planning: Find the necessary sample size

-

But how many observations do we need to find the presumed effect with a power of 80%? Like before, we can now systematically vary certain parameters (e.g., sample size) of our simulation and see how that affects power. We could, for example, vary the sample size in a range from 30 to 200. Running these simulations typically requires quite some computing time.

-
-
#Set seed to make results reproducible
-set.seed(21364)
-
-#test ns between 30 and 200
-ns_sem <- seq(30, 200, by=10) 
-
-#prepare empty vector to store results
-result_sem <- data.frame()
-
-#set number of iterations
-iterations_sem <- 500
-
-#write function
-sim_sem <- function(n, model, mu, sigma) {
-  
-
-  simulated_data <- Rfast::rmvnorm(n = n, mu = mu, sigma = sigma) |> as.data.frame()
-  fit_sem_simulated <- sem(model, data = simulated_data)
-  p_value_sem <- parameterestimates(fit_sem_simulated)[8,]$pvalue
-  return(p_value_sem)
-  
-    }
-
-
-#replicate function with varying ns
-for (n in ns_sem) {  
-  
-p_values_sem <- future_replicate(iterations_sem, sim_sem(n = n, model = model_sem, mu = means_vector, sigma = cov_mat), future.seed=TRUE)  
-result_sem <- rbind(result_sem, data.frame(
-    n = n,
-    power = sum(p_values_sem < .005)/iterations_sem)
-  )
-
-#The following line of code can be used to track the progress of the simulations 
-#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time
-#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line
-#message(paste("Progress info: Simulations completed for n =", n))
-
-
-}
-
-

Let’s plot the results:

-
-
ggplot(result_sem, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red")
-
-

-
-
-

This graph suggests that we need a sample size of approximately 50 participants to reach a power of 80% with the given population estimates. That’s all it takes to run a power analysis for a SEM!

-

In the following two sub-paragraphs, we would like to present two alternatives and/or extensions to this first way of doing a power analysis for a SEM. First, we would like to present an alternative way to simulate data for SEMs, i.e. by using a built-in function in the lavaan package. Second, we would like to show how a “safeguard” approach to power analysis can be used within the context of SEM.

-
-
-

Using the lavaan syntax to simulate data

-

In our opening example in this chapter, we used the rmvnorm function from the Rfastpackage to simulate data based on the means of the manifest variables as well as their variance-covariance matrix. An alternative to this procedure is to use a built-in function in the lavaan package, that is, the simulateData() function. The main idea here is to provide this function with a lavaan model that specifies all relevant population parameters and then to use this function to directly simulate data.

-

More specifically, we need to incorporate the factor loadings, regression coefficients and (residual) variances of all latent and manifest variables in the lavaan syntax. As these parameters are hardly ever known without a previous study, we will again draw on the results from the data by Bergh et al (2016). Let’s again take a look at the results of our SEM when applying it to this data set.

-
-
#fit the SEM to the pilot data set
-fit_bergh <- sem(model_sem, data = Bergh)
-
-#display the results
-summary(fit_bergh)
-
-
lavaan 0.6.15 ended normally after 40 iterations
-
-  Estimator                                         ML
-  Optimization method                           NLMINB
-  Number of model parameters                        15
-
-  Number of observations                           861
-
-Model Test User Model:
-                                                      
-  Test statistic                                40.574
-  Degrees of freedom                                13
-  P-value (Chi-square)                           0.000
-
-Parameter Estimates:
-
-  Standard errors                             Standard
-  Information                                 Expected
-  Information saturated (h1) model          Structured
-
-Latent Variables:
-                           Estimate  Std.Err  z-value  P(>|z|)
-  generalized_prejudice =~                                    
-    EP                        1.000                           
-    DP                        0.710    0.041   17.383    0.000
-    SP                        0.907    0.052   17.337    0.000
-    HP                        1.042    0.112    9.267    0.000
-  openness =~                                                 
-    O1                        1.000                           
-    O2                        0.932    0.035   26.255    0.000
-    O3                        1.143    0.040   28.923    0.000
-
-Regressions:
-                          Estimate  Std.Err  z-value  P(>|z|)
-  generalized_prejudice ~                                    
-    openness                -0.766    0.056  -13.641    0.000
-
-Variances:
-                   Estimate  Std.Err  z-value  P(>|z|)
-   .EP                0.219    0.016   13.403    0.000
-   .DP                0.141    0.009   14.972    0.000
-   .SP                0.233    0.015   15.079    0.000
-   .HP                2.124    0.106   19.965    0.000
-   .O1                0.073    0.005   14.423    0.000
-   .O2                0.079    0.005   15.805    0.000
-   .O3                0.052    0.005    9.823    0.000
-   .generlzd_prjdc    0.191    0.018   10.362    0.000
-    openness          0.161    0.011   14.210    0.000
-
-
-

In order to use the simulateData() function, we can take the estimates from this previous study and plug them into the lavaan syntax in the following chunk.

-
-
#Set seed to make results reproducible
-set.seed(21364)
-
-#specify SEM
-model_fully_specified <- 
-
-"generalized_prejudice =~ 1*EP + 0.71*DP + 0.91*SP + 1*HP
-openness =~ 1*O1 + 0.93*O2 + 1.14*O3
-generalized_prejudice ~ -0.77*openness
-
-
-generalized_prejudice ~~ 0.19*generalized_prejudice
-openness ~~ 0.16*openness
-EP ~~ 0.21*EP
-DP ~~ 0.14*DP
-SP ~~ 0.23*SP
-HP ~~ 2.12*HP
-O1 ~~ 0.07*O1
-O2 ~~ 0.08*O2
-O3 ~~ 0.05*O3
-
-"
-
-#lets try this
-sim_lavaan <- simulateData(model_fully_specified, sample.nobs=100)
-head(sim_lavaan)
-
-
            EP         DP          SP         HP          O1         O2
-1  0.009175975 -0.4818076 -0.29785829 -3.5273250 -0.02254289 -0.1394437
-2 -0.204483904 -0.4787598  0.15013918 -3.1894504 -0.56917823 -0.2076622
-3  0.456774325 -0.6544974  0.05802164 -2.3337546  0.26092341  0.7767911
-4 -1.153264826 -0.3216415 -0.91603659 -2.0334785 -0.16649019 -0.1387532
-5  0.512683482  0.3138148  0.28465681  0.1202821 -0.41602783 -0.2699893
-6 -1.064347791 -0.3287166 -0.62215235 -2.4007914  0.60927732 -0.1814121
-           O3
-1 -0.22583796
-2 -1.17504320
-3  0.11402119
-4  0.06726909
-5 -0.62531893
-6  0.13393737
-
-
-

The next step is to integrate this code into our first simulation-based power analysis in this chapter, that is, to replace the data simulation process using the rmvnorm function from the Rfast package with this new method using simulateData() from the lavaan package. To this end, I am adapting the power analysis SEM chunk from above accordingly.

-
-
#Set seed to make results reproducible
-set.seed(21364)
-
-#test ns between 30 and 200
-ns_sem <- seq(30, 200, by=10) 
-
-#prepare empty vector to store results
-result_sem <- data.frame()
-
-#set number of iterations
-iterations_sem <- 500
-
-#write function
-sim_sem_lavaan <- function(n, model) {
-  
-  
-  sim_lavaan <- simulateData(model = model, sample.nobs=n)
-  fit_sem_simulated <- sem(model_sem, data = sim_lavaan)
-  p_value_sem <- parameterestimates(fit_sem_simulated)[8,]$pvalue
-  return(p_value_sem)
-  
-}
-
-
-#replicate function with varying ns
-for (n in ns_sem) {  
-  
-  p_values_sem <- future_replicate(iterations_sem, sim_sem_lavaan(n = n, model = model_fully_specified), future.seed=TRUE)  
-  result_sem <- rbind(result_sem, data.frame(
-    n = n,
-    power = sum(p_values_sem < .005, na.rm = TRUE)/iterations_sem)
-  )
-  
-#The following line of code can be used to track the progress of the simulations 
-#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time
-#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line
-#message(paste("Progress info: Simulations completed for n =", n))
-
-}
-
-

Let’s plot this again.

-
-
ggplot(result_sem, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red")
-
-

-
-
-

Here, we conclude that approx. 55 participants would be needed to achieve 80% power. The slight difference compared to the previous power analysis should be explained by the fact that we rounded the numbers that define our statistical populations and that we only used 500 Monte Carlo iterations – these differences should decrease with an increasing number of iterations.

-
-
-
- -
-
-Tip -
-
-
-

Wang and Rhemtulla (2021) developed a shiny app that can do power analyses for SEMs in a similar fashion, but that additionally provides a point-and-click interface. You can use it here, for instance, to replicate the results of this simulation-based power analysis: https://yilinandrewang.shinyapps.io/pwrSEM/

-
-
-
-
-

A safeguard power approach for SEMs

-

Of note, the two power analyses for our SEM we have conducted so far used the observed effect size from the Bergh et al. (2016) data set as an estimate of the “true” effect size. But, as this point estimate may be imprecise (e.g., because of publication bias), it seems reasonable to use a more conservative estimate of the true effect size. One more conservative approach in this context is the safeguard power approach (Perugini et al., 2014), which we have already applied in Chapter 1 (Linear Model I: a single dichotomous predictor).

-

Basically, all need to do in order to account for variability of observed effect sizes is to calculate a 60%-confidence interval around the point estimate of the observed effect size from our pilot data and to use the more conservative bound of this confidence interval (here: the upper bound) as our new effect size estimate. This can be easily done with the parameterestimates function from the lavaan package which takes the level of the confidence interval as an input parameter. Let’s use this function on our object fit_bergh which stores the results of our SEM in the Bergh data set.

-
-
parameterestimates(fit_bergh, level = .60)
-
-
                     lhs op                   rhs    est    se       z pvalue
-1  generalized_prejudice =~                    EP  1.000 0.000      NA     NA
-2  generalized_prejudice =~                    DP  0.710 0.041  17.383      0
-3  generalized_prejudice =~                    SP  0.907 0.052  17.337      0
-4  generalized_prejudice =~                    HP  1.042 0.112   9.267      0
-5               openness =~                    O1  1.000 0.000      NA     NA
-6               openness =~                    O2  0.932 0.035  26.255      0
-7               openness =~                    O3  1.143 0.040  28.923      0
-8  generalized_prejudice  ~              openness -0.766 0.056 -13.641      0
-9                     EP ~~                    EP  0.219 0.016  13.403      0
-10                    DP ~~                    DP  0.141 0.009  14.972      0
-11                    SP ~~                    SP  0.233 0.015  15.079      0
-12                    HP ~~                    HP  2.124 0.106  19.965      0
-13                    O1 ~~                    O1  0.073 0.005  14.423      0
-14                    O2 ~~                    O2  0.079 0.005  15.805      0
-15                    O3 ~~                    O3  0.052 0.005   9.823      0
-16 generalized_prejudice ~~ generalized_prejudice  0.191 0.018  10.362      0
-17              openness ~~              openness  0.161 0.011  14.210      0
-   ci.lower ci.upper
-1     1.000    1.000
-2     0.676    0.744
-3     0.863    0.951
-4     0.948    1.137
-5     1.000    1.000
-6     0.902    0.962
-7     1.110    1.176
-8    -0.814   -0.719
-9     0.205    0.233
-10    0.133    0.148
-11    0.220    0.246
-12    2.034    2.213
-13    0.069    0.078
-14    0.074    0.083
-15    0.048    0.057
-16    0.175    0.206
-17    0.152    0.171
-
-
-

This output shows that the upper bound of the 60% confidence interval around the focal regression coefficient is -0.72. We can now use this as our new and more conservative effect size estimate. We can for example insert this value into our simulation using the lavaan syntax. For this purpose, we copy the chunk from above and simply replace the previous effect size estimate (-0.77) with the new estimate (-0.72), while keeping all other parameters that define this data set.

-
-
#Set seed to make results reproducible
-set.seed(21364)
-
-#specify SEM
-model_fully_specified_safeguard <- 
-
-"generalized_prejudice =~ 1*EP + 0.71*DP + 0.91*SP + 1*HP
-openness =~ 1*O1 + 0.93*O2 + 1.14*O3
-generalized_prejudice ~ -0.72*openness
-
-
-generalized_prejudice ~~ 0.19*generalized_prejudice
-openness ~~ 0.16*openness
-EP ~~ 0.21*EP
-DP ~~ 0.14*DP
-SP ~~ 0.23*SP
-HP ~~ 2.12*HP
-O1 ~~ 0.07*O1
-O2 ~~ 0.08*O2
-O3 ~~ 0.05*O3
-
-"
-
-#lets try this
-sim_lavaan_safeguard <- simulateData(model_fully_specified_safeguard, sample.nobs=100)
-head(sim_lavaan_safeguard)
-
-
           EP         DP         SP         HP          O1         O2
-1  0.03163582 -0.4674398 -0.2762416 -3.5075535 -0.04391145 -0.1593227
-2 -0.19674199 -0.4764434  0.1605740 -3.1573002 -0.58997086 -0.2268869
-3  0.48315832 -0.6351419  0.0799879 -2.3272372  0.25229795  0.7683784
-4 -1.13893054 -0.3131634 -0.9001980 -2.0386232 -0.18372798 -0.1543441
-5  0.49985196  0.3039113  0.2740270  0.1386227 -0.41638579 -0.2703941
-6 -1.04098556 -0.3130325 -0.5996895 -2.4067345  0.59293247 -0.1965773
-           O3
-1 -0.25024294
-2 -1.19986667
-3  0.10431698
-4  0.04700113
-5 -0.62587386
-6  0.11528181
-
-
-

Now, everything is ready for the actual safeguard power analysis. We can re-use the sim_sem_lavaan function we have defined above. Let’s see what we get here!

-
-
#Set seed to make results reproducible
-set.seed(21364)
-
-#prepare empty vector to store results
-result_sem_safeguard <- data.frame()
-
-#replicate function with varying ns
-for (n in ns_sem) {  
-  
-  p_values_sem_safeguard <- future_replicate(iterations_sem, sim_sem_lavaan(n = n, model = model_fully_specified_safeguard), future.seed=TRUE)  
-  result_sem_safeguard <- rbind(result_sem_safeguard, data.frame(
-    n = n,
-    power = sum(p_values_sem_safeguard < .005, na.rm = TRUE)/iterations_sem)
-  )
-  
-#The following line of code can be used to track the progress of the simulations 
-#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time
-#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line
-#message(paste("Progress info: Simulations completed for n =", n))
-  
-}
-
-ggplot(result_sem_safeguard, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red")
-
-

-
-
-

This safeguard power analysis yields a required sample size of ca. 63 participants.

-
-
-
- -
-
-Note -
-
-
-

In addition to this safeguard power approach, we would also have liked to derive a smallest effect size of interest (SESOI). However, no prior studies on SESOI in the context of personality/stereotypes were available and the measures/response scales used by Bergh et al (2016) were only vaguely reported in their manuscript, thereby making it difficult to derive a meaningful SESOI. We therefore only report a safeguard approach but no SESOI approach here.

-
-
-
-
-
-

A simulation-based power analysis for a mediation model with latent variables

-

Sometimes, researchers not only wish to investigate whether and how two (latent) variables related to each other, but also whether the association between two (manifest or latent) variables is mediated by a third variable. We will run a power analysis for such a latent mediation model, investigating whether gender affects generalized prejudice (fully or partially) through openness to experience, while using the Bergh et al (2016) dataset as a pilot study. We can repeat the same steps as in our previous power analysis, while incorporating gender into our analysis. Specifically, we will follow these steps:

-
    -
  1. find plausible estimates of the population parameters
  2. -
  3. specify the statistical model
  4. -
  5. simulate data from this population
  6. -
  7. compute the index of interest (e.g., the p-value) and store the results
  8. -
  9. repeat steps 2) and 3) multiple times
  10. -
  11. count how many samples would have detected the specified effect (i.e., compute the statistical power)
  12. -
  13. vary your simulation parameters until the desired level of power (e.g., 80%) is achieved
  14. -
-

We first draw on the Bergh et al (2016) data set to estimate the means and the variance-covariance matrix. In this data set, gender is a factor with two levels, male and female. We first need to transform this categorical variable into an integer variable, for instance coding male = 0 and female = 1.

-
-
Bergh_int <- Bergh 
-Bergh_int$gender <- ifelse(Bergh_int$gender == "male", 0, ifelse(Bergh_int$gender == "female", 1, NA))
-
-attach(Bergh_int)
-
-
Die folgenden Objekte sind maskiert von Bergh:
-
-    A1, A2, A3, DP, EP, gender, HP, O1, O2, O3, SP
-
-
#store means
-means_mediation <- c(mean(gender), mean(EP), mean(SP), mean(HP), mean(DP), mean(O1), mean(O2), mean(O3)) |> round(2)
-
-#store covarainces
-cov_mediation <- cov(cbind(gender, EP, SP, HP, DP, O1, O2, O3)) |> round(2)
-
-
-
-
- -
-
-Tip -
-
-
-

If we were interested in a mediation model with only manifest variables, we could also use a useful shiny app developed by Schoemann et al. (2017), which can be accessed via https://schoemanna.shinyapps.io/mc_power_med/. This app currently enables power analyses for some manifest mediation models: Mediation with (i) one mediator, (ii) two parallel mediators, (iii) two serial mediators, (iv) and three parallel mediators. But as we want to incorporate generalized prejudice and openness to experience as latent variables, we need to write a simulation-based power analyses ourselves.

-
-
-

Now, we specify the statistical mediation model in lavaan.

-
-
#specify mediation model
-model_mediation <- '
-
-              # measurement model
-              generalized_prejudice =~ EP + DP + SP + HP
-              openness =~ O1 + O2 + O3
-              
-              # direct effect
-              generalized_prejudice ~ c*gender
-              
-              # mediator
-              openness ~ a*gender
-              generalized_prejudice ~ b*openness
-
-              # indirect effect (a*b)
-              ab := a*b
-           
-              # total effect
-              total := c + (a*b)
-
-'
-
-

To verify that this model syntax works properly, we can fit this model to the data set provided by Bergh et al. (2016).

-
-
#fit the SEM to the simulated data set
-fit_mediation <- sem(model_mediation, data = Bergh_int)
-
-#display the results
-summary(fit_mediation)
-
-
lavaan 0.6.15 ended normally after 39 iterations
-
-  Estimator                                         ML
-  Optimization method                           NLMINB
-  Number of model parameters                        17
-
-  Number of observations                           861
-
-Model Test User Model:
-                                                      
-  Test statistic                                84.622
-  Degrees of freedom                                18
-  P-value (Chi-square)                           0.000
-
-Parameter Estimates:
-
-  Standard errors                             Standard
-  Information                                 Expected
-  Information saturated (h1) model          Structured
-
-Latent Variables:
-                           Estimate  Std.Err  z-value  P(>|z|)
-  generalized_prejudice =~                                    
-    EP                        1.000                           
-    DP                        0.723    0.042   17.332    0.000
-    SP                        0.962    0.054   17.722    0.000
-    HP                        1.057    0.115    9.174    0.000
-  openness =~                                                 
-    O1                        1.000                           
-    O2                        0.931    0.035   26.266    0.000
-    O3                        1.142    0.039   28.923    0.000
-
-Regressions:
-                          Estimate  Std.Err  z-value  P(>|z|)
-  generalized_prejudice ~                                    
-    gender     (c)          -0.268    0.039   -6.815    0.000
-  openness ~                                                 
-    gender     (a)           0.094    0.032    2.950    0.003
-  generalized_prejudice ~                                    
-    openness   (b)          -0.712    0.054  -13.245    0.000
-
-Variances:
-                   Estimate  Std.Err  z-value  P(>|z|)
-   .EP                0.233    0.016   14.383    0.000
-   .DP                0.142    0.009   15.316    0.000
-   .SP                0.217    0.015   14.405    0.000
-   .HP                2.130    0.106   20.005    0.000
-   .O1                0.073    0.005   14.395    0.000
-   .O2                0.079    0.005   15.798    0.000
-   .O3                0.052    0.005    9.855    0.000
-   .generlzd_prjdc    0.168    0.017   10.051    0.000
-   .openness          0.160    0.011   14.210    0.000
-
-Defined Parameters:
-                   Estimate  Std.Err  z-value  P(>|z|)
-    ab               -0.067    0.023   -2.891    0.004
-    total            -0.335    0.044   -7.552    0.000
-
-
-

Now, everything is set up to run the actual power analysis. In the following chunk, we repeatedly simulate data from the specified population and store the p-value of the indirect effect while varying the sample size in a range from 500 to 1500.

-
-
#Set seed to make results reproducible
-set.seed(21364)
-
-#test ns between 100 and 1500
-ns_mediation <- seq(500, 1500, by=50) 
-
-#prepare empty vector to store results
-result_mediation <- data.frame()
-
-#iterations
-iterations_mediation <- 500
-
-#write function
-sim_mediation <- function(n, model, mu, sigma) {
-  
-
-      simulated_data_mediation <- Rfast::rmvnorm(n = n, mu = mu, sigma = sigma) |> as.data.frame()
-      fit_mediation_simulated <- sem(model, data = simulated_data_mediation)
-  
-      p_value_mediation <- parameterestimates(fit_mediation_simulated)[21,]$pvalue
-      return(p_value_mediation)
-    }
-
-
-#replicate function with varying ns
-for (n in ns_mediation) {  
-  
-p_values_mediation <- future_replicate(iterations_mediation, sim_mediation(n = n, model = model_mediation, mu = means_mediation, sigma = cov_mediation), future.seed=TRUE)  
-result_mediation <- rbind(result_mediation, data.frame(
-    n = n,
-    power = sum(p_values_mediation < .005)/iterations_mediation)
-  )
-
-#The following line of code can be used to track the progress of the simulations 
-#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time
-#I have deactivated this here; to enable it, just remove the "#" sign at the beginning of the next line
-#message(paste("Progress info: Simulations completed for n =", n))
-
-}
-
-

Let’s plot the results:

-
-
ggplot(result_mediation, aes(x=n, y=power)) + geom_point() + geom_line() + scale_y_continuous(n.breaks = 10) + scale_x_continuous(n.breaks = 20) + geom_hline(yintercept= 0.8, color = "red")
-
-

-
-
-

This shows that roughly 1,300 to 1,400 participants will be needed to obtain sufficient power under the assumptions we specified. To achieve a more precise estimate, just increase the number of iterations (and get a cup of coffee while you wait for the results 😅)

-
-
-

References

-

Bergh, R., Akrami, N., Sidanius, J., & Sibley, C. G. (2016). Is group membership necessary for understanding generalized prejudice? A re-evaluation of why prejudices are interrelated. Journal of Personality and Social Psychology, 111(3), 367–395. https://doi.org/10.1037/pspi0000064

-

Perugini, M., Gallucci, M., & Costantini, G. (2014). Safeguard power as a protection against imprecise power estimates. Perspectives on Psychological Science, 9(3), 319–332. https://doi.org/10.1177/1745691614528519

-

Schoemann, A. M., Boulton, A. J., & Short, S. D. (2017). Determining power and sample size for simple and complex mediation models. Social Psychological and Personality Science, 8(4), 379–386. https://doi.org/10.1177/1948550617715068

-

Wang, Y. A., & Rhemtulla, M. (2021). Power analysis for parameter estimation in structural equation modeling: A discussion and tutorial. Advances in Methods and Practices in Psychological Science, 4(1), 1–17. https://doi.org/10.1177/2515245920918253

- - -
- -
- -
- - - - \ No newline at end of file diff --git a/docs/SEM_files/figure-html/plot power curve SEM lavaan-1.png b/docs/SEM_files/figure-html/plot power curve SEM lavaan-1.png deleted file mode 100644 index 8e534d8..0000000 Binary files a/docs/SEM_files/figure-html/plot power curve SEM lavaan-1.png and /dev/null differ diff --git a/docs/SEM_files/figure-html/plot power curve SEM-1.png b/docs/SEM_files/figure-html/plot power curve SEM-1.png deleted file mode 100644 index edf1b60..0000000 Binary files a/docs/SEM_files/figure-html/plot power curve SEM-1.png and /dev/null differ diff --git a/docs/SEM_files/figure-html/plot power curve mediation-1.png b/docs/SEM_files/figure-html/plot power curve mediation-1.png deleted file mode 100644 index 7fc5d33..0000000 Binary files a/docs/SEM_files/figure-html/plot power curve mediation-1.png and /dev/null differ diff --git a/docs/SEM_files/figure-html/safeguard power analysis-1.png b/docs/SEM_files/figure-html/safeguard power analysis-1.png deleted file mode 100644 index 28e9535..0000000 Binary files a/docs/SEM_files/figure-html/safeguard power analysis-1.png and /dev/null differ diff --git a/docs/SEM_fit_index.html b/docs/SEM_fit_index.html deleted file mode 100644 index 0a846c7..0000000 --- a/docs/SEM_fit_index.html +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - -Bonus: Fit indices in SEM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Bonus: Fit indices in SEM

-
- - - -
- -
-
Author
-
-

Moritz Fischer

-
-
- - -
- - -
- -

Reading/working time: ~15 min.

-
-
#install.packages(c("future.apply", "ggplot2", "lavaan", "MPsychoR"), dependencies = TRUE)
-
-require(future.apply)
-require(ggplot2)
-require(lavaan)
-require(MPsychoR)
-
-

In this chapter, we will turn to simulation-based power analysis for fit indices in the context of SEM. We will build on the model we have introduced in Chapter 5 (Structural Equation Modelling (SEM)), it is therefore recommendable to read this chapter first. The following chunk specifies this model, in which (latent) generalized prejudice is predicted by (latent) openness to experience (as conceptualized in the big five personality traits). We fit this model to the dataset by Bergh et al. (2016) in order to get a first impression of the model fit.

-
-
data("Bergh")
-
-model_sem <- "generalized_prejudice =~ EP + DP + SP + HP
-              openness =~ O1 + O2 + O3
-              generalized_prejudice ~ openness"
-
-#fit the SEM to the pilot data set
-fit <- sem(model_sem, data = Bergh)
-
-summary(fit, fit.measures = TRUE)
-
-
lavaan 0.6.15 ended normally after 40 iterations
-
-  Estimator                                         ML
-  Optimization method                           NLMINB
-  Number of model parameters                        15
-
-  Number of observations                           861
-
-Model Test User Model:
-                                                      
-  Test statistic                                40.574
-  Degrees of freedom                                13
-  P-value (Chi-square)                           0.000
-
-Model Test Baseline Model:
-
-  Test statistic                              2395.448
-  Degrees of freedom                                21
-  P-value                                        0.000
-
-User Model versus Baseline Model:
-
-  Comparative Fit Index (CFI)                    0.988
-  Tucker-Lewis Index (TLI)                       0.981
-
-Loglikelihood and Information Criteria:
-
-  Loglikelihood user model (H0)              -4740.818
-  Loglikelihood unrestricted model (H1)      -4720.530
-                                                      
-  Akaike (AIC)                                9511.635
-  Bayesian (BIC)                              9583.007
-  Sample-size adjusted Bayesian (SABIC)       9535.371
-
-Root Mean Square Error of Approximation:
-
-  RMSEA                                          0.050
-  90 Percent confidence interval - lower         0.033
-  90 Percent confidence interval - upper         0.067
-  P-value H_0: RMSEA <= 0.050                    0.482
-  P-value H_0: RMSEA >= 0.080                    0.002
-
-Standardized Root Mean Square Residual:
-
-  SRMR                                           0.038
-
-Parameter Estimates:
-
-  Standard errors                             Standard
-  Information                                 Expected
-  Information saturated (h1) model          Structured
-
-Latent Variables:
-                           Estimate  Std.Err  z-value  P(>|z|)
-  generalized_prejudice =~                                    
-    EP                        1.000                           
-    DP                        0.710    0.041   17.383    0.000
-    SP                        0.907    0.052   17.337    0.000
-    HP                        1.042    0.112    9.267    0.000
-  openness =~                                                 
-    O1                        1.000                           
-    O2                        0.932    0.035   26.255    0.000
-    O3                        1.143    0.040   28.923    0.000
-
-Regressions:
-                          Estimate  Std.Err  z-value  P(>|z|)
-  generalized_prejudice ~                                    
-    openness                -0.766    0.056  -13.641    0.000
-
-Variances:
-                   Estimate  Std.Err  z-value  P(>|z|)
-   .EP                0.219    0.016   13.403    0.000
-   .DP                0.141    0.009   14.972    0.000
-   .SP                0.233    0.015   15.079    0.000
-   .HP                2.124    0.106   19.965    0.000
-   .O1                0.073    0.005   14.423    0.000
-   .O2                0.079    0.005   15.805    0.000
-   .O3                0.052    0.005    9.823    0.000
-   .generlzd_prjdc    0.191    0.018   10.362    0.000
-    openness          0.161    0.011   14.210    0.000
-
-
-

There are many different fit indices displayed in this output, for example the Comparative Fit Index (CFI), the Root Mean Square Error of Approximation (RMSEA) and the Standardized Root Mean Square Residual (SRMR). We can not go into the details of the interpretations of the fit indices here, but it is important to know that many of these indices are not very sensitive to sample size. Therefore, running a power analysis for these fit indices is not really meaningful. But instead of analyzing how one of these indices varies as a function of sample size, we can optimize the precision of one of these indices. For example, the lavaan output from above displays the 90% confidence interval for the RMSEA index. We could, for example, plan to find a certain sample size that ensures that the confidence interval around the RMSEA estimate has a certain maximum size, that is, the RMSEA estimate is sufficiently precise. This what we will learn to do in this chapter.

-

As a starting point, we again define the population parameters (i.e., the means, variances, and co-variances of all measured variables). We use the study by Bergh et al. (2016) to estimate these parameters (just as we did in Chapter 5).

-
-
attach(Bergh)
-
-#store means
-means_vector <- c(mean(EP), mean(SP), mean(HP), mean(DP), mean(O1), mean(O2), mean(O3)) |> round(2)
-
-#store covariances
-cov_mat <- cov(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2)
-
-

With these parameters, we can simulate data using the rmvnorm function from the Rfast package. The only difference to the simulation described in Chapter 5 is that here, we do not calculate and store the p-value of the regression coefficient, but rather, we compute the width of the RMSEA confidence interval and store it in a vector. We then count the number of simulations that yield a confidence interval with a maximum size of, say, .10. The next chunk shows how this is done.

-
-
set.seed(9875234)
-
-#test ns between 50 and 200
-ns <- seq(50, 200, by=10) 
-
-#prepare empty vector to store results
-result <- data.frame()
-
-#set number of iterations
-iterations <- 1000
-
-#write function
-sim_sem <- function(n, model, mu, sigma) {
-  
-
-  simulated_data <- Rfast::rmvnorm(n = n, mu = mu, sigma = sigma) |> as.data.frame()
-  fit_sem_simulated <- sem(model_sem, data = simulated_data)
-  rmsea_ci_width <- as.numeric(fitMeasures(fit_sem_simulated)["rmsea.ci.upper"] - fitMeasures(fit_sem_simulated)["rmsea.ci.lower"])
-  return(rmsea_ci_width)
-  
-    }
-
-
-#replicate function with varying ns
-for (n in ns) {  
-  
-rmsea_ci_width <- future_replicate(iterations, sim_sem(n = n, model = model_sem, mu = means_vector, sigma = cov_mat), future.seed=TRUE)  
-result <- rbind(result, data.frame(
-    n = n,
-    power = sum(rmsea_ci_width < .1)/iterations)
-  )
-
-}
-
-

Let’s plot this.

-
-
ggplot(result, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = "red")
-
-

-
-
-

This analysis suggests that approx. 168 participants are needed to obtain a 90%-confidence interval around the RMSEA coefficient that is not larger than .10 in 80% of the cases.

- - - -
- -
- - - - \ No newline at end of file diff --git a/docs/SEM_fit_index_files/figure-html/plot power curve-1.png b/docs/SEM_fit_index_files/figure-html/plot power curve-1.png deleted file mode 100644 index ba868a9..0000000 Binary files a/docs/SEM_fit_index_files/figure-html/plot power curve-1.png and /dev/null differ diff --git a/docs/how_many_iterations.html b/docs/how_many_iterations.html deleted file mode 100644 index 7ac3d6e..0000000 --- a/docs/how_many_iterations.html +++ /dev/null @@ -1,519 +0,0 @@ - - - - - - - - - - -Bonus: How many Monte-Carlo iterations are necessary? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Bonus: How many Monte-Carlo iterations are necessary?

-
- - - -
- -
-
Author
-
-

Felix Schönbrodt

-
-
- - -
- - -
- -

Reading/working time: ~25 min.

-
-
# Preparation: Install and load all necessary packages
-# install.packages(c("RcppArmadillo", "ggplot2", "patchwork", "pwr"))
-
-library(ggplot2)         # for plotting
-library(RcppArmadillo)   # for fast LMs
-library(patchwork)       # for arranging multiple ggplots
-library(pwr)             # for analytical power analysis
-
-

To find the sample size needed for a study, we have previously use, say, 1000 iterations of data simulation and analysis, and varied the sample size n from, say, 100 to 1000, every 50, to find where the 80% power threshold was crossed (see LM1.qmd#sample-size-planning-find-the-necessary-sample-size). But is 1000 iterations giving a precise enough result?

-

Using the optimized code, we can explore how many Monte-Carlo iterations are necessary to get stable computational results: we can re-run the same simulation with the same sample size and see how much random variation is between results!

-
-
-
- -
-
-Note -
-
-
-

Monte Carlo methods, or Monte Carlo experiments, are a broad class of computational algorithms that rely on repeated random sampling to obtain numerical results. The underlying concept is to use randomness to solve problems that might be deterministic in principle. They are often used in physical and mathematical problems and are most useful when it is difficult or impossible to use other approaches. Monte Carlo methods are mainly used in three problem classes: optimization, numerical integration, and generating draws from a probability distribution.

-
-
-

Let’s start with 1000 iterations (at n = 100, and 10 repetitions of the same power analysis):

-
-
set.seed(0xBEEF)
-iterations <- 1000
-
-# CHANGE: do the same sample size repeatedly, and see how much different runs deviate.
-ns <- rep(100, 10)
-
-result <- data.frame()
-
-for (n in ns) {
-  
-  x <- cbind(
-    rep(1, n),
-    c(rep(0, n/2), rep(1, n/2))
-  )
-  
-  p_values <- rep(NA, iterations)
-  
-  for (i in 1:iterations) {
-    y <- 23 - 3*x[, 2] + rnorm(n, mean=0, sd=sqrt(117))
-    
-    mdl <- RcppArmadillo::fastLmPure(x, y)
-    pval <- 2*pt(abs(mdl$coefficients/mdl$stderr), mdl$df.residual, lower.tail=FALSE)
-    
-    p_values[i] <- pval[2]
-  }
-  
-  result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))
-}
-
-result
-
-
     n power
-1  100 0.065
-2  100 0.070
-3  100 0.068
-4  100 0.066
-5  100 0.067
-6  100 0.073
-7  100 0.071
-8  100 0.070
-9  100 0.071
-10 100 0.063
-
-
-

As you can see, the power estimates show some variance, ranging from 0.063 to 0.073. This can be formalized as the Monte-Carlo error (MCE), which is define as “the standard deviation of the Monte Carlo estimator, taken across hypothetical repetitions of the simulation” (Koehler et al., 2009). With 1000 iterations (and 10 repetitions), this is:

-
-
sd(result$power) |> round(4)
-
-
[1] 0.0031
-
-
-

We only computed 10 repetitions of our power estimate, hence the MCE estimate is quite unstable. In the next computation, we will compute 100 repetitions of each power estimate (all with the same simulated sample size).

-

How much do we have to increase the iterations to achieve a MCE smaller than, say, 0.005 (i.e, an SD of +/- 0.5% of the power estimate)?

-

Let’s loop through increasing iterations (this takes a few minutes):

-
-
iterations <- seq(1000, 6000, by=1000)
-
-# let's have 100 repetitions to get sufficiently stable MCE estimates
-ns <- rep(100, 100)
-result <- data.frame()
-
-for (it in iterations) {
-
-  # print(it)  uncomment for showing the progress
-
-  for (n in ns) {
-    
-    x <- cbind(
-      rep(1, n),
-      c(rep(0, n/2), rep(1, n/2))
-    )
-    
-    p_values <- rep(NA, it)
-    
-    for (i in 1:it) {
-      y <- 23 - 3*x[, 2] + rnorm(n, mean=0, sd=sqrt(117))
-      
-      mdl <- RcppArmadillo::fastLmPure(x, y)
-      pval <- 2*pt(abs(mdl$coefficients/mdl$stderr), mdl$df.residual, lower.tail=FALSE)
-      
-      p_values[i] <- pval[2]
-    }
-    
-    result <- rbind(result, data.frame(iterations = it, n = n, power = sum(p_values < .005)/it))
-  }
-}
-
-# We can compute the exact power with the analytical solution:
-exact_power <- pwr.t.test(d = 3 / sqrt(117), sig.level = 0.005, n = 50)
-
-p1 <- ggplot(result, aes(x=iterations, y=power)) + stat_summary(fun.data=mean_cl_normal) + ggtitle("Power estimate (error bars = SD)") + geom_hline(yintercept = exact_power$power, colour = "blue", linetype = "dashed")
-
-p2 <- ggplot(result, aes(x=iterations, y=power)) + stat_summary(fun="sd", geom="point") + ylab("MCE") + ggtitle("Monte Carlo Error")
-
-p1/p2
-
-

-
-
-

As you can see, the MCE gets smaller with increasing iterations. The desired precision of MCE <= .005 can be achieved at around 3000 iterations (the dashed blue line is the exact power estimate from the analytical solution). While precision increases quickly by going from 1000 to 2000 iterations, further improvements are costly in terms of computation time. In sum, 3000 iterations seems to be a good compromise for this specific power simulation.

-
-
-
- -
-
-Note -
-
-
-

This choice of 3000 iterations does not necessarily generalize to other power simulations with other statistical models. But in my experience, 2000 iterations typically is a good (enough) choice. I often start with 500 iterations when exploring the parameter space (i.e., looking roughly for the range of reasonable sample sizes), and then “zoom” into this range with 2000 iterations.

-
-
-

In the lower plot, you can also see that the MCE estimate itself is a bit wiggly - we would expect a smooth curve. It suffers from meta-MCE! We could increase the precision of the MCE estimate by increasing the number of repetitions (currently at 100).

- - - -
- -
- - - - \ No newline at end of file diff --git a/docs/how_many_iterations_files/figure-html/unnamed-chunk-3-1.png b/docs/how_many_iterations_files/figure-html/unnamed-chunk-3-1.png deleted file mode 100644 index 592b7b5..0000000 Binary files a/docs/how_many_iterations_files/figure-html/unnamed-chunk-3-1.png and /dev/null differ diff --git a/docs/images/BDI-interaction-2.jpg b/docs/images/BDI-interaction-2.jpg deleted file mode 100644 index 3911a79..0000000 Binary files a/docs/images/BDI-interaction-2.jpg and /dev/null differ diff --git a/docs/images/LMM_Lafit_example.png b/docs/images/LMM_Lafit_example.png deleted file mode 100644 index b3f2a87..0000000 Binary files a/docs/images/LMM_Lafit_example.png and /dev/null differ diff --git a/docs/images/LMM_Murayama_app_example.png b/docs/images/LMM_Murayama_app_example.png deleted file mode 100644 index 4a5b244..0000000 Binary files a/docs/images/LMM_Murayama_app_example.png and /dev/null differ diff --git a/docs/images/choose_your_level.png b/docs/images/choose_your_level.png deleted file mode 100644 index 8c5a746..0000000 Binary files a/docs/images/choose_your_level.png and /dev/null differ diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 68b6237..0000000 --- a/docs/index.html +++ /dev/null @@ -1,477 +0,0 @@ - - - - - - - - - -Simulations for Advanced Power Analyses - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Simulations for Advanced Power Analyses

-
- - - -
- - - -
- - -
- -
-

About this work

-

This tutorial was created by Felix Schönbrodt and Moritz Fischer, with contributions from Malika Ihle, to be part of the training offering of the Ludwig-Maximilian University Open Science Center in Munich.

-
-
-
- -
-
-Note -
-
-
-

This book is still being developed. If you have comment to contribute to its improvement, you can submit pull requests in the respective .qmd file of the source repository by clicking on the ‘Edit this page’ and ‘Report an issue’ in the right navigation panel of each page.

-
-
-
-
-

Structure of the tutorial

-

Depending on your prior knowledge, you can fast forward some steps:

-

-
-

Acquire necessary basic coding skills in R

-

You need to know R programming basics. If you are unfamiliar with R, you are advised to follow a self-paced basic tutorial prior to the workshop, e.g.: https://www.tutorialspoint.com/r up to “data reshaping” (this tutorial, for example, takes around 2h and covers all necessary basics).

-

For a higher-level introduction to R coding skills you can do the self-paced tutorial Introduction to simulation in R. This tutorial teaches how to simulate data and writing functions in R, with the goal to e.g.

-
    -
  • check alpha so your statistical models don’t yield more than 5% false-positive results
  • -
  • check beta (power) for easy tests such as t-tests
  • -
  • prepare a preregistration and make sure your code works
  • -
  • check your understanding of statistics.
  • -
-
-
-

Comprehensive introduction to power analyses

-

Please read Chapter 1 of the SuperpowerBook by Aaron R. Caldwell, Daniël Lakens, Chelsea M. Parlett-Pelleriti, Guy Prochilo, Frederik Aust.

-

This introduction covers sample effect sizes vs population effect sizes, how to take into account the uncertainty of the sample effect size to create a safeguard effect size to be used in power analyses, why post hoc power analyses are pointless, and why it is better to calculate the minimal detectable effect instead.

-

The rest of the Superpower book teaches how to use the superpower R package to simulate factorial designs and calculate power, which may be of great interest to you! In our tutorial, we chose to teach how to write simulation ‘by hand’ so you can understand the concept and adapt it to any of your designs.

-
-
-

Tutorial structure

-

With these prerequisites, you can start to learn power calculations for different complex models. Here are the type of models we will cover, you can pick and choose what is relevant to you:

- -

We recommend that everybody works through chapters 1 and 2, and then dive into the other chapters that are relevant.

-

For each model, we will follow the structure:

-
    -
  • define what type of data and variables need to be simulated, i.e. their distribution, their class (e.g. factor vs numerical value)
  • -
  • generate data based on the equation of the model (data = model + error)
  • -
  • run the statistical test, and record the relevant statistic (e.g. p-value)
  • -
  • replicate step 2 and 3 to get the distribution of the statistic of interest (e.g. p-value)
  • -
  • analyze and interpret the combined results of many simulations i.e. check for which sample size you get at a significant result in 80% of the simulations
  • -
-
-
-

Install all packages

-

The following packages are necessary to reproduce the output of this tutorial. We recommend installing all of them before you dive into the individual chapters.

-
-
install.packages(c(
-              "ggplot2", 
-              "ggdist", 
-              "gghalves", 
-              "pwr", 
-              "MBESS", 
-              "Rfast", 
-              "DescTools", 
-              "lme4", 
-              "lmerTest", 
-              "tidyr", 
-              "Rfast", 
-              "future.apply", 
-              "lavaan", 
-              "MASS"), dependencies = TRUE, repos = "https://cran.rstudio.com/")
-
-install.packages("devtools")
-devtools::install_github("debruine/faux")
-
-
-
-
-

License & Funding note

-

This tutorial was initially commissioned and funded by the University of Hamburg, Faculty of Psychology and Movement Science.

-

It is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

- - -
- -
- -
- - - - \ No newline at end of file diff --git a/docs/optimizing_code.html b/docs/optimizing_code.html deleted file mode 100644 index 706bde5..0000000 --- a/docs/optimizing_code.html +++ /dev/null @@ -1,885 +0,0 @@ - - - - - - - - - - -Bonus: Optimizing R code for speed - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - - -
- -
-
-

Bonus: Optimizing R code for speed

-
- - - -
- -
-
Author
-
-

Felix Schönbrodt

-
-
- - -
- - -
- -

Reading/working time: ~30 min.

-
-
# Preparation: Install and load all necessary packages
-# install.packages(c("RcppArmadillo", "future.apply"))
-
-library(RcppArmadillo) # for fast LMs
-library(future.apply)  # for parallel processing
-
-# plan() initializes parallel processing, 
-# for faster code execution
-# By default, it uses all available cores
-plan(multisession)
-
-

Optimizing code for speed can be an art - and you get lost and spend/waste hours by micro-optimizing some milliseconds. But the Pareto principle applies here: with 20% effort, you can have quick and substantial gains.

-

Code profiling means that the code execution is timed, just like you had a stopwatch. Your goal is to make your code snippet as fast as possible. RStudio has a built-in profiler that (in theory) allows to see which code line takes up the longest time. But in my experience, if the computation of each single line is very short (and the duration mostly comes from the many repetitions), it is very inaccurate (i.e., the time spent is allocated to the wrong lines). Therefore, we’ll resort to the simplest way of timing code: We will measure overall execution time by wrapping our code in a system.time({ ... }) call. Longer code blocks need to be wrapped in curly braces {...}. The function returns multiple timings; the relevant number for us is the “elapsed” time. This is also called the “wall clock” time - the time you actually have to wait until computation finished.

-
-

First, naive version

-

Here is a first version of the power simulation code for a simple LM. Let’s see how long it takes:

-
-
t0 <- system.time({
-
-iterations <- 5000
-ns <- seq(300, 500, by=50)
-result <- data.frame()
-
-for (n in ns) {
-  p_values <- c()
-  
-  for (i in 1:iterations) {
-    treatment <- c(rep(0, n/2), rep(1, n/2))
-    BDI <- 23 - 3*treatment + rnorm(n, mean=0, sd=sqrt(117))
-    df <- data.frame(treatment, BDI)
-    res <- lm(BDI ~ treatment, data=df)
-    p_values <- c(p_values, summary(res)$coefficients["treatment", "Pr(>|t|)"])
-  }
-  
-  result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))
-}
-
-})
-t0
-
-
       User      System verstrichen 
-      9.627       0.218       9.888 
-
-
result
-
-
    n  power
-1 300 0.3348
-2 350 0.4088
-3 400 0.4914
-4 450 0.5358
-5 500 0.6146
-
-
-

This first version takes 9.888 seconds. Of course we have sampling error here as well; if you run this code multiple times, you will always get slightly different timings. But, again, we refrain from micro-optimizing in the millisecond range, so a single run is generally good enough. You should only tune your simulation in a way that it takes at least a few seconds; if you are in the millisecond range, the timings are imprecise and you won’t see speed improvements very well.

-
-
-

Rule 1: No growing vectors/data frames

-

This is one of the most common bottlenecks: You start with an empty vector (or even worse: data frame), and grow it by rbind-ing new rows to it in each iteration.

-
-
t1 <- system.time({
-
-iterations <- 5000
-ns <- seq(300, 500, by=50)
-
-result <- data.frame()
-
-for (n in ns) {
-  # print(n)   # uncomment to see progress
-  
-  # CHANGE: Preallocate vector with the final size, initialize with NAs
-  p_values <- rep(NA, iterations)
-  
-  for (i in 1:iterations) {
-    treatment <- c(rep(0, n/2), rep(1, n/2))
-    BDI <- 23 - 6*treatment + rnorm(n, mean=0, sd=sqrt(117))
-    df <- data.frame(treatment, BDI)
-    res <- lm(BDI ~ treatment, data=df)
-    
-    # CHANGE: assign resulting p-value to specific slot in vector
-    p_values[i] <- summary(res)$coefficients["treatment", "Pr(>|t|)"]
-  }
-  
-  # Here we stil have a growing data.frame - but as this is only done 5 times, it does not matter.
-  result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations)) 
-}
-
-})
-
-# Combine the different timings in a data frame
-timings <- rbind(t0[3], t1[3]) |> data.frame()
-
-# compute the absolute and relative difference of consecutive rows:
-timings$diff <- c(NA, timings[2:nrow(timings), 1] - timings[1:(nrow(timings)-1), 1])
-timings$rel_diff <- c(NA, timings[2:nrow(timings), "diff"]/timings[1:(nrow(timings)-1), 1]) |> round(3)
-
-timings
-
-
  elapsed  diff rel_diff
-1   9.888    NA       NA
-2  10.579 0.691     0.07
-
-
-

OK, this didn’t really change anything here. But in general (in particular with data frames) this is worth looking at.

-
-
-

Rule 2: Avoid data frames as far as possible

-

Use matrizes instead of data frames wherever possible; or avoid them at all (as we do in the code below).

-
-
t2 <- system.time({
-
-iterations <- 5000
-ns <- seq(300, 500, by=50)
-
-result <- data.frame()
-
-for (n in ns) {
-  treatment <- c(rep(0, n/2), rep(1, n/2))
-  p_values <- rep(NA, iterations)
-  
-  for (i in 1:iterations) {
-    BDI <- 23 - 3*treatment + rnorm(n, mean=0, sd=sqrt(117))
-    
-    # CHANGE: We don't need the data frame - just create the two variables
-    # in the environment and lm() takes them from there.
-    #df <- data.frame(treatment, BDI)
-    res <- lm(BDI ~ treatment)
-    
-    p_values[i] <- summary(res)$coefficients["treatment", "Pr(>|t|)"]
-  }
-  
-  result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))
-}
-
-})
-
-timings <- rbind(t0[3], t1[3], t2[3]) |> data.frame()
-timings$diff <- c(NA, timings[2:nrow(timings), 1] - timings[1:(nrow(timings)-1), 1])
-timings$rel_diff <- c(NA, timings[2:nrow(timings), "diff"]/timings[1:(nrow(timings)-1), 1]) |> round(3)
-timings
-
-
  elapsed   diff rel_diff
-1   9.888     NA       NA
-2   9.623 -0.265   -0.027
-3   7.384 -2.239   -0.233
-
-
-

This showed a substantial improvement of around -3.2 seconds; a relative gain of -23.3%.

-
-
-

Rule 3: Avoid unnecessary computations

-

What do we actually need? In fact only the p-value for our focal predictor. But the lm function does so many more things, for example parsing the formula BDI ~ treatment.

-

We could strip away all overhead and do only the necessary steps: Fit the linear model, and retrieve the p-values (see https://stackoverflow.com/q/49732933). This needs some deeper knowledge of the functions and some google-fu. When you do this, you should definitely compare your results with the original result from the lm function and verify that they are identical!

-
-
t3 <- system.time({
-
-iterations <- 5000
-ns <- seq(300, 500, by=50)
-
-result <- data.frame()
-
-for (n in ns) {
-  # construct the design matrix: first column is all-1 (intercept), second column is the treatment factor
-  x <- cbind(
-    rep(1, n),
-    c(rep(0, n/2), rep(1, n/2))
-  )
-  
-  p_values <- rep(NA, iterations)
-  
-  for (i in 1:iterations) {
-    y <- 23 - 3*x[, 2] + rnorm(n, mean=0, sd=sqrt(117))
-
-    # For comparison - do we get the same results? Yes!
-    # res0 <- lm(y ~ x[, 2])
-    # summary(res0)
-    
-    # fit the model:
-    m <- .lm.fit(x, y)
-    
-    # compute p-values based on the residuals:
-    rss <- sum(m$residuals^2)
-    rdf <- length(y) - ncol(x)
-    resvar <- rss/rdf
-    R <- chol2inv(m$qr)
-    se <- sqrt(diag(R) * resvar)
-    ps <- 2*pt(abs(m$coef/se),rdf,lower.tail=FALSE)
-    
-    p_values[i] <- ps[2]
-  }
-  
-  result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))
-}
-
-})
-timings <- rbind(t0[3], t1[3], t2[3], t3[3]) |> data.frame()
-timings$diff <- c(NA, timings[2:nrow(timings), 1] - timings[1:(nrow(timings)-1), 1])
-timings$rel_diff <- c(NA, timings[2:nrow(timings), "diff"]/timings[1:(nrow(timings)-1), 1]) |> round(3)
-timings
-
-
  elapsed   diff rel_diff
-1   9.888     NA       NA
-2   9.623 -0.265   -0.027
-3   7.384 -2.239   -0.233
-4   0.793 -6.591   -0.893
-
-
-

This step led to a massive improvement of around -6.6 seconds; a relative gain of -89.3%.

-
-
-

Rule 4: Use optimized packages

-

For many statistical models, there are packages optimized for speed, see for example here: https://stackoverflow.com/q/49732933

-
-
t4 <- system.time({
-
-iterations <- 5000
-ns <- seq(300, 500, by=50)
-
-result <- data.frame()
-
-for (n in ns) {
-  # construct the design matrix: first column is all-1 (intercept), second column is the treatment factor
-  x <- cbind(
-    rep(1, n),
-    c(rep(0, n/2), rep(1, n/2))
-  )
-  
-  p_values <- rep(NA, iterations)
-  
-  for (i in 1:iterations) {
-    y <- 23 - 3*x[, 2] + rnorm(n, mean=0, sd=sqrt(117))
-
-    # For comparison - do we get the same results? Yes!
-    # res0 <- lm(y ~ x[, 2])
-    # summary(res0)
-    
-    mdl <- RcppArmadillo::fastLmPure(x, y)
-    
-    # compute the p-value - but only for the coefficient of interest!
-    p_values[i] <- 2*pt(abs(mdl$coefficients[2]/mdl$stderr[2]), mdl$df.residual, lower.tail=FALSE)
-  }
-  
-  result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))
-}
-
-})
-timings <- rbind(t0[3], t1[3], t2[3], t3[3], t4[3]) |> data.frame()
-timings$diff <- c(NA, timings[2:nrow(timings), 1] - timings[1:(nrow(timings)-1), 1])
-timings$rel_diff <- c(NA, timings[2:nrow(timings), "diff"]/timings[1:(nrow(timings)-1), 1]) |> round(3)
-timings
-
-
  elapsed   diff rel_diff
-1   9.888     NA       NA
-2  10.579  0.691    0.070
-3   7.384 -3.195   -0.302
-4   0.793 -6.591   -0.893
-5   0.764 -0.029   -0.037
-
-
-

This step only gave a minor -4% relative increase in speed - but as a bonus, it made our code much easier to read and shorter.

-
-
-

Rule 5: Go parallel

-

By default, R runs single-threaded. That means, a single CPU core works off all lines of code sequentially. When you optimized this single thread performance, the only way to gain more speed (except buying a faster computer) is to distribute the workload to multiple CPU cores that work in parallel. Every modern CPU comes with multiple cores (also called “workers” in the code); typically 4 to 8 on local computers and laptops.

-
-

Preparation: Wrap the simulation in a function

-

The first step does not really change a lot: We put the simulation code into a separate function that returns the quantity of interest (in our case: the focal p-value). Different settings of the simulation parameters, such as the sample size or the effect size, can be defined as parameters of the function.

-

Every single function call sim() now gives you one simulated p-value - try it out!

-

We then use the replicate function to run the sim function many times and to store the resulting p-values in a vector. Programming the simulation in such a functional style also has the nice side effect that you do not have to pre-allocate the results vector; this is automatically done by the replicate function.

-
-
# Wrap the code for a single simulation into a function. It returns the quantity of interest.
-sim <- function(n=100) {
-  # the "n" is now taken from the function parameter "n"
-  x <- cbind(
-    rep(1, n),
-    c(rep(0, n/2), rep(1, n/2))
-  )
-  
-  y <- 23 - 3*x[, 2] + rnorm(n, mean=0, sd=sqrt(117))
-  mdl <- RcppArmadillo::fastLmPure(x, y)
-  p_val <- 2*pt(abs(mdl$coefficients[2]/mdl$stderr[2]), mdl$df.residual, lower.tail=FALSE)
-
-  return(p_val)
-}
-
-t5 <- system.time({
-
-iterations <- 5000
-ns <- seq(300, 500, by=50)
-
-result <- data.frame()
-
-for (n in ns) {
-  p_values <- replicate(n=iterations, sim(n=n))
-  result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))
-}
-
-})
-timings <- rbind(t0[3], t1[3], t2[3], t3[3], t4[3], t5[3]) |> data.frame()
-timings$diff <- c(NA, timings[2:nrow(timings), 1] - timings[1:(nrow(timings)-1), 1])
-timings$rel_diff <- c(NA, timings[2:nrow(timings), "diff"]/timings[1:(nrow(timings)-1), 1]) |> round(3)
-timings
-
-
  elapsed   diff rel_diff
-1   9.888     NA       NA
-2  10.579  0.691    0.070
-3   7.384 -3.195   -0.302
-4   0.793 -6.591   -0.893
-5   0.764 -0.029   -0.037
-6   0.935  0.171    0.224
-
-
-

While this refactoring actually slightly increased computation time, we need this for the last, final optimization where we reap the benefits.

-
-
-

Run on multiple cores

-

With the use of the replicate function in the previous step, we prepared everything for an easy switch to multi-core processing. You only need to load the future.apply package, start a multi-core session with the plan command, and replace the replicate function call with future_replicate.

-
-
# Show how many cores are available on your machine:
-availableCores()
-
-# with plan() you enter the parallel mode. Enter the number of workers (aka. CPU cores)
-plan(multisession, workers = 4)
-
-t6 <- system.time({
-
-iterations <- 5000
-ns <- seq(300, 500, by=50)
-
-result <- data.frame()
-
-for (n in ns) {
-  # future.seed = TRUE is needed to set seeds in all parallel processes. Then the computation is reproducible.
-  p_values <- future_replicate(n=iterations, sim(n=n), future.seed = TRUE)
-  result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))
-}
-
-})
-
-timings <- rbind(t0[3], t1[3], t2[3], t3[3], t4[3], t5[3], t6[3]) |> data.frame()
-timings$diff <- c(NA, timings[2:nrow(timings), 1] - timings[1:(nrow(timings)-1), 1])
-timings$rel_diff <- c(NA, timings[2:nrow(timings), "diff"]/timings[1:(nrow(timings)-1), 1]) |> round(3) |> round(2)
-timings
-
-
-
-
  elapsed   diff rel_diff
-1   9.888     NA       NA
-2  10.579  0.691     0.07
-3   7.384 -3.195    -0.30
-4   0.793 -6.591    -0.89
-5   0.764 -0.029    -0.04
-6   0.935  0.171     0.22
-7   0.788 -0.147    -0.16
-
-
-

The speed improvement seems only small - with 4 workers, one might expect that the computations only need 1/4th of the previous time. But parallel processing creates some overhead. For example, 4 separate R sessions need to be created and all packages, code (and sometimes data) need to be loaded in each session. Finally, all results must be collected and aggregated from all separate sessions. This can add up to substantial one-time costs. If your (single-core) computations only take a few seconds or less, parallel processing can even take longer.

-
-
-
-

The final speed test: Burn your machine 🔥

-

Let’s see if parallel processing has an advantage when we have longer computations. We now expand the simulation by exploring a broad parameter range (n ranging from 100 to 1000) and increasing the iterations to 20,000 for more stable results. (See also: “Bonus: How many Monte-Carlo iterations are necessary?”)

-
-
plan(multisession, workers = 4)
-
-iterations <- 20000
-ns <- seq(100, 1000, by=50)
-result_single <- result_parallel <- data.frame()
-
-# single core
-t_single <- system.time({
-  for (n in ns) {
-    p_values <- replicate(n=iterations, sim(n=n))
-    result_single <- rbind(result_single, data.frame(n = n, power = sum(p_values < .005)/iterations))
-  }
-})
-
-# multi-core
-t_parallel <- system.time({
-  for (n in ns) {
-    p_values <- future_replicate(n=iterations, sim(n=n), future.seed = TRUE)
-    result_parallel <- rbind(result_parallel, data.frame(n = n, power = sum(p_values < .005)/iterations))
-  }
-})
-
-# compare results
-cbind(result_single, power.parallel = result_parallel[, 2])
-
-
      n   power power.parallel
-1   100 0.07075        0.07395
-2   150 0.13085        0.12945
-3   200 0.19460        0.19165
-4   250 0.26615        0.26295
-5   300 0.33020        0.33635
-6   350 0.41320        0.40745
-7   400 0.48625        0.47710
-8   450 0.54520        0.55245
-9   500 0.61160        0.61090
-10  550 0.67060        0.66615
-11  600 0.71825        0.72150
-12  650 0.75465        0.76470
-13  700 0.80650        0.79815
-14  750 0.83965        0.83605
-15  800 0.86445        0.86475
-16  850 0.89125        0.88980
-17  900 0.91100        0.90960
-18  950 0.92700        0.93055
-19 1000 0.94025        0.94065
-
-
rbind(t_single, t_parallel) |> data.frame()
-
-
           user.self sys.self elapsed user.child sys.child
-t_single      15.613    0.784  16.446          0         0
-t_parallel     1.071    0.064   6.622          0         0
-
-
-
- -
-

With this optimized setup, we are running 380000 simulations in just 6.622 seconds. If you try this with the first code version, it takes 165.732 seconds.

-

With the final, parallelized version we have a 25x speed gain relative to the first version!

-
-
-

Recap

-

We covered the most important steps for speeding up your code in R:

-
    -
  1. No growing vectors/data frames. Solution: Pre-allocate the results vector.
  2. -
  3. Avoid data.frames. Solution: Use matrices wherever possible, or switch to data.table for more complex data structures (not covered here).
  4. -
  5. Avoid unnecessary computations and/or switch to optimized packages that do the same computations much faster, such as the package Rfast.
  6. -
  7. Switch to parallel processing. Solution: If you already programmed your simulations with the replicate function, it is very easy with the future.apply package.
  8. -
-

Some steps, such as avoiding growing vectors, didn’t really help in our current example, but will help a lot in other scenarios.

-

There are many blog post showing and comparing strategies to increase R performance, e.g.:

- -
-
-
- -
-
-But always remember: -
-
-
-

“We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.”

-

Donald Knuth (Structured Programming with go to Statements, ACM Journal Computing Surveys, Vol 6, No. 4, Dec. 1974. p. 268)

-
-
-
-
-

Session Info

-

These speed measurements have been performed on a 2021 MacBook Pro with M1 processor.

-
-
sessionInfo()
-
-
R version 4.2.0 (2022-04-22)
-Platform: aarch64-apple-darwin20 (64-bit)
-Running under: macOS 13.2.1
-
-Matrix products: default
-BLAS:   /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRblas.0.dylib
-LAPACK: /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRlapack.dylib
-
-locale:
-[1] de_DE.UTF-8/de_DE.UTF-8/de_DE.UTF-8/C/de_DE.UTF-8/de_DE.UTF-8
-
-attached base packages:
-[1] stats     graphics  grDevices utils     datasets  methods   base     
-
-other attached packages:
-[1] future.apply_1.10.0      future_1.32.0            RcppArmadillo_0.12.0.1.0
-[4] prettycode_1.1.0         colorDF_0.1.7           
-
-loaded via a namespace (and not attached):
- [1] Rcpp_1.0.10       parallelly_1.35.0 knitr_1.42        magrittr_2.0.3   
- [5] rlang_1.1.0       fastmap_1.1.1     globals_0.16.2    tools_4.2.0      
- [9] parallel_4.2.0    xfun_0.37         cli_3.6.1         htmltools_0.5.5  
-[13] yaml_2.3.7        digest_0.6.31     lifecycle_1.0.3   crayon_1.5.2     
-[17] purrr_1.0.1       htmlwidgets_1.6.2 vctrs_0.6.1       codetools_0.2-19 
-[21] evaluate_0.20     rmarkdown_2.20    compiler_4.2.0    jsonlite_1.8.4   
-[25] listenv_0.9.0    
-
-
- - -
- -
- -
- - - - \ No newline at end of file diff --git a/docs/search.json b/docs/search.json deleted file mode 100644 index 2195710..0000000 --- a/docs/search.json +++ /dev/null @@ -1,303 +0,0 @@ -[ - { - "objectID": "GLM.html", - "href": "GLM.html", - "title": "Ch. 3: Generalized Linear Models", - "section": "", - "text": "Reading/working time: ~35 min.\nIn the previous chapters, we have learned how to conduct simulation-based power analyses for linear regressions with categorical and/or continuous predictor variables. In this chapter, we will turn to generalized linear models, which constitute a generalization of regular linear regressions. This class of statistical models additionally comprise more complex models such as logistic regression and poisson regression. However, as covering all of variants of the generalized linear models would exceed the scope of this workshop, we will only learn how to conduct a simulation-based power analysis for a logistic regression. Recall that a logistic regression is suitable for designs in which you predict a dichotomous outcome with a set of (continuous or categorical) predictors." - }, - { - "objectID": "GLM.html#lets-get-some-data-as-a-starting-point", - "href": "GLM.html#lets-get-some-data-as-a-starting-point", - "title": "Ch. 3: Generalized Linear Models", - "section": "Let’s get some data as a starting point", - "text": "Let’s get some data as a starting point\n\n# load the data\ndata(\"BtheB\", package = \"HSAUR\")\n\n# show which variables this data set includes\nstr(BtheB)\n\n'data.frame': 100 obs. of 8 variables:\n $ drug : Factor w/ 2 levels \"No\",\"Yes\": 1 2 2 1 2 2 2 1 2 2 ...\n $ length : Factor w/ 2 levels \"<6m\",\">6m\": 2 2 1 2 2 1 1 2 1 2 ...\n $ treatment: Factor w/ 2 levels \"TAU\",\"BtheB\": 1 2 1 2 2 2 1 1 2 2 ...\n $ bdi.pre : num 29 32 25 21 26 7 17 20 18 20 ...\n $ bdi.2m : num 2 16 20 17 23 0 7 20 13 5 ...\n $ bdi.4m : num 2 24 NA 16 NA 0 7 21 14 5 ...\n $ bdi.6m : num NA 17 NA 10 NA 0 3 19 20 8 ...\n $ bdi.8m : num NA 20 NA 9 NA 0 7 13 11 12 ...\n\n\nThis data set compares the effects of two interventions for depression, that is a so-called “Beat the blues” intervention (BtheB) and a “treatment-as-usual” intervention (TAU). Note that in this chapter we will compare two active treatment conditions with each other - in the other chapters on linear models, we compare an active treatment with a passive waiting group.\nUnfortunately, this data set does not include any dichotomous variable we could use as an outcome measure. Therefore, we simply dichotomize one of the continuous outcome variables to create a categorical outcome artificially. More specifically, we will dichotomize the bdi.2m variable which contains a follow-up depression measure (i.e., the Beck Depression Inventory score) two months after the intervention.\n\n\n\n\n\n\nNote\n\n\n\nPlease note that we dichotomize this variable for didactic reasons only. From a statistical point of view, it is preferable to treat a continuous variable as such because dichotomizing leads to a loss of statistical information (Royston et al., 2006).\n\n\nLet’s start by dichotomizing the bdi.2m variable and storing the result in a new variable which we label bdi.2m.dicho. Here, we take 20 as our cut-off value, as BDIs of 20 or more refer to a moderate or severe depression. BDI values lower than 20, in turn, reflect a minimal or mild depression (see first chapter on linear models for more info). In our new bdi.2m.dicho variable, we code minimal/mild depression as “0” and moderate/severe depression as “1”.\n\n#dichotomize dv\nBtheB$bdi.2m.dicho <- ifelse(BtheB$bdi.2m < 20, 0, 1)\n\n#show frequencies\ntable(BtheB$bdi.2m.dicho, BtheB$treatment)\n\n \n TAU BtheB\n 0 21 37\n 1 24 15\n\n\nThe frequency table shows that there were less patients with moderate/severe depression in the TAU treatment as compared to the BtheB treatment. That’s a first sign that the BtheB intervention might outperform the treatment-as-usual!\nIn this chapter, we focus on a very simple version of a logistic regression: A model in which the dichotomous outcome variable is predicted by one categorical predictor variable, that is, the treatment condition (BtheB vs. TAU). Let’s assume that we plan to set up a study in which we want to scrutinize whether or not the “Beat the blues” intervention really outperforms the “treatment-as-usual” intervention with regard to the BDI scores two months after the end of the interventions. How many participants would we need for such a study to achieve 80% power?" - }, - { - "objectID": "GLM.html#estimating-the-population-parameters", - "href": "GLM.html#estimating-the-population-parameters", - "title": "Ch. 3: Generalized Linear Models", - "section": "Estimating the population parameters", - "text": "Estimating the population parameters\nAs in the previous chapters, we first need to estimate all population parameters relevant for this study. More specifically, we will need three estimates:\n\nthe proportion of participants in the BtheB condition\nthe probability of a favorable outcome in the BtheB condition\nthe probability of a favorable outcome in the TAU condition\n\nThe first parameter is pretty easy to estimate. In most cases, researchers will assign half of the participants to the treatment condition and the other half to a control condition. In this case, the probability of being in the BtheB condition would be 50%. Let’s store that in a variable called prop_btheb.\n\nprop_btheb <- 0.5\n\nThe other two estimates are only slightly more complicated to estimate. The probability of a favorable outcome in each of the conditions basically means: How many of the participants will not have a moderate/severe depression two months after completing the treatment-as-usual? And, how many of the participants will not have a moderate/severe depression two months after completing the Beat the blues intervention?\nThese two probabilities can only be estimated with solid pilot data. In our case, we can estimate both probabilities from the BtheB data set. The following chunk does that, rounds the estimates to two decimals, and stores the estimates in two objects called prop_tau and prop_btheb.\n\nprob_tau <- round(length(BtheB$treatment[BtheB$treatment == \"TAU\" & BtheB$bdi.2m.dicho == 0 & !is.na(BtheB$bdi.2m.dicho)]) / length(BtheB$treatment[BtheB$treatment == \"TAU\" & !is.na(BtheB$bdi.2m.dicho)]),2) \nprob_tau\n\n[1] 0.47\n\nprob_btheb <- round(length(BtheB$treatment[BtheB$treatment == \"BtheB\" & BtheB$bdi.2m.dicho == 0 & !is.na(BtheB$bdi.2m.dicho)]) / length(BtheB$treatment[BtheB$treatment == \"BtheB\" & !is.na(BtheB$bdi.2m.dicho)]),2)\nprob_btheb\n\n[1] 0.71\n\n\nIn the BtheB data set, the probability of not having a moderate/ severe depression in the BtheB condition was 0.71 and the same probability in the TAU condition was 0.47.\nNow we have all we need to start simulating data. There are multiple ways to simulate this kind of data, but one very simple way is to apply the sample() function. Here, we use it twice: Once to draw for the TAU condition and once for the BtheB condition (which differ in their probabilities of yielding a positive outcome, as we have seen before). For each condition, we draw whether or not the participant has a moderate/severe depression two months after the treatment, while coding “no moderate/severe depression” with 0 and “moderate/severe depression” with 1. Let’s try this by sampling 100 observations per condition.\n\nset.seed(8526)\n\n#sample from distribution in \"TAU\" condition\ntau <- cbind(rep(\"TAU\", 100), sample(x = c(0,1), replace = TRUE, prob = c(prob_tau, 1-prob_tau), size = 100))\n\n#sample from distribution in \"BtheB\" condition\nbtheb <- cbind(rep(\"BtheB\", 100), sample(x = c(0,1), replace = TRUE, prob = c(prob_btheb, 1-prob_btheb), size = 100))\n\nWe now have two data frames (labeled tau and btheB), one per condition. In the following chunk, we combine them into one data frame, change the variable names, transform the treatment variables to a factor, transform the outcome variable to an integer variable, and edit the factor levels – all of this is necessary to run our logistic regression on this data set later.\n\n#combine data frames\nsimulated_data <- rbind(tau, btheb) |> as.data.frame()\n\n#set variable names \ncolnames(simulated_data) <- c(\"treatment\", \"bdi.2m.dicho\")\n\n#change treatment variable to factor\nsimulated_data$treatment <- simulated_data$treatment |> as.factor()\n\n#change outcome variable to integer\nsimulated_data$bdi.2m.dicho <- simulated_data$bdi.2m.dicho |> as.integer()\n\n#reverse factor levels, this affects the coding of this factor in the regression analysis later\nsimulated_data$treatment <- factor(simulated_data$treatment, levels=rev(levels(simulated_data$treatment)))\n\nWith this first simulated data set, we can now perform a first logistic regression.\n\nsimulated_fit <- glm(bdi.2m.dicho ~ treatment, data = simulated_data, family = \"binomial\")\n\nsummary(simulated_fit)\n\n\nCall:\nglm(formula = bdi.2m.dicho ~ treatment, family = \"binomial\", \n data = simulated_data)\n\nDeviance Residuals: \n Min 1Q Median 3Q Max \n-1.3354 -0.8615 -0.8615 1.0273 1.5305 \n\nCoefficients:\n Estimate Std. Error z value Pr(>|z|) \n(Intercept) 0.3640 0.2033 1.790 0.0734 . \ntreatmentBtheB -1.1641 0.2968 -3.922 8.78e-05 ***\n---\nSignif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n\n(Dispersion parameter for binomial family taken to be 1)\n\n Null deviance: 275.26 on 199 degrees of freedom\nResidual deviance: 259.19 on 198 degrees of freedom\nAIC: 263.19\n\nNumber of Fisher Scoring iterations: 4\n\n\nHere, we find a negative and significant regression weight for the treatment predictor of -1.16, indicating that the BtheB treatment led to less moderate/severe depressions as compared to the TAU treatment. But, actually, it is not our central interest to test whether this particular simulated data set results in a significant treatment effect. What we really want to know is: How many of a theoretically infinite number of simulations yield a significant p-value of this effect? Thus, as in the previous chapters, we now repeatedly simulate data sets of a certain size from the specified population and store the results of the focal test (here: the p-value of the regression coefficient) in a vector called p_values." - }, - { - "objectID": "GLM.html#lets-do-the-power-analysis", - "href": "GLM.html#lets-do-the-power-analysis", - "title": "Ch. 3: Generalized Linear Models", - "section": "Let’s do the power analysis", - "text": "Let’s do the power analysis\n\n#write a function to automize the data simulation process\nsimulation <- function(n, p0, p1, prop = .50){\n \n #sample from distribution in \"TAU\" condition\n tau <- cbind(rep(\"TAU\", n*prop), sample(x = c(0,1), replace = TRUE, prob = c(p0, 1-p0), size = n*prop))\n \n #sample from distribution in \"BtheB\" condition\n btheb <- cbind(rep(\"BtheB\", n*prop), sample(x = c(0,1), replace = TRUE, prob = c(p1, 1-p1), size = n*prop))\n \n #combine both data sets and do some preprocessing\n simulated_data <- rbind(tau, btheb) |> as.data.frame()\n colnames(simulated_data) <- c(\"treatment\", \"bdi.2m.dicho\")\n simulated_data$treatment <- simulated_data$treatment |> as.factor()\n simulated_data$bdi.2m.dicho <- simulated_data$bdi.2m.dicho |> as.numeric()\n simulated_data$treatment <-factor(simulated_data$treatment, levels=rev(levels(simulated_data$treatment)))\n return(simulated_data)\n}\n\n\n#prepare empty vector to store the p-values\np_value <- NULL\n\n#prepare empty vector to store the results (i.e., the power per sample size)\nresults <- data.frame()\n\n\n# write function to store results of simulation\nsim <- function(n, p0, p1, prop = .50){\n \n #simulate data\n data <- simulation(n = n, p0 = p0, p1 = p1, prop = .50)\n \n #run regression\n simulated_fit <- glm(bdi.2m.dicho ~ treatment, data = data, family = \"binomial\")\n \n #store p-value\n p_value <- coef(summary(simulated_fit))[2,4]\n \n #return p-value\n return(p_value)\n\n}\n\n# set range of sample sizes\nns <- seq(from = 20, to = 500, by = 10)\n\n# set number of iterations\niterations <- 1000\n\n# perform power analysis\nfor(n in ns){\n \np_values <- replicate(iterations, sim(n, p0 = prob_tau, p1 = prob_btheb, prop = prop)) \nresults <- rbind(results, data.frame(\n n = n,\n power = sum(p_values < .005)/iterations)\n )\n}\n\nLet’s visualize the results.\n\nggplot(results, aes(x=n, y=power)) + geom_point() + geom_line() + scale_y_continuous(n.breaks = 10) + scale_x_continuous(n.breaks = 20) + geom_hline(yintercept= 0.8, color = \"red\")\n\n\n\n\nThis plot shows that we need approx. 220 participants to achieve 80% under the given assumptions. Note that this is the overall sample size, not the size per condition! That’s it - we have performed a simulation-based power analysis for a logistic regression!" - }, - { - "objectID": "GLM.html#verification-with-the-pwr2ppl-package", - "href": "GLM.html#verification-with-the-pwr2ppl-package", - "title": "Ch. 3: Generalized Linear Models", - "section": "Verification with the pwr2ppl package", - "text": "Verification with the pwr2ppl package\nLuckily, there are also R packages that can perform these kinds of power analyses, for example the pwr2ppl package (Aberson, 2019). We can use this package to verify our results. Does the pwr2ppl package yield the same result? Note that you will need to install this package if you haven’t used it before.\n\n#install.packages(\"devtools\")\n#devtools::install_github(\"chrisaberson/pwr2ppl\")\npwr2ppl::LRcat(p0 = prob_tau, p1 = prob_btheb, prop = .50,alpha = .005, power = .80)\n\nSample Size = 221 for Odds Ratio = 2.761\n\n\nThat’s basically the same result, well done!" - }, - { - "objectID": "GLM.html#using-a-safeguard-power-approach", - "href": "GLM.html#using-a-safeguard-power-approach", - "title": "Ch. 3: Generalized Linear Models", - "section": "Using a safeguard power approach", - "text": "Using a safeguard power approach\nOf note, our effect size estimates (i.e, the estimates of the probabilities of not having a moderate/severe depression two months after the BtheB or the TAU treatment) were so far based on a pilot study. However, this pilot study might not have yielded a precise estimate of these effect sizes. Thus, in order to consider uncertainty in these effect size estimates, it has been suggested to perform a safeguard power analysis (see chapter first chapter on linear models for more info) instead of a power analysis using the observed effect size. The main idea behind the safeguard power analysis is to compute a 60% confidence interval around the observed effect size, and to take the lower bound of this confidence interval as an effect size estimate (see Perugini et al., 2014). Let’s try this here.\nLet’s assume that we view the probability of not having a moderate/severe depression after the TAU treatment to be probably accurate, but that we want to account for uncertainty in the estimation of the probability of not having a moderate/severe depression after the BtheB treatment. We then simply calculate the 60% confidence interval around this effect size. We can use the BinomCI function from the DescTools package to do this. We need to provide this function with the number of successes (here: 37, see table above) and the number of observations (here: 52, see above).\n\nDescTools::BinomCI(37, 52, conf.level = 0.60)\n\n est lwr.ci upr.ci\n[1,] 0.7115385 0.6560993 0.761292\n\n\nThis gives us an estimate of 0.66 for the lower bound of the 60% confidence interval of the probability of not having a moderate/severe depression after the BtheB treatment, while the point estimate for this effect size was 0.71. We can now redo our power analysis from above with this new effect size estimation. I am copying the chunk from above, while replacing the prob_btheb value with 0.66.\n\n#prepare empty vector to store the p-values\np_value <- NULL\n\n#prepare empty vector to store the results (i.e., the power per sample size)\nresults <- data.frame()\n\n# set range of sample sizes\nns <- seq(from = 20, to = 500, by = 10)\n\n# set number of iterations\niterations <- 1000\n\n# perform power analysis\nfor(n in ns){\n \np_values <- replicate(iterations, sim(n, p0 = prob_tau, p1 = 0.66, prop = prop)) \nresults <- rbind(results, data.frame(\n n = n,\n power = sum(p_values < .005)/iterations)\n )\n}\n\n#let's plot this\nggplot(results, aes(x=n, y=power)) + geom_point() + geom_line() + scale_y_continuous(n.breaks = 10) + scale_x_continuous(n.breaks = 20) + geom_hline(yintercept= 0.8, color = \"red\")\n\n\n\n\nHere, we get a total sample size of ca. 360 participants in order to ensure 80% power with our safeguard estimation." - }, - { - "objectID": "how_many_iterations.html", - "href": "how_many_iterations.html", - "title": "Bonus: How many Monte-Carlo iterations are necessary?", - "section": "", - "text": "# Preparation: Install and load all necessary packages\n# install.packages(c(\"RcppArmadillo\", \"ggplot2\", \"patchwork\", \"pwr\"))\n\nlibrary(ggplot2) # for plotting\nlibrary(RcppArmadillo) # for fast LMs\nlibrary(patchwork) # for arranging multiple ggplots\nlibrary(pwr) # for analytical power analysis\n\nTo find the sample size needed for a study, we have previously use, say, 1000 iterations of data simulation and analysis, and varied the sample size n from, say, 100 to 1000, every 50, to find where the 80% power threshold was crossed (see LM1.qmd#sample-size-planning-find-the-necessary-sample-size). But is 1000 iterations giving a precise enough result?\nUsing the optimized code, we can explore how many Monte-Carlo iterations are necessary to get stable computational results: we can re-run the same simulation with the same sample size and see how much random variation is between results!\n\n\n\n\n\n\nNote\n\n\n\nMonte Carlo methods, or Monte Carlo experiments, are a broad class of computational algorithms that rely on repeated random sampling to obtain numerical results. The underlying concept is to use randomness to solve problems that might be deterministic in principle. They are often used in physical and mathematical problems and are most useful when it is difficult or impossible to use other approaches. Monte Carlo methods are mainly used in three problem classes: optimization, numerical integration, and generating draws from a probability distribution.\n\n\nLet’s start with 1000 iterations (at n = 100, and 10 repetitions of the same power analysis):\n\nset.seed(0xBEEF)\niterations <- 1000\n\n# CHANGE: do the same sample size repeatedly, and see how much different runs deviate.\nns <- rep(100, 10)\n\nresult <- data.frame()\n\nfor (n in ns) {\n \n x <- cbind(\n rep(1, n),\n c(rep(0, n/2), rep(1, n/2))\n )\n \n p_values <- rep(NA, iterations)\n \n for (i in 1:iterations) {\n y <- 23 - 3*x[, 2] + rnorm(n, mean=0, sd=sqrt(117))\n \n mdl <- RcppArmadillo::fastLmPure(x, y)\n pval <- 2*pt(abs(mdl$coefficients/mdl$stderr), mdl$df.residual, lower.tail=FALSE)\n \n p_values[i] <- pval[2]\n }\n \n result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))\n}\n\nresult\n\n n power\n1 100 0.065\n2 100 0.070\n3 100 0.068\n4 100 0.066\n5 100 0.067\n6 100 0.073\n7 100 0.071\n8 100 0.070\n9 100 0.071\n10 100 0.063\n\n\nAs you can see, the power estimates show some variance, ranging from 0.063 to 0.073. This can be formalized as the Monte-Carlo error (MCE), which is define as “the standard deviation of the Monte Carlo estimator, taken across hypothetical repetitions of the simulation” (Koehler et al., 2009). With 1000 iterations (and 10 repetitions), this is:\n\nsd(result$power) |> round(4)\n\n[1] 0.0031\n\n\nWe only computed 10 repetitions of our power estimate, hence the MCE estimate is quite unstable. In the next computation, we will compute 100 repetitions of each power estimate (all with the same simulated sample size).\nHow much do we have to increase the iterations to achieve a MCE smaller than, say, 0.005 (i.e, an SD of +/- 0.5% of the power estimate)?\nLet’s loop through increasing iterations (this takes a few minutes):\n\niterations <- seq(1000, 6000, by=1000)\n\n# let's have 100 repetitions to get sufficiently stable MCE estimates\nns <- rep(100, 100)\nresult <- data.frame()\n\nfor (it in iterations) {\n\n # print(it) uncomment for showing the progress\n\n for (n in ns) {\n \n x <- cbind(\n rep(1, n),\n c(rep(0, n/2), rep(1, n/2))\n )\n \n p_values <- rep(NA, it)\n \n for (i in 1:it) {\n y <- 23 - 3*x[, 2] + rnorm(n, mean=0, sd=sqrt(117))\n \n mdl <- RcppArmadillo::fastLmPure(x, y)\n pval <- 2*pt(abs(mdl$coefficients/mdl$stderr), mdl$df.residual, lower.tail=FALSE)\n \n p_values[i] <- pval[2]\n }\n \n result <- rbind(result, data.frame(iterations = it, n = n, power = sum(p_values < .005)/it))\n }\n}\n\n# We can compute the exact power with the analytical solution:\nexact_power <- pwr.t.test(d = 3 / sqrt(117), sig.level = 0.005, n = 50)\n\np1 <- ggplot(result, aes(x=iterations, y=power)) + stat_summary(fun.data=mean_cl_normal) + ggtitle(\"Power estimate (error bars = SD)\") + geom_hline(yintercept = exact_power$power, colour = \"blue\", linetype = \"dashed\")\n\np2 <- ggplot(result, aes(x=iterations, y=power)) + stat_summary(fun=\"sd\", geom=\"point\") + ylab(\"MCE\") + ggtitle(\"Monte Carlo Error\")\n\np1/p2\n\n\n\n\nAs you can see, the MCE gets smaller with increasing iterations. The desired precision of MCE <= .005 can be achieved at around 3000 iterations (the dashed blue line is the exact power estimate from the analytical solution). While precision increases quickly by going from 1000 to 2000 iterations, further improvements are costly in terms of computation time. In sum, 3000 iterations seems to be a good compromise for this specific power simulation.\n\n\n\n\n\n\nNote\n\n\n\nThis choice of 3000 iterations does not necessarily generalize to other power simulations with other statistical models. But in my experience, 2000 iterations typically is a good (enough) choice. I often start with 500 iterations when exploring the parameter space (i.e., looking roughly for the range of reasonable sample sizes), and then “zoom” into this range with 2000 iterations.\n\n\nIn the lower plot, you can also see that the MCE estimate itself is a bit wiggly - we would expect a smooth curve. It suffers from meta-MCE! We could increase the precision of the MCE estimate by increasing the number of repetitions (currently at 100)." - }, - { - "objectID": "index.html", - "href": "index.html", - "title": "Simulations for Advanced Power Analyses", - "section": "", - "text": "This tutorial was created by Felix Schönbrodt and Moritz Fischer, with contributions from Malika Ihle, to be part of the training offering of the Ludwig-Maximilian University Open Science Center in Munich.\n\n\n\n\n\n\nNote\n\n\n\nThis book is still being developed. If you have comment to contribute to its improvement, you can submit pull requests in the respective .qmd file of the source repository by clicking on the ‘Edit this page’ and ‘Report an issue’ in the right navigation panel of each page." - }, - { - "objectID": "index.html#structure-of-the-tutorial", - "href": "index.html#structure-of-the-tutorial", - "title": "Simulations for Advanced Power Analyses", - "section": "Structure of the tutorial", - "text": "Structure of the tutorial\nDepending on your prior knowledge, you can fast forward some steps:\n\n\nAcquire necessary basic coding skills in R\nYou need to know R programming basics. If you are unfamiliar with R, you are advised to follow a self-paced basic tutorial prior to the workshop, e.g.: https://www.tutorialspoint.com/r up to “data reshaping” (this tutorial, for example, takes around 2h and covers all necessary basics).\nFor a higher-level introduction to R coding skills you can do the self-paced tutorial Introduction to simulation in R. This tutorial teaches how to simulate data and writing functions in R, with the goal to e.g.\n\ncheck alpha so your statistical models don’t yield more than 5% false-positive results\ncheck beta (power) for easy tests such as t-tests\nprepare a preregistration and make sure your code works\ncheck your understanding of statistics.\n\n\n\nComprehensive introduction to power analyses\nPlease read Chapter 1 of the SuperpowerBook by Aaron R. Caldwell, Daniël Lakens, Chelsea M. Parlett-Pelleriti, Guy Prochilo, Frederik Aust.\nThis introduction covers sample effect sizes vs population effect sizes, how to take into account the uncertainty of the sample effect size to create a safeguard effect size to be used in power analyses, why post hoc power analyses are pointless, and why it is better to calculate the minimal detectable effect instead.\nThe rest of the Superpower book teaches how to use the superpower R package to simulate factorial designs and calculate power, which may be of great interest to you! In our tutorial, we chose to teach how to write simulation ‘by hand’ so you can understand the concept and adapt it to any of your designs.\n\n\nTutorial structure\nWith these prerequisites, you can start to learn power calculations for different complex models. Here are the type of models we will cover, you can pick and choose what is relevant to you:\n\nCh. 1: Linear Model I: a single dichotomous predictor\n\nCh. 2: Linear Model 2: Multiple predictors\n\nCh. 3: Generalized Linear Models\n\nCh. 4: Linear Mixed Models\n\nCh. 5: Structural Equation Modelling (SEM)\n\nWe recommend that everybody works through chapters 1 and 2, and then dive into the other chapters that are relevant.\nFor each model, we will follow the structure:\n\ndefine what type of data and variables need to be simulated, i.e. their distribution, their class (e.g. factor vs numerical value)\ngenerate data based on the equation of the model (data = model + error)\nrun the statistical test, and record the relevant statistic (e.g. p-value)\nreplicate step 2 and 3 to get the distribution of the statistic of interest (e.g. p-value)\nanalyze and interpret the combined results of many simulations i.e. check for which sample size you get at a significant result in 80% of the simulations\n\n\n\nInstall all packages\nThe following packages are necessary to reproduce the output of this tutorial. We recommend installing all of them before you dive into the individual chapters.\n\ninstall.packages(c(\n \"ggplot2\", \n \"ggdist\", \n \"gghalves\", \n \"pwr\", \n \"MBESS\", \n \"Rfast\", \n \"DescTools\", \n \"lme4\", \n \"lmerTest\", \n \"tidyr\", \n \"Rfast\", \n \"future.apply\", \n \"lavaan\", \n \"MASS\"), dependencies = TRUE, repos = \"https://cran.rstudio.com/\")\n\ninstall.packages(\"devtools\")\ndevtools::install_github(\"debruine/faux\")" - }, - { - "objectID": "index.html#license-funding-note", - "href": "index.html#license-funding-note", - "title": "Simulations for Advanced Power Analyses", - "section": "License & Funding note", - "text": "License & Funding note\nThis tutorial was initially commissioned and funded by the University of Hamburg, Faculty of Psychology and Movement Science.\nIt is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License." - }, - { - "objectID": "LM1.html", - "href": "LM1.html", - "title": "Ch. 1: Linear Model 1: A single dichotomous predictor", - "section": "", - "text": "Reading/working time: ~50 min.\nWe start with the simplest possible linear model: (a) a continuous outcome variable is predicted by a single dichotomous predictor. This model actually rephrases a t-test as a linear model! Then we build up increasingly complex models: (b) a single continuous predictor and (c) multiple continuous predictors (i.e., multiple regression)." - }, - { - "objectID": "LM1.html#get-some-real-data-as-starting-point", - "href": "LM1.html#get-some-real-data-as-starting-point", - "title": "Ch. 1: Linear Model 1: A single dichotomous predictor", - "section": "Get some real data as starting point", - "text": "Get some real data as starting point\n\n\n\n\n\n\nNote\n\n\n\nThe creators of this tutorial are no experts in clinical psychology; we opportunistically selected open data sets based on their availability. Usually, we would look for meta-analyses - ideally bias-corrected - for more comprehensive evidence.\n\n\nThe R package HSAUR contains open data on 100 depressive patients, where 50 received treatment-as-usual (TAU) and 50 received a new treatment (“Beat the blues”; BtheB). Data was collected in a pre-post-design with several follow-up measurements. For the moment, we focus on the pre-treatment baseline value (bdi.pre) and the first post-treatment value (bdi.2m). We will use that data set as a “pilot study” for our power analysis.\nNote that this pilot data does not contain an inactive control group, such as the waiting list group that we assume for our planned study. Both the BtheB and the TAU group are active treatment groups. Nonetheless, we will be able to infer our treatment effect (vs. an inactive control group) from that data by looking at the pre-treatment data.\n\n# the data can be found in the HSAUR package, must be installed first\n#install.packages(\"HSAUR\")\n\n# load the data\ndata(\"BtheB\", package = \"HSAUR\")\n\n# get some information about the data set:\n?HSAUR::BtheB\n\nhist(BtheB$bdi.pre)\n\n\n\n\nThe standardized cutoffs for the BDI are:\n\n0–13: minimal depression\n14–19: mild depression\n20–28: moderate depression\n29–63: severe depression.\n\nReturning to our questions from above:\nWhat BDI values would we expect on average in our sample before treatment?\n\n# we take the pre-score here:\nmean(BtheB$bdi.pre)\n\n[1] 23.33\n\n\nThe average BDI score before treatment was 23, corresponding to a “moderate depression”.\n\nWhat variability would we expect in our sample?\n\n\nvar(BtheB$bdi.pre)\n\n[1] 117.5163\n\n\n\nWhat average treatment effect would we expect?\n\n\n# we take the 2 month follow-up measurement, \n# separately for the \"treatment as usual\" and \n# the \"Beat the blues\" group:\nmean(BtheB$bdi.2m[BtheB$treatment == \"TAU\"], na.rm=TRUE)\n\n[1] 19.46667\n\nmean(BtheB$bdi.2m[BtheB$treatment == \"BtheB\"])\n\n[1] 14.71154\n\n\nHence, the two treatments reduced BDI scores from an average of 23 to 19 (TAU) and 15 (BtheB). Based on that data set, we can conclude that a typical treatment effect is somewhere between a 4 and a 8-point reduction of BDI scores.1\nFor our purpose, we compute the average treatment effect combined for both treatments. The average post-treatment score is:\n\nmean(BtheB$bdi.2m, na.rm=TRUE)\n\n[1] 16.91753\n\n\nSo, the average reduction across both treatments is 23-17=6. In the following scripts, we’ll use that value as our assumed treatment effect." - }, - { - "objectID": "LM1.html#enter-specific-values-for-the-model-parameters", - "href": "LM1.html#enter-specific-values-for-the-model-parameters", - "title": "Ch. 1: Linear Model 1: A single dichotomous predictor", - "section": "Enter specific values for the model parameters", - "text": "Enter specific values for the model parameters\nLet’s rewrite the abstract equation with the specific variable names. We first write the equation for the systematic part (without the error term). This also represents the predicted value:\n\\widehat{\\text{BDI}} = b_0 + b_1*\\text{treatment}\nWe use the notation \\widehat{\\text{BDI}} (with a hat) to denote the predicted BDI score.\nThe predicted score for the control group then simply is the intercept of the model, as the second term is erased by entering the value “0” for the control group:\n\\widehat{\\text{BDI}} = b_0 + b_1*0 = b_0\nThe predicted score for the treatment group is the value for the control group plus the regression weight:\n\\widehat{\\text{BDI}} = b_0 + b_1*1 Hence, the regression weight (aka. “slope parameter”) b_1 estimates the mean difference between both groups, which is the treatment effect.\nWith our knowledge from the open BDI data, we insert plausible values for the intercept b_0 and the treatment effect b_1. We expect a reduction of the depression score, so the treatment effect is assumed to be negative. We take the combined treatment effect of the two pilot treatments. And as power analysis is not rocket science, we generously round the values:\n\\widehat{\\text{BDI}} = 23 - 6*treatment\nHence, the predicted value is 23 - 6*0 = 23 for the control group, and 23 - 6*1 = 17 for the treatment group.\nWith the current model, all participants in the control group have the same predicted value (23), as do all participants in the treatment group (17).\nAs a final step, we add the random noise to the model, based on the variance in the pilot data:\n\\text{BDI} = 23 - 6*treatment + e; e \\sim N(0, var=117) \nThat’s our final equation with assumed population parameters! With that equation, we assume a certain state of reality and can sample “virtual participants”." - }, - { - "objectID": "LM1.html#what-is-the-effect-size-in-the-model", - "href": "LM1.html#what-is-the-effect-size-in-the-model", - "title": "Ch. 1: Linear Model 1: A single dichotomous predictor", - "section": "What is the effect size in the model?", - "text": "What is the effect size in the model?\nResearchers often have been trained to think in standardized effect sizes, such as Cohen’s d, a correlation r, or other indices such as f^2 or partial \\eta^2. In the simulation approach, we typical work on the raw scale of variables.\nThe raw effect size is simply the treatment effect on the original BDI scale (i.e., the group difference in the outcome variable). In our case we assume that the treatment lowers the BDI score by 6 points, on average. Defining the raw effect requires some domain knowledge - you need to know your measurement scale, and you need to know what the values (and differences between values) mean. In our example, a reduction of 6 BDI points means that the average patient moves from a moderate depression (23 points) to a mild depression (17 points). Working with raw effect sizes forces you to think about your actual data (instead of plugging in content-free default standardized effect sizes), and enables you to do plausibility checks on your simulation.\nThe standardized effect size relates the raw effect size to the unexplained error variance. In the two-group example, this can be expressed as Cohen’s d, which is the mean difference divided by the standard deviation (SD):\nd = \\frac{M_{treat} - M_{control}}{SD} = \\frac{17 - 23}{\\sqrt{117}} = -0.55\nThe standardized effect size always relates two components: The raw effect size (here: 6 points difference) and the error variance. Hence, you can increase the standardized effect size by (a) increasing the raw treatment effect, or (b) reducing the error variance.\n\n\n\n\n\n\nNote\n\n\n\nIf you look up the formula of Cohen’s d, it typically uses the pooled SD from both groups. As we assumed that both groups have the same SD, we simply took that value." - }, - { - "objectID": "LM1.html#doing-the-power-analysis", - "href": "LM1.html#doing-the-power-analysis", - "title": "Ch. 1: Linear Model 1: A single dichotomous predictor", - "section": "Doing the power analysis", - "text": "Doing the power analysis\nNow we need to repeatedly draw many samples and see how many of the analyses would have detected the existing effect. To do this, we put the code from above into a function called sim. We coded the function to either return the focal p-value (as default) or to print a model summary (helpful for debugging and testing the function). This function takes two parameters:\n\nn defines the required sample size\ntreatment_effect defines the treatment effect in the raw scale (i.e., reduction in BDI points)\n\nWe then use the replicate function to repeatedly call the sim function for 1000 iterations.\n\nset.seed(0xBEEF)\n\niterations <- 1000 # the number of Monte Carlo repetitions\nn <- 100 # the size of our simulated sample\n\nsim1 <- function(n=100, treatment_effect=-6, print=FALSE) {\n treatment <- c(rep(0, n/2), rep(1, n/2))\n BDI <- 23 + treatment_effect*treatment + rnorm(n, mean=0, sd=sqrt(117))\n \n # this lm() call should be exactly the function that you use\n # to analyse your real data set\n res <- lm(BDI ~ treatment)\n p_value <- summary(res)$coefficients[\"treatment\", \"Pr(>|t|)\"]\n \n if (print==TRUE) print(summary(res))\n else return(p_value)\n}\n\n# now run the sim() function a 1000 times and store the p-values in a vector:\np_values <- replicate(iterations, sim1(n=100))\n\nHow many of our 1000 virtual samples would have found the effect?\n\ntable(p_values < .005)\n\n\nFALSE TRUE \n 535 465 \n\n\nOnly 46% of samples with the same size of n=100 result in a significant p-value.\n46% - that is our power for \\alpha = .005, Cohen’s d=.55, and n=100." - }, - { - "objectID": "LM1.html#sample-size-planning-find-the-necessary-sample-size", - "href": "LM1.html#sample-size-planning-find-the-necessary-sample-size", - "title": "Ch. 1: Linear Model 1: A single dichotomous predictor", - "section": "Sample size planning: Find the necessary sample size", - "text": "Sample size planning: Find the necessary sample size\nNow we know that a sample size of 100 does not lead to a sufficient power. But what sample size would we need to achieve a power of at least 80%? In the simulation approach you need to test different ns until you find the necessary sample size. We do this by wrapping the simulation code into a loop that continuously increases the n. We then store the computed power for each n.\n\nset.seed(0xBEEF)\n\n# define all predictor and simulation variables.\niterations <- 1000\nns <- seq(100, 300, by=20) # test ns between 100 and 300\n\nresult <- data.frame()\n\nfor (n in ns) { # loop through elements of the vector \"ns\"\n p_values <- replicate(iterations, sim1(n=n))\n \n result <- rbind(result, data.frame(\n n = n,\n power = sum(p_values < .005)/iterations)\n )\n \n # show the result after each run (not shown here in the tutorial)\n print(result)\n}\n\nLet’s plot there result:\n\nggplot(result, aes(x=n, y=power)) + geom_point() + geom_line()\n\n\n\n\nHence, with n=180 (90 in each group), we have a 80% chance to detect the effect.\n🥳 Congratulations! You did your first power analysis by simulation. 🎉\nFor these simple models, we can also compute analytic solutions. Let’s verify our results with the pwr package - a linear regression with a single dichotomous predictor is equivalent to a t-test:\n\npwr.t.test(d = 0.55, sig.level = 0.005, power = .80)\n\n\n Two-sample t test power calculation \n\n n = 90.00212\n d = 0.55\n sig.level = 0.005\n power = 0.8\n alternative = two.sided\n\nNOTE: n is number in *each* group\n\n\nExactly the same result - phew 😅" - }, - { - "objectID": "LM1.html#safeguard-power-analysis", - "href": "LM1.html#safeguard-power-analysis", - "title": "Ch. 1: Linear Model 1: A single dichotomous predictor", - "section": "Safeguard power analysis", - "text": "Safeguard power analysis\nAs sensitivity analysis, we will apply a safeguard power analysis (Perugini et al., 2014) that aims for the lower end of a two-sided 60% CI around the parameter of the treatment effect (the intercept is irrelevant). (Of course you can use any other value than 60%, but this is the value (tentatively) mentioned by the inventors of the safeguard power analysis.)\n\n\n\n\n\n\nNote\n\n\n\nIf you assume publication bias, another heuristic for aiming at a more realistic population effect size is the “divide-by-2” heuristic. (see kickoff presentation)\n\n\nWe can use the ci.smd function from the MBESS package to compute a CI around Cohen’s d that we computed for our treatment effect:\n\nci.smd(smd=-0.55, n.1=50, n.2=50, conf.level=.60)\n\n$Lower.Conf.Limit.smd\n[1] -0.7201263\n\n$smd\n[1] -0.55\n\n$Upper.Conf.Limit.smd\n[1] -0.377061\n\n\nHowever, in the simulated regression equation, we need the raw effect size - so we have to backtransform the standardized confidence limits into the original metric. As the assumed effect is negative, we aim for the upper, i.e., the more conservative limit. After backtransformation in the raw metric, it is considerably smaller, at -4.1:\nd = \\frac{M_{diff}}{SD} \\Rightarrow M_{diff} = d*SD = -0.377 * \\sqrt{117} = -4.08\nNow we can rerun the power simulation with this more conservative value (the only change to the code above is that we changed the treatment effect from -6 to -4.1).\n\n\nShow the code\nset.seed(0xBEEF)\n\n# define all predictor and simulation variables.\niterations <- 1000\nns <- seq(200, 400, by=20) # test ns between 200 and 400\n\nresult <- data.frame()\n\nfor (n in ns) { # loop through elements of the vector \"ns\"\n p_values <- replicate(iterations, sim1(n=n, treatment_effect = -4.1))\n \n result <- rbind(result, data.frame(\n n = n,\n power = sum(p_values < .005)/iterations)\n )\n \n # show the result after each run\n print(result)\n}\n\n\n\n\n n power\n1 200 0.448\n2 220 0.466\n3 240 0.549\n4 260 0.584\n5 280 0.646\n6 300 0.664\n7 320 0.708\n8 340 0.744\n9 360 0.790\n10 380 0.813\n11 400 0.822\n\n\nWith that more conservative effect size assumption, we would need around 380 participants, i.e. 190 per group." - }, - { - "objectID": "LM1.html#smallest-effect-size-of-interest-sesoi", - "href": "LM1.html#smallest-effect-size-of-interest-sesoi", - "title": "Ch. 1: Linear Model 1: A single dichotomous predictor", - "section": "Smallest effect size of interest (SESOI)", - "text": "Smallest effect size of interest (SESOI)\nMany methodologists argue that we should not power for the expected effect size, but rather for the smallest effect size of interest (SESOI). In this case, a non-significant result can be interpreted as “We accept the H_0, and even if a real effect existed, it most likely is too small to be relevant”.\nWhat change of BDI scores is perceived as “clinically important”? The hard part is to find a convincing theoretical or empirical argument for the chosen SESOI. In the case of the BDI, luckily someone else did that work.\nThe NICE guidance suggest that a change of >=3 BDI-II points is clinically important.\nHowever, as you can expect, things are more complicated. Button et al. (2015) analyzed data sets where patients have been asked, after a treatment, whether they felt “better”, “the same” or “worse”. With these subjective ratings, they could relate changes in BDI-II scores to perceived improvements. Hence, even when depressive symptoms were measurably reduced in the BDI, patients still might answer “feels the same”, which indicates that the reduction did not surpass a threshold of subjective relevant improvement. But the minimal clinical importance depends on the baseline severity: For patients to feel notably better, they need more reduction of BDI-II scores if they start from a higher level of depressive symptoms. Following from this analysis, typical SESOIs are higher than the NICE guidelines, more in the range of -6 BDI points.\nFor our example, let’s use the NICE recommendation of -3 BDI points as a lower threshold for our power analysis (anything larger than that will be covered anyway).\n\n\nShow the code\nset.seed(0xBEEF)\n\n# define all predictor and simulation variables.\niterations <- 1000\n\n# CHANGE: we adjusted the range of probed sample sizes upwards, as the effect size now is considerably smaller\nns <- seq(600, 800, by=20)\n\nresult <- data.frame()\n\nfor (n in ns) {\n p_values <- replicate(iterations, sim1(n=n, treatment_effect = -3))\n \n result <- rbind(result, data.frame(\n n = n,\n power = sum(p_values < .005)/iterations)\n )\n\n print(result)\n}\n\n\n\n\n n power\n1 600 0.728\n2 620 0.738\n3 640 0.756\n4 660 0.781\n5 680 0.791\n6 700 0.791\n7 720 0.827\n8 740 0.820\n9 760 0.852\n10 780 0.866\n11 800 0.871\n\n\nHence, we need around 700 participants to reliably detect this smallest effect size of interest.\nDid you spot the strange pattern in the result? At n=720, the power is 83%, but only 82% with n=740? This is not possible, as power monotonically increases with sample size. It suggests that this is simply Monte Carlo sampling error - 1000 iterations are not enough to get precise estimates. When we increase iterations to 10,000, it takes much longer, but gives more precise results:\n\n\nShow the code\nset.seed(0xBEEF)\n\n# define all predictor and simulation variables.\niterations <- 10000\nns <- seq(640, 740, by=20)\n\nresult <- data.frame()\n\nfor (n in ns) {\n p_values <- replicate(iterations, sim1(n=n, treatment_effect = -3))\n \n result <- rbind(result, data.frame(\n n = n,\n power = sum(p_values < .005)/iterations)\n )\n\n print(result)\n}\n\n\n\n\n n power\n1 640 0.7583\n2 660 0.7758\n3 680 0.7865\n4 700 0.8060\n5 720 0.8192\n6 740 0.8273\n\n\nNow power increases monotonically with sample size, as expected.\nTo explore how many Monte-Carlo iterations are necessary to get stable computational results, see the bonus page Bonus: How many Monte-Carlo iterations are necessary?" - }, - { - "objectID": "LM2.html", - "href": "LM2.html", - "title": "Ch. 2: Linear Model 2: Multiple predictors", - "section": "", - "text": "Reading/working time: ~50 min.\nIn the first chapter on linear models, we had the simplest possible linear model: a continuous outcome variable is predicted by a single dichotomous predictor. In this chapter, we build up increasingly complex models by (a) adding a single continuous predictor and (b) modeling an interaction." - }, - { - "objectID": "LM2.html#get-some-real-data-as-starting-point", - "href": "LM2.html#get-some-real-data-as-starting-point", - "title": "Ch. 2: Linear Model 2: Multiple predictors", - "section": "Get some real data as starting point", - "text": "Get some real data as starting point\nInstead of guessing the necessary quantities - in the current case, the pre-post-correlation - let’s look at real data. The “Beat the blues” (BtheB) data set from the HSAUR R package contains pre-treatment baseline values (bdi.pre), along with multiple post-treatment values. Here we focus on the first post-treatment assessment, 2 months after the treatment (bdi.2m).\n\n# load the data\ndata(\"BtheB\", package = \"HSAUR\")\n\n# pre-post-correlation\ncor(BtheB$bdi.pre, BtheB$bdi.2m, use=\"p\")\n\n[1] 0.6142207\n\n\nIn our pilot data set, the pre-post-correlation is around r=.6. We will use this value in our simulations." - }, - { - "objectID": "LM2.html#update-the-sim-function", - "href": "LM2.html#update-the-sim-function", - "title": "Ch. 2: Linear Model 2: Multiple predictors", - "section": "Update the sim() function", - "text": "Update the sim() function\nNow we add the continuous predictor into our sim() function. In order to simulate a correlated variable, we need to slightly change the workflow in our simulation.\nWe use the rmvnorm function from the Rfast package to create correlated, normally distributed variables. It takes three parameters:\n\nn: The number of random observations\nmu: A vector of mean values (one for each random variable)\nsigma: The variance-covariance-matrix of the random variables.\n\nFor mu, we use the value 23 for both the pre and the post value. You can imagine that we only look at the control group: The mean value is not supposed to change systematically from pre to post (although each individual person can go somewhat up or down).\nThe variance-covariance-matrix defines two properties at once: The variance of each variable, and the covariance to all other variables. In the two-variable case, the general matrix looks like:\n\n\\begin{bmatrix}\nvar_x & cov_{xy}\\\\\ncov_{xy} & var_y\n\\end{bmatrix}\n\nNote that the covariance in the diagonal has the same values, as cov_{xy} = cov_{yx}. As we need to enter the covariance into sigma, we need to convert the correlation into a covariance. Here’s the formula:\ncor_{xy} = \\frac{cov_{xy}}{\\sigma_x\\sigma_y}\n(Note: The denominator contains the standard deviation, not the variance.) Solved for the covariance yields:\ncov_{xy} = cor_{xy}\\sigma_X\\sigma_y = 0.6 * \\sqrt{117} * \\sqrt{117} = 70.2\nHence, the specific variance-covariance-matrix sigma is in our case (generously rounded):\n\n\\begin{bmatrix}\n117 & 70\\\\\n70 & 117\n\\end{bmatrix}\n\nPut it all together:\n\nset.seed(0xBEEF)\nmu <- c(23, 23) # the mean values of both variables\nsigma <- matrix(\n c(117 , 70, \n 70, 117), nrow=2, byrow=TRUE)\ndf <- rmvnorm(n=10000, mu=mu, sigma=sigma) |> data.frame()\nnames(df) <- c(\"BDI_pre\", \"BDI_post0\")\n\n# Check: in a large sample the correlation should be close to .6\ncor(df)\n\n BDI_pre BDI_post0\nBDI_pre 1.0000000 0.5978088\nBDI_post0 0.5978088 1.0000000\n\n\n\n\n\n\n\n\nNote\n\n\n\nA very nice and user-friendly alternative for simulating correlated variables is the rnorm_multi function from the faux package.\n\n\nWe now have the correlated BDI_{pre} and BDI_{post} scores. Finally, we have to impose the treatment effect onto the post variable: In the control group, the mean value stays constant at 23 (what we already have simulated), in the treatment group, the BDI is 6 points lower:\n\n# add treatment predictor variables\ndf$treatment <- rep(c(0, 1), times=nrow(df)/2)\n \n# add the treatment effect to the BDI_post0 variable\ndf$BDI_post <- df$BDI_post0 + -6*df$treatment\n\nWe do not need to add an explicit intercept, as this is already encoded in the mean value of the simulated variables. (Alternatively, we could have simulated them centered on zero and then explicitly add the intercept in the model equation).\nLet’s make a plausibility check by plotting the simulated variables. We first have to convert them into long format for a nicer plot:\n\n# reduce to 100 cases for plotting; define factors\ndf2 <- df[1:400, ]\ndf2$id <- 1:nrow(df2)\ndf2$BDI_post0 <- NULL\n\ndf_long <- pivot_longer(df2, cols=c(BDI_pre, BDI_post), names_to=\"time\")\ndf_long$time <- factor(df_long$time, levels=c(\"BDI_pre\", \"BDI_post\"))\ndf_long$treatment <- factor(df_long$treatment, levels=c(0, 1), labels=c(\"Control\", \"Treatment\"))\n\nggplot(df_long, aes(x=time, y=value, group=id)) + geom_point() + geom_line() + facet_wrap(~treatment)\n\n\n\nggplot(df_long, aes(x=time, y=value, group=time)) + geom_boxplot() + facet_wrap(~treatment)\n\n\n\n\nLooks good - reasonable BDI values, same means in control group, same variance. The treatment effect of -6 is visible.\nLet’s put that together in the new sim function:\n\nsim2 <- function(n=100, treatment_effect=-6, pre_post_cor = 0.6, err_var = 117, print=FALSE) {\n library(Rfast)\n \n # Here we simulate correlated BDI pre and post scores\n mu <- c(23, 23) # the mean values of both variables\n sigma <- matrix(\n c(err_var, pre_post_cor*sqrt(err_var)*sqrt(err_var), \n pre_post_cor*sqrt(err_var)*sqrt(err_var), err_var), \n nrow=2, byrow=TRUE)\n df <- rmvnorm(n, mu, sigma) |> data.frame()\n names(df) <- c(\"BDI_pre\", \"BDI_post0\")\n \n # add treatment predictor variables\n df$treatment <- c(rep(0, n/2), rep(1, n/2))\n \n # center the BDI_pre value for better interpretability\n df$BDI_pre.c <- df$BDI_pre - mean(df$BDI_pre)\n \n # add the treatment effect to the BDI_post0 variable\n # We do not need to add an intercept, as this is already encoded\n # in the mean value of the simulated variables.\n df$BDI_post <- df$BDI_post0 + df$treatment*treatment_effect\n \n # fit the model\n res <- lm(BDI_post ~ BDI_pre.c + treatment, data=df)\n summary(res)\n p_value <- summary(res)$coefficients[\"treatment\", \"Pr(>|t|)\"]\n \n if (print==TRUE) print(summary(res))\n else return(p_value) \n}\n\nLet’s test the plausibility of the new sim2() function by simulating a very large sample - this should give estimates close to the true values. We also vary the assumed pre-post-correlation. When this is 0, the results should be identical to the simpler model from Chapter 1 (except one df that we lost due to the additional predictor). When the pre-post-correlation is > 0, the error variance should be reduced (see Residual standard error at the bottom of each lm output).\n\nset.seed(0xBEEF)\n\n# without pre_post_cor, the result should be the same\nsim2(n=100000, pre_post_cor=0, print=TRUE)\n\n\nCall:\nlm(formula = BDI_post ~ BDI_pre.c + treatment, data = df)\n\nResiduals:\n Min 1Q Median 3Q Max \n-50.580 -7.401 0.021 7.381 47.636 \n\nCoefficients:\n Estimate Std. Error t value Pr(>|t|) \n(Intercept) 23.039486 0.048743 472.676 <2e-16 ***\nBDI_pre.c -0.002105 0.003178 -0.662 0.508 \ntreatment -6.091742 0.068933 -88.372 <2e-16 ***\n---\nSignif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n\nResidual standard error: 10.9 on 99997 degrees of freedom\nMultiple R-squared: 0.07244, Adjusted R-squared: 0.07242 \nF-statistic: 3905 on 2 and 99997 DF, p-value: < 2.2e-16\n\n# with pre_post_cor, the residual error should be reduced\nsim2(n=100000, pre_post_cor=0.6, print=TRUE)\n\n\nCall:\nlm(formula = BDI_post ~ BDI_pre.c + treatment, data = df)\n\nResiduals:\n Min 1Q Median 3Q Max \n-35.915 -5.846 0.005 5.807 35.457 \n\nCoefficients:\n Estimate Std. Error t value Pr(>|t|) \n(Intercept) 22.942640 0.038783 591.6 <2e-16 ***\nBDI_pre.c 0.597809 0.002538 235.5 <2e-16 ***\ntreatment -5.956531 0.054847 -108.6 <2e-16 ***\n---\nSignif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n\nResidual standard error: 8.672 on 99997 degrees of freedom\nMultiple R-squared: 0.4017, Adjusted R-squared: 0.4017 \nF-statistic: 3.357e+04 on 2 and 99997 DF, p-value: < 2.2e-16\n\n\nIndeed, with pre_post_cor = 0 the parameter estimates are identical, and with non-zero pre-post-correlation the error term gets reduced.\n\n\n\n\n\n\nAn additional plausibility check (advanced)\n\n\n\n\n\nWe assume independence of both predictor variables (BDI_pre and treatment). This is plausible, because the treatment was randomized and therefore independent from the baseline. In this case, the variance that each predictor explains in the dependent variable is additive. The pre-measurement explains r^2 = .6^2 = 36\\% of the variance in the post-measurement. As this variance is unrelated to the treatment factor, it reduces the variance of the error term (which was at 117) by 36%:\n\\sigma^2_{err} = 117 * (1-0.36) = 74.88\nThe square root of this unexplained error variance is the Residual standard error from the lm output: \\sqrt{74.88} = 8.65." - }, - { - "objectID": "LM2.html#do-the-power-analysis", - "href": "LM2.html#do-the-power-analysis", - "title": "Ch. 2: Linear Model 2: Multiple predictors", - "section": "Do the power analysis", - "text": "Do the power analysis\nThe next step is the same as always: use the replicate function to repeatedly call the sim function for many iterations, and increase the simulated sample size until the desired power level is achieved.\n\n\nCode\nset.seed(0xBEEF)\n\n# define all predictor and simulation variables.\niterations <- 2000\nns <- seq(90, 180, by=10) # ns are already adjusted to cover the relevant range\n\nresult <- data.frame()\n\nfor (n in ns) { # loop through elements of the vector \"ns\"\n p_values <- replicate(iterations, sim2(n=n, pre_post_cor = 0.6))\n \n result <- rbind(result, data.frame(\n n = n,\n power = sum(p_values < .005)/iterations\n )\n )\n \n # show the result after each run (not shown here in the tutorial)\n print(result)\n}\n\n\n\n\n n power\n1 90 0.6690\n2 100 0.7245\n3 110 0.7705\n4 120 0.8185\n5 130 0.8765\n6 140 0.8890\n7 150 0.9335\n8 160 0.9335\n9 170 0.9465\n10 180 0.9675\n\n\nIn the original analysis, we needed n=180 (90 in each group) for 80% power. Including the baseline covariate (which explains r^2 = .6^2 = 36\\% of the variance in post scores) reduces that number to around n=115.\nLet’s check the plausibility of our power simulation. Borm et al (2007, p. 1237) propose a simple method on how to arrive at a planned sample size when switching from a simple t-test (comparing post-treatment groups) to a model that controls for the baseline:\n\n“We propose a simple method for the sample size calculation when ANCOVA is used: multiply the number of subjects required for the t-test by (1-r^2) and add one extra subject per group.\n\nWhen we enter our assumed pre-post-correlation into that formula, we arrive a n=117 - very close to our value:\n180 * (1 - .6^2) + 2 = 117" - }, - { - "objectID": "LM2.html#thinking-visually", - "href": "LM2.html#thinking-visually", - "title": "Ch. 2: Linear Model 2: Multiple predictors", - "section": "Thinking visually", - "text": "Thinking visually\nArriving at plausible guesses for interaction effects can be tricky. We strongly recommend to visualize your assumed interaction effect in a plot - first drawn by hand: Do you expect a disordinal (i.e., cross-over) interaction, or an ordinal one where the effect is amplified (but not reversed) by the moderating variable?\nLook at a reasonable maximum and minimum of your variables - how large would you expect the effect to be there?\nEvaluate the effect size in subgroups: For example, imagine a group of really severely depressed patients. And then assume that the therapy works exceptionally well for them - what would be a realistic outcome for them? Would you expect them all to be at BDI<10? Probably not. Or would a very good outcome simply be that they move from a “severe depression” to a “moderate depression”? This gives you an estimate of the upper limit of your effect size. After defining upper limits, you should ask: What effect size would be plausible, given your background knowledge of typical effects in your field?\nOnly when you have drawn a reasonable plot by hand (and validated that with colleagues), start to work out the parameter values that you need to enter in the regression equations in order to arrive at the desired interaction plot." - }, - { - "objectID": "LM2.html#do-the-power-analysis-1", - "href": "LM2.html#do-the-power-analysis-1", - "title": "Ch. 2: Linear Model 2: Multiple predictors", - "section": "Do the power analysis", - "text": "Do the power analysis\nNow that we have our desired parameter values, we update our sim() function by:\n\nImposing the interaction effect onto our dependent variable\nAdding the interaction effect to our lm analysis model\nExtracting two p-values: We now want to compute the power both for the treatment main effect and for the interaction term.\n\n\n# CHANGE: Add interaction_effect\nsim3 <- function(n=100, treatment_effect=-6, pre_post_cor = 0.6, \n interaction_effect = -.2, err_var = 117, print=FALSE) {\n library(Rfast)\n mu <- c(23, 23) # the mean values of both variables\n sigma <- matrix(\n c(err_var, pre_post_cor*sqrt(err_var)*sqrt(err_var), \n pre_post_cor*sqrt(err_var)*sqrt(err_var), err_var), \n nrow=2, byrow=TRUE)\n df <- rmvnorm(n, mu, sigma) |> data.frame()\n names(df) <- c(\"BDI_pre\", \"BDI_post0\")\n \n # add treatment predictor variables\n df$treatment <- c(rep(0, n/2), rep(1, n/2))\n \n # center the BDI_pre value for better interpretability\n df$BDI_pre.c <- df$BDI_pre - mean(df$BDI_pre)\n \n # CHANGE: add the treatment main effect and the interaction to the BDI_post variable\n df$BDI_post <- df$BDI_post0 + treatment_effect*df$treatment + interaction_effect*df$treatment*df$BDI_pre.c\n \n # CHANGE: Add interaction effect to analysis model\n res <- lm(BDI_post ~ BDI_pre.c * treatment, data=df)\n \n # CHANGE: extract both focal p-values\n p_values <- summary(res)$coefficients[c(\"treatment\", \"BDI_pre.c:treatment\"), \"Pr(>|t|)\"]\n \n if (print==TRUE) print(summary(res))\n else return(p_values) \n}\n\n\nset.seed(0xBEEF)\n\n# define all predictor and simulation variables.\niterations <- 1000\nns <- seq(100, 400, by=20)\n\nresult <- data.frame()\n\nfor (n in ns) { # loop through elements of the vector \"ns\"\n p_values <- replicate(iterations, sim3(n=n, pre_post_cor = 0.6, interaction_effect = -.2))\n \n # CHANGE: Analyze both sets of p-values separately\n # The p-values for the main effect are stored in the first row,\n # the p-values for the interaction effect are stored in the 2nd row\n \n result <- rbind(result, data.frame(\n n = n,\n power_treatment = sum(p_values[1, ] < .005)/iterations,\n power_interaction = sum(p_values[2, ] < .005)/iterations\n )\n )\n \n # show the result after each run (not shown here in the tutorial)\n print(result)\n}\n\n\n\n n power_treatment power_interaction\n1 100 0.709 0.058\n2 120 0.830 0.069\n3 140 0.900 0.084\n4 160 0.932 0.118\n5 180 0.962 0.122\n6 200 0.991 0.129\n7 220 0.987 0.158\n8 240 0.997 0.210\n9 260 0.996 0.189\n10 280 0.999 0.212\n11 300 0.999 0.262\n12 320 1.000 0.257\n13 340 0.998 0.293\n14 360 1.000 0.338\n15 380 1.000 0.385\n16 400 1.000 0.370\n\n\nWhile the power for detecting the main effect quickly approaches 100%, even 400 participants are by far not enough to detect the interaction effect reliably. (In particular when you recall that we set the assumed interaction effect to the largest plausible value)." - }, - { - "objectID": "LMM.html", - "href": "LMM.html", - "title": "Ch. 4: Linear Mixed Models / Multilevel models", - "section": "", - "text": "Reading/working time: ~60 min.\nIn order to keep this chapter at a reasonable length, we focus on how to simulate the mixed effects/multi-level model and spend less time discussing how to arrive at plausible parameter values. Please read Chapters 1 and 2 for multiple examples where the parameter values are derived from (a) the literature (e.g., bias-corrected meta-analyses), (b) pilot data, or (c) plausibility constraints.\nFurthermore, we’ll use the techniques described in the Chapter “Bonus: Optimizing R code for speed”, otherwise the simulations are too slow. If you wonder about the unknown commands in the code, please read the bonus chapter to learn how to considerably speed up your code!\nWe continue to work with the “Beat the blues” (BtheB) data set from the HSAUR R package. As there are multiple post-treatment measurements (after 2, 3, 6, and 8 months) we can create a nice longitudinal multilevel data set, with measurement points on level 1 (L1) and persons on level 2 (L2). We will work with the lme4 package to run the mixed-effect models.\nFor that, we first have to transform the pilot data set into the long format:\nHere we plot the trajectory of the first 20 participants. As you can see, there are missing values; not all participants have all follow-up values:" - }, - { - "objectID": "LMM.html#the-formulas", - "href": "LMM.html#the-formulas", - "title": "Ch. 4: Linear Mixed Models / Multilevel models", - "section": "The formulas", - "text": "The formulas\n\ni = index for time points\nj = index for persons\n\nLevel 1 equation:\n\n\\text{BDI}_{ij} = \\beta_{0j} + \\beta_{1j} time_{ij} + e_{ij}\n\nLevel 2 equations:\n\n\\beta_{0j} = \\gamma_{00} + u_{0j}\\\\\n\\beta_{1j} = \\gamma_{10}\n\nCombined equation:\n\n\\text{BDI}_{ij} = \\gamma_{00} + \\gamma_{10} time_{ij} + u_{0j} + e_{ij} \\\\\n\\\\\ne_{ij} \\mathop{\\sim}\\limits^{\\mathrm{iid}} N(mean=0, var=\\sigma^2) \\\\\nu_{0j} \\mathop{\\sim}\\limits^{\\mathrm{iid}} N(mean=0, var=\\tau_{00})" - }, - { - "objectID": "LMM.html#analysis-in-pilot-data", - "href": "LMM.html#analysis-in-pilot-data", - "title": "Ch. 4: Linear Mixed Models / Multilevel models", - "section": "Analysis in pilot data", - "text": "Analysis in pilot data\nFirst, let’s run the analysis model in the pilot data. To make the intercept more interpretable, we center it on the first post-measurement by subtracting 2 (i.e., month “2” becomes month “0” etc.).\n\nBtheB_long$time.c <- BtheB_long$time - 2\n\nl0 <- lmer(BDI ~ 1 + time.c + (1|person_id), data=BtheB_long)\nsummary(l0)\n\nLinear mixed model fit by REML. t-tests use Satterthwaite's method [\nlmerModLmerTest]\nFormula: BDI ~ 1 + time.c + (1 | person_id)\n Data: BtheB_long\n\nREML criterion at convergence: 1929.4\n\nScaled residuals: \n Min 1Q Median 3Q Max \n-2.6308 -0.4698 -0.0810 0.3536 3.7142 \n\nRandom effects:\n Groups Name Variance Std.Dev.\n person_id (Intercept) 97.15 9.857 \n Residual 25.48 5.048 \nNumber of obs: 280, groups: person_id, 97\n\nFixed effects:\n Estimate Std. Error df t value Pr(>|t|) \n(Intercept) 16.9691 1.0990 110.4143 15.441 < 2e-16 ***\ntime.c -0.6869 0.1486 192.8764 -4.623 6.91e-06 ***\n---\nSignif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n\nCorrelation of Fixed Effects:\n (Intr)\ntime.c -0.267\n\n\nHence, in the pilot data there is a significant negative trend - with each month, participants have a decrease of 0.7 BDI points. But remember that in this data set all of the participants have been treated (with one of two treatments). For a passive control group we probably would expect no, or at least a much smaller negative trend over time. (There could be spontaneous remissions, but if this effect is assumed to be very strong, there would be no need for psychotherapy).\nThe grand intercept \\gamma_{00} is 17. This is also the average post-treatment value that we assumed in our previous models in Chapters 1 and 2.\nFor the random intercept variance and the residual variance, we take generously rounded estimates from the pilot data: \\tau_{00} = 100 and \\sigma^2 = 25." - }, - { - "objectID": "LMM.html#lets-simulate", - "href": "LMM.html#lets-simulate", - "title": "Ch. 4: Linear Mixed Models / Multilevel models", - "section": "Let’s simulate", - "text": "Let’s simulate\nThe next script shows how to simulate multilevel data with a random intercept. We first create a L2 data set (where each row is once participant). This contains a person id (which is necessary to merge the L1 and the L2 data set) and the random intercepts (i.e., the random deviations of persons from the fixed intercept). In more complex models, this also contains all L2 predictors.\nNext we create a L1 data set, and then merge both into one long format data set. We closely simulate the situation of the pilot data: All participants are treated and show a negative trend.\n\n#------ Setting the model parameters ---------------\n# between-person random intercept variance: var(u_0j) = tau_00\ntau_00 <- 100\n\ngamma_00 <- 17 # the grand (fixed) intercept\ngamma_10 <- -0.7 # the fixed slope for time\nresidual_var <- 25\n\nn_persons <- 100\n\n# Create a L2 (person-level) data set; each row is one person\ndf_L2 <- data.frame(\n person_id = 1:n_persons,\n u_0j = rnorm(n_persons, mean=0, sd=sqrt(tau_00))\n)\n\n# Create a L1 (measurement-point-level) data set; each row is one measurement\ndf_L1 <- data.frame(\n person_id = rep(1:n_persons, each=4), # create 4 rows for each person (as we have 4 measurements) \n time.c = rep(c(0, 2, 4, 6), times=n_persons) \n)\n\n# combine to a long format data set \ndf_long <- merge(df_L1, df_L2, by=\"person_id\")\n \n# compute the DV by writing down the combined equation\ndf_long <- within(df_long, {\n BDI = gamma_00 + gamma_10*time.c + u_0j + rnorm(n_persons*4, mean=0, sd=sqrt(residual_var))\n})\n\nl1 <- lmer(BDI ~ 1 + time.c + (1|person_id), data=df_long)\nsummary(l1)\n\nLinear mixed model fit by REML. t-tests use Satterthwaite's method [\nlmerModLmerTest]\nFormula: BDI ~ 1 + time.c + (1 | person_id)\n Data: df_long\n\nREML criterion at convergence: 2681.8\n\nScaled residuals: \n Min 1Q Median 3Q Max \n-2.87552 -0.58921 -0.04505 0.61462 2.50725 \n\nRandom effects:\n Groups Name Variance Std.Dev.\n person_id (Intercept) 129.04 11.359 \n Residual 21.43 4.629 \nNumber of obs: 400, groups: person_id, 100\n\nFixed effects:\n Estimate Std. Error df t value Pr(>|t|) \n(Intercept) 16.5616 1.2001 113.5218 13.800 < 2e-16 ***\ntime.c -0.7500 0.1035 299.0000 -7.246 3.66e-12 ***\n---\nSignif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n\nCorrelation of Fixed Effects:\n (Intr)\ntime.c -0.259\n\n\nNow we put this data generating script into a function, and run our power analysis:\n\n# Note: This code could be more optimized for speed, but this way is easier to understand\nsim4 <- function(n_persons = 100, tau_00 = 100, gamma_00 = 17, gamma_10 = -0.7, \n residual_var = 25, print=FALSE) {\n\n df_L2 <- data.frame(\n person_id = 1:n_persons,\n u_0j = rnorm(n_persons, mean=0, sd=sqrt(tau_00))\n )\n \n df_L1 <- data.frame(\n person_id = rep(1:n_persons, each=4), # create 4 rows for each person (as we have 4 measurements)\n time.c = rep(c(0, 2, 4, 6), times=n_persons) \n )\n \n # combine to a long format data set \n df_long <- merge(df_L1, df_L2, by=\"person_id\")\n \n # simulate response variable, based on combined equation \n df_long <- within(df_long, {\n BDI = gamma_00 + gamma_10*time.c + u_0j + rnorm(n_persons*4, mean=0, sd=sqrt(residual_var))\n })\n \n # compute p-values\n # these options might speed up code execution a little bit:\n # , control=lmerControl(optimizer=\"bobyqa\", calc.derivs = FALSE)\n l1 <- lmer(BDI ~ 1 + time.c + (1|person_id), data=df_long)\n \n if (print==TRUE) print(summary(l1))\n else return(summary(l1)$coefficients[, \"Pr(>|t|)\"])\n}\n\n\nset.seed(0xBEEF)\niterations <- 1000\nns <- seq(30, 50, by=5)\n\nresult <- data.frame()\n\nfor (n_persons in ns) {\n system.time({\n p_values <- future_replicate(iterations, sim4(n_persons=n_persons), future.seed = TRUE)\n })\n \n result <- rbind(result, data.frame(\n n = n_persons,\n power_intercept = sum(p_values[1, ] < .005)/iterations,\n power_time.c = sum(p_values[2, ] < .005)/iterations\n )\n )\n \n # show the result after each run (not shown here in the tutorial)\n print(result)\n}\n\n\n\n n power_intercept power_time.c\n1 30 1 0.700\n2 35 1 0.786\n3 40 1 0.874\n4 45 1 0.912\n5 50 1 0.937\n\n\nHence, we need around 36 persons to achieve a power of 80% to detect this slope." - }, - { - "objectID": "LMM.html#comparison-with-other-approaches", - "href": "LMM.html#comparison-with-other-approaches", - "title": "Ch. 4: Linear Mixed Models / Multilevel models", - "section": "Comparison with other approaches", - "text": "Comparison with other approaches\nMurayama and colleagues (2022) recently released an approximate but easy approach for LMM power analysis based on pilot data. They also provide an interactive Shiny app.\nGo to the tab “Level-1 predictor -> Planning L2 sample size only” and enter the values from the pilot study into the fields in the left panel:\n\n(Absolute) t value of the focal level-1 predictor = 4.623\nLevel-2 sample size = 97\nNumber of cross-level interactions related to the focal level-1 predictor = 0\n\nThis approximation yields very close values to our simulation:\n\n\n\nScreenshot from Murayama app\n\n\nAnother option is the app “PowerAnalysisIL” by Lafit (2021). In that app you would need to choose “Model 4: Effect of a level-1 continuous predictor (fixed slope)” and set “Autocorrelation of level-1 errors” to zero to estimate an equivalent model to ours. This app, however, is much slower than our own simulations, and cannot perfectly mirror our model, as the continuous L1 predictor cannot be defined exactly as we need it.\nNonetheless, the estimates are not far away from ours:\n\n\n\nScreenshot from Lafit app" - }, - { - "objectID": "LMM.html#the-formulas-1", - "href": "LMM.html#the-formulas-1", - "title": "Ch. 4: Linear Mixed Models / Multilevel models", - "section": "The formulas", - "text": "The formulas\n\ni = index for time points\nj = index for persons\n\nLevel 1 equation:\n\n\\text{BDI}_{ij} = \\beta_{0j} + \\beta_{1j} time_{ij} + e_{ij}\n\nLevel 2 equations:\n\n\\beta_{0j} = \\gamma_{00} + \\gamma_{01} \\text{treatment}_j + u_{0j}\\\\\n\\beta_{1j} = \\gamma_{10} + \\gamma_{11} \\text{treatment}_j + u_{1j}\n\nCombined equation:\n\n\\text{BDI}_{ij} = \\gamma_{00} + \\gamma_{01} \\text{treatment}_j + \\gamma_{10} time_{ij} + \\gamma_{11} \\text{treatment}_j time_{ij} + u_{1j} time_{ij} + u_{0j} + e_{ij} \\\\\n\\\\\ne_{ij} \\mathop{\\sim}\\limits^{\\mathrm{iid}} N(mean=0, var=\\sigma^2) \\\\\nu_{0j} \\mathop{\\sim}\\limits^{\\mathrm{iid}} N(mean=0, var=\\tau_{00}) \\\\\nu_{1j} \\mathop{\\sim}\\limits^{\\mathrm{iid}} N(mean=0, var=\\tau_{11}) \\\\\ncov(u_{0j}, u_{1j}) = \\tau_{01}" - }, - { - "objectID": "LMM.html#setting-the-population-parameter-values", - "href": "LMM.html#setting-the-population-parameter-values", - "title": "Ch. 4: Linear Mixed Models / Multilevel models", - "section": "Setting the population parameter values", - "text": "Setting the population parameter values\nWe need to assume four additional population values, namely: \\tau_{11} (the variance of random slopes), \\tau_{01} (the intercept-slope-covariance), \\gamma_{01} (the treatment main effect), and \\gamma_{11} (the interaction effect).\nFixed effects:\n\nThe grand intercept \\gamma_{00} now is set to 23.\nThe treatment effect \\gamma_{01} is set to -6 (as before). In this more complex model, the treatment effect refers to a difference between treatment and control group directly after treatment.\nAs mentioned above, it is reasonable to assume that there should be no marked trend in a passive control group. Therefore, we set the conditional main effect for time, \\gamma_{10} to 0.\nFor the interaction effect, we expect a steeper decline in the treatment group (e.g., because the treatment “unfolds” its effect over time). If we set the interaction effect to -0.7, this translates to a predicted slope of -0.7 in the treatment group: \\gamma_{10} time_{ij} + \\gamma_{11} \\text{treatment}_j time_{ij} = (\\gamma_{10} + \\gamma_{11} \\text{treatment}_j) time_{ij}.\n\nIs this slope of -0.7, estimated from the pilot data, reasonable? Let’s extrapolate the trend: Treated patients have an average BDI score of 17 (directly after treatment). If they decline 0.7 points per month, they would have a BDI difference of 12*-0.7 = -8.4 after one year, and are at around 9 BDI points. With that decline, they would have moved from a mild depression to a minimal depression. This seems plausible.\nRandom terms:\n\nAssume that ~95% of slopes in the treatment group lie between -0.7 ± 0.3 (i.e., between -0.4 and -1). This corresponds to a standard deviation of 0.15, and consequently a variance of \\tau_{11} = 0.15^2 = 0.0225.\nWe assume no random effect correlation, so \\tau_{01} = 0.\n\nAs recommended in the last chapter, we visualize this assumed interaction effect by a hand drawing:\n\n\n\nhand-drawn interaction plot\n\n\nLooks good!" - }, - { - "objectID": "LMM.html#lets-simulate-1", - "href": "LMM.html#lets-simulate-1", - "title": "Ch. 4: Linear Mixed Models / Multilevel models", - "section": "Let’s simulate", - "text": "Let’s simulate\n\nsim5 <- function(n_persons = 100, tau_00 = 100, tau_11 = 0.0225, tau_01 = 0, \n gamma_00 = 23, gamma_01 = -6, gamma_10 = 0, gamma_11 = -0.7, \n residual_var = 25, print=FALSE) {\n\n # create (correlated) random effect structure\n mu <- c(0 ,0)\n sigma <- matrix(\n c(tau_00, tau_01, \n tau_01, tau_11), nrow=2, byrow=TRUE)\n RE <- rmvnorm(n=n_persons, mu=mu, sigma=sigma) |> data.frame()\n names(RE) <- c(\"u_0j\", \"u_1j\")\n \n df_L1 <- data.frame(\n person_id = 1:n_persons,\n u_0j = RE$u_0j,\n u_1j = RE$u_1j,\n treatment = rep(c(0, 1), times=n_persons/2)\n )\n \n df_L2 <- data.frame(\n person_id = rep(1:n_persons, each=4), # create 4 rows for each person (as we have 4 measurements)\n time.c = rep(c(0, 2, 4, 6), times=n_persons) \n )\n \n # combine to a long format data set \n df_long <- merge(df_L1, df_L2, by=\"person_id\")\n \n # simulate response variable, based on combined equation \n df_long <- within(df_long, {\n BDI = gamma_00 + # intercept\n gamma_10*time.c + gamma_01*treatment + gamma_11*time.c*treatment + # fixed terms\n u_0j + u_1j*time.c + rnorm(n_persons*4, mean=0, sd=sqrt(residual_var)) # random terms\n })\n \n # for debugging: plot some simulated participants\n #ggplot(df_long[df_long$person_id <=20, ], aes(x=time.c, y=BDI, color=factor(treatment), group=person_id)) +\n # geom_point() + geom_line() + facet_wrap(~person_id)\n \n # ggplot(df_long, aes(x=time.c, y=BDI, color=factor(treatment))) +\n # stat_summary(fun.data=mean_cl_normal, geom = \"pointrange\")\n \n # compute p-values\n # these options might speed up code execution a little bit:\n # , control=lmerControl(optimizer=\"bobyqa\", calc.derivs = FALSE)\n l1 <- lmer(BDI ~ 1 + time.c*treatment + (1 + time.c|person_id), data=df_long)\n \n if (print==TRUE) print(summary(l1))\n else return(summary(l1)$coefficients[, \"Pr(>|t|)\"])\n}\n\n\nset.seed(0xBEEF)\niterations <- 1000\nns <- seq(60, 200, by=10) # must be dividable by 2\n\nresult <- data.frame()\n\nfor (n_persons in ns) {\n system.time({\n p_values <- future_replicate(iterations, sim5(n_persons=n_persons), future.seed = TRUE)\n })\n \n result <- rbind(result, data.frame(\n n = n_persons,\n power_intercept = sum(p_values[1, ] < .005)/iterations,\n power_time.c = sum(p_values[2, ] < .005)/iterations,\n power_treatment = sum(p_values[3, ] < .005)/iterations,\n power_IA = sum(p_values[4, ] < .005)/iterations\n )\n )\n \n # show the result after each run (not shown here in the tutorial)\n print(result)\n}\n\n\n\n n power_intercept power_time.c power_treatment power_IA\n1 60 1 0.004 0.267 0.306\n2 70 1 0.006 0.302 0.422\n3 80 1 0.002 0.413 0.439\n4 90 1 0.003 0.386 0.514\n5 100 1 0.002 0.449 0.563\n6 110 1 0.007 0.462 0.628\n7 120 1 0.007 0.596 0.702\n8 130 1 0.003 0.567 0.768\n9 140 1 0.005 0.629 0.797\n10 150 1 0.005 0.720 0.844\n11 160 1 0.006 0.768 0.835\n12 170 1 0.003 0.790 0.865\n13 180 1 0.005 0.787 0.888\n14 190 1 0.004 0.815 0.913\n15 200 1 0.006 0.904 0.937\n\n\nYou will see a lot of warnings about boundary (singular) fit - you can generally ignore these; they typically occur when one of the random variances is exactly zero; but the fixed effect estimates (which are our focus here) are still valid. You will also see some rare warnings about Model failed to converge with max|grad|. In this case, the optimizer did not converge with the required precision, but typically still is very close. Finally, there are some warnings Model failed to converge with 1 negative eigenvalue. These instances give wrong results. However, if in our thousands of simulations some very few models did not converge, this makes no noticeable difference. If, however, the majority of your models does not converge this is a hint that your simulation has some errors.\nConcerning the computed power, we see that we need around 180 participants for the treatment main effect (which mirrors the result from Chapter 1), and around 140 participants for detecting the interaction effect. As there is no main effect for time.c (it has been simulated as zero), the power stays always around the \\alpha-level." - }, - { - "objectID": "optimizing_code.html", - "href": "optimizing_code.html", - "title": "Bonus: Optimizing R code for speed", - "section": "", - "text": "Reading/working time: ~30 min.\nOptimizing code for speed can be an art - and you get lost and spend/waste hours by micro-optimizing some milliseconds. But the Pareto principle applies here: with 20% effort, you can have quick and substantial gains.\nCode profiling means that the code execution is timed, just like you had a stopwatch. Your goal is to make your code snippet as fast as possible. RStudio has a built-in profiler that (in theory) allows to see which code line takes up the longest time. But in my experience, if the computation of each single line is very short (and the duration mostly comes from the many repetitions), it is very inaccurate (i.e., the time spent is allocated to the wrong lines). Therefore, we’ll resort to the simplest way of timing code: We will measure overall execution time by wrapping our code in a system.time({ ... }) call. Longer code blocks need to be wrapped in curly braces {...}. The function returns multiple timings; the relevant number for us is the “elapsed” time. This is also called the “wall clock” time - the time you actually have to wait until computation finished." - }, - { - "objectID": "optimizing_code.html#preparation-wrap-the-simulation-in-a-function", - "href": "optimizing_code.html#preparation-wrap-the-simulation-in-a-function", - "title": "Bonus: Optimizing R code for speed", - "section": "Preparation: Wrap the simulation in a function", - "text": "Preparation: Wrap the simulation in a function\nThe first step does not really change a lot: We put the simulation code into a separate function that returns the quantity of interest (in our case: the focal p-value). Different settings of the simulation parameters, such as the sample size or the effect size, can be defined as parameters of the function.\nEvery single function call sim() now gives you one simulated p-value - try it out!\nWe then use the replicate function to run the sim function many times and to store the resulting p-values in a vector. Programming the simulation in such a functional style also has the nice side effect that you do not have to pre-allocate the results vector; this is automatically done by the replicate function.\n\n# Wrap the code for a single simulation into a function. It returns the quantity of interest.\nsim <- function(n=100) {\n # the \"n\" is now taken from the function parameter \"n\"\n x <- cbind(\n rep(1, n),\n c(rep(0, n/2), rep(1, n/2))\n )\n \n y <- 23 - 3*x[, 2] + rnorm(n, mean=0, sd=sqrt(117))\n mdl <- RcppArmadillo::fastLmPure(x, y)\n p_val <- 2*pt(abs(mdl$coefficients[2]/mdl$stderr[2]), mdl$df.residual, lower.tail=FALSE)\n\n return(p_val)\n}\n\nt5 <- system.time({\n\niterations <- 5000\nns <- seq(300, 500, by=50)\n\nresult <- data.frame()\n\nfor (n in ns) {\n p_values <- replicate(n=iterations, sim(n=n))\n result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))\n}\n\n})\ntimings <- rbind(t0[3], t1[3], t2[3], t3[3], t4[3], t5[3]) |> data.frame()\ntimings$diff <- c(NA, timings[2:nrow(timings), 1] - timings[1:(nrow(timings)-1), 1])\ntimings$rel_diff <- c(NA, timings[2:nrow(timings), \"diff\"]/timings[1:(nrow(timings)-1), 1]) |> round(3)\ntimings\n\n elapsed diff rel_diff\n1 9.888 NA NA\n2 10.579 0.691 0.070\n3 7.384 -3.195 -0.302\n4 0.793 -6.591 -0.893\n5 0.764 -0.029 -0.037\n6 0.935 0.171 0.224\n\n\nWhile this refactoring actually slightly increased computation time, we need this for the last, final optimization where we reap the benefits." - }, - { - "objectID": "optimizing_code.html#run-on-multiple-cores", - "href": "optimizing_code.html#run-on-multiple-cores", - "title": "Bonus: Optimizing R code for speed", - "section": "Run on multiple cores", - "text": "Run on multiple cores\nWith the use of the replicate function in the previous step, we prepared everything for an easy switch to multi-core processing. You only need to load the future.apply package, start a multi-core session with the plan command, and replace the replicate function call with future_replicate.\n\n# Show how many cores are available on your machine:\navailableCores()\n\n# with plan() you enter the parallel mode. Enter the number of workers (aka. CPU cores)\nplan(multisession, workers = 4)\n\nt6 <- system.time({\n\niterations <- 5000\nns <- seq(300, 500, by=50)\n\nresult <- data.frame()\n\nfor (n in ns) {\n # future.seed = TRUE is needed to set seeds in all parallel processes. Then the computation is reproducible.\n p_values <- future_replicate(n=iterations, sim(n=n), future.seed = TRUE)\n result <- rbind(result, data.frame(n = n, power = sum(p_values < .005)/iterations))\n}\n\n})\n\ntimings <- rbind(t0[3], t1[3], t2[3], t3[3], t4[3], t5[3], t6[3]) |> data.frame()\ntimings$diff <- c(NA, timings[2:nrow(timings), 1] - timings[1:(nrow(timings)-1), 1])\ntimings$rel_diff <- c(NA, timings[2:nrow(timings), \"diff\"]/timings[1:(nrow(timings)-1), 1]) |> round(3) |> round(2)\ntimings\n\n\n\n elapsed diff rel_diff\n1 9.888 NA NA\n2 10.579 0.691 0.07\n3 7.384 -3.195 -0.30\n4 0.793 -6.591 -0.89\n5 0.764 -0.029 -0.04\n6 0.935 0.171 0.22\n7 0.788 -0.147 -0.16\n\n\nThe speed improvement seems only small - with 4 workers, one might expect that the computations only need 1/4th of the previous time. But parallel processing creates some overhead. For example, 4 separate R sessions need to be created and all packages, code (and sometimes data) need to be loaded in each session. Finally, all results must be collected and aggregated from all separate sessions. This can add up to substantial one-time costs. If your (single-core) computations only take a few seconds or less, parallel processing can even take longer." - }, - { - "objectID": "Regression_to_mean.html", - "href": "Regression_to_mean.html", - "title": "Regression to the mean in pre-post-designs", - "section": "", - "text": "BDI_{post} = b_0 + 1*BDI_{pre} + e\nBDI_{post} = b_0 + b_1*BDI_{pre} + b_2*treatment + e\nHence, the pre value simply is carried forward to the post value. We add random error, as participants of course go somewhat up and down, but there is no systematic trend.\n\n library(Rfast)\n\nLade nötiges Paket: Rcpp\n\n\nLade nötiges Paket: RcppZiggurat\n\n n <- 10000\n pre_post_cor <- 0.9\n mu <- c(23, 23)\n sigma <- matrix(c(117, pre_post_cor*sqrt(117)*sqrt(117), pre_post_cor*sqrt(117)*sqrt(117), 117), nrow=2, byrow=TRUE)\n BDI <- rmvnorm(n, mu, sigma) |> data.frame()\n \n \n xi <- rnorm(n, mean=23, sd=sqrt(117))\n pre <- 1*xi + rnorm(n, mean=0, sd=2)\n post <- 1*xi + rnorm(n, mean=0, sd=2)\n \n cor(pre, post)\n\n[1] 0.9679785\n\n colMeans(BDI)\n\n X1 X2 \n22.92187 22.95114 \n\n var(BDI)\n\n X1 X2\nX1 118.0560 106.2575\nX2 106.2575 118.1335\n\n names(BDI) <- c(\"pre\", \"post\")\n BDI$id <- 1:nrow(BDI)\n summary(lm(post~pre, BDI))\n\n\nCall:\nlm(formula = post ~ pre, data = BDI)\n\nResiduals:\n Min 1Q Median 3Q Max \n-16.0754 -3.2091 0.0313 3.2705 19.4456 \n\nCoefficients:\n Estimate Std. Error t value Pr(>|t|) \n(Intercept) 2.320078 0.110740 20.95 <2e-16 ***\npre 0.900060 0.004366 206.17 <2e-16 ***\n---\nSignif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n\nResidual standard error: 4.743 on 9998 degrees of freedom\nMultiple R-squared: 0.8096, Adjusted R-squared: 0.8096 \nF-statistic: 4.251e+04 on 1 and 9998 DF, p-value: < 2.2e-16\n\n library(tidyr)\n library(ggplot2)\n BDI_long <- pivot_longer(BDI, c(pre, post), names_to = \"time\")\n ggplot(BDI_long, aes(x=time, y=value, group=id)) + geom_line()\n\n\n\n # typically RTM pattern: The pre-post difference is\n BDI$diff <- BDI$post-BDI$pre\n BDI$absdiff <- abs(BDI$post-BDI$pre)\n hist(BDI$diff)\n\n\n\n mean(BDI$diff)\n\n[1] 0.02927231\n\n summary(lm(diff~pre, BDI))\n\n\nCall:\nlm(formula = diff ~ pre, data = BDI)\n\nResiduals:\n Min 1Q Median 3Q Max \n-16.0754 -3.2091 0.0313 3.2705 19.4456 \n\nCoefficients:\n Estimate Std. Error t value Pr(>|t|) \n(Intercept) 2.320078 0.110740 20.95 <2e-16 ***\npre -0.099940 0.004366 -22.89 <2e-16 ***\n---\nSignif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n\nResidual standard error: 4.743 on 9998 degrees of freedom\nMultiple R-squared: 0.04981, Adjusted R-squared: 0.04971 \nF-statistic: 524.1 on 1 and 9998 DF, p-value: < 2.2e-16\n\n ggplot(BDI, aes(x=pre, y=absdiff)) + geom_point() + geom_smooth()\n\n`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = \"cs\")'\n\n\n\n\n ggplot(BDI, aes(x=pre, y=post)) + geom_point() + geom_smooth()\n\n`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = \"cs\")'\n\n\n\n\n BDI$predicted <- predict(lm(post~pre, BDI))\n mean(BDI$predicted)\n\n[1] 22.95114\n\n var(BDI$predicted)\n\n[1] 95.63817\n\n # The predicted values have the same mean (so no systematic treatment effect, as expected)\n # but much smaller variance:\n \n BDI_long2 <- pivot_longer(BDI, c(pre, post, predicted), names_to = \"cat\")\n \n library(ggplot2)\nggplot(BDI_long2, aes(x=as.factor(cat), y=value)) + \n ggdist::stat_halfeye(adjust = .5, width = .3, .width = 0, justification = -.3, point_colour = NA) + \n geom_boxplot(width = .1, outlier.shape = NA) +\n gghalves::geom_half_point(side = \"l\", range_scale = .4, alpha = .5)\n\n\n\n\nAssumed that we use the predicted scores at t2 (post) and predict the next scores at t3 - will it shrink ever more, until all data points are at the mean? But this is not substantively reflected in the raw scores. Is it an artifact of regression?" - }, - { - "objectID": "Resources.html", - "href": "Resources.html", - "title": "Resources", - "section": "", - "text": "SuperpowerBook by Aaron R. Caldwell, Daniël Lakens, Chelsea M. Parlett-Pelleriti, Guy Prochilo, Frederik Aust. This teaches how to use the superpower R package to simulate factorial designs and calculate power.\nBlog post series “Power Analysis by Data Simulation in R” by Julian Quandt (see Parts I to IV). These blog posts provide a very detailed step-by-step explanation of the necessary steps.\n“Simulation-based power analysis for regression” by Andrew Hales (Youtube video, OSF Material). In this video, Andrew explains the steps to you.\nfaux package by Lisa DeBruine. Simulate data for factorial and mixed designs." - }, - { - "objectID": "SEM.html", - "href": "SEM.html", - "title": "Ch. 5: Structural Equation Models", - "section": "", - "text": "Reading/working time: ~50 min.\nIn this chapter, we will focus on some rather simple Structural Equation Models (SEM). The goal is to illustrate how simulations can be used to estimate statistical power to detect a given effect in a SEM. In the context of SEMs, the focal effect may be for instance a fit index (e.g., Chi-Square, Root Mean Square Error of Approximation, etc.) or a model coefficient (e.g., a regression coefficient for the association between two latent factors). In this chapter, we only focus on the latter, that is power analyses for regression coefficients in the context of SEM. Please see the bonus chapter titled “Power analysis for fit indices in SEM” if you’re interested in power analyses for fit indices." - }, - { - "objectID": "SEM.html#lets-get-some-real-data-as-starting-point", - "href": "SEM.html#lets-get-some-real-data-as-starting-point", - "title": "Ch. 5: Structural Equation Models", - "section": "Let’s get some real data as starting point", - "text": "Let’s get some real data as starting point\nJust like for any other simulation-based power analysis, we first need to come up with plausible estimates of the distribution of the (manifest) variables. For the sake of simplicity, let’s assume that there is a published study that measured manifestations of our two latent variables and that the corresponding data set is publicly available. For the purpose of this tutorial, we will draw on a publication by Bergh et al (2016) and the corresponding data set which has been made accessible as part of the MPsychoR package. Let’s take a look at this data set.\n\n#install.packages(\"MPsychoR\")\nlibrary(MPsychoR)\n\ndata(\"Bergh\")\nattach(Bergh)\n\n#let's take a look\nhead(Bergh)\n\n EP SP HP DP A1 A2 A3 O1 O2 O3\n1 2.666667 3.125 1.4 2.818182 3.4375 3.600000 3.352941 2.8750 3.400000 3.176471\n2 2.666667 3.250 1.4 2.545455 2.3125 2.666667 3.117647 4.4375 3.866667 4.470588\n3 1.000000 1.625 2.7 2.000000 3.5625 4.600000 3.941176 4.2500 3.666667 3.705882\n4 2.666667 2.750 1.8 2.818182 2.7500 3.200000 3.352941 2.8750 3.400000 3.117647\n5 2.888889 3.250 2.7 3.000000 3.2500 4.200000 3.764706 3.9375 4.400000 4.294118\n6 2.000000 2.375 1.7 2.181818 3.2500 3.333333 2.941176 3.8125 3.066667 3.411765\n gender\n1 male\n2 male\n3 male\n4 male\n5 male\n6 male\n\ntail(Bergh)\n\n EP SP HP DP A1 A2 A3 O1 O2\n856 2.000000 2.500 1.0 2.363636 3.3125 3.800000 3.705882 3.6875 3.733333\n857 1.555556 1.875 1.1 1.818182 2.6250 3.733333 3.000000 3.3125 2.800000\n858 3.000000 2.750 1.0 1.909091 3.3125 3.533333 3.882353 4.0000 3.533333\n859 1.444444 1.250 1.0 1.000000 3.8750 3.800000 3.529412 3.9375 3.533333\n860 1.222222 1.625 1.0 2.363636 2.8750 3.600000 3.411765 2.8125 3.600000\n861 1.555556 1.750 1.0 1.090909 3.3750 4.266667 4.058824 3.3750 2.800000\n O3 gender\n856 4.411765 female\n857 3.529412 female\n858 3.823529 female\n859 3.411765 female\n860 3.058824 female\n861 3.588235 female\n\n\nThis data set comprises 11 variables measured in 861 participants. For now, we will focus on the following measured variables:\n\nEP is a continuous variable measuring ethnic prejudice.\nSP is a continuous variable measuring sexism.\nHP is a continuous variable measuring sexual prejudice towards gays and lesbians.\nDP is a continuous variable measuring prejudice toward people with disabilities.\nO1, O2, and O3 are three items measuring openness to experience.\n\nTo get an impression of this data, we look at the correlations of the variables we’re interested in.\n\ncor(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2)\n\n EP SP HP DP O1 O2 O3\nEP 1.00 0.53 0.25 0.53 -0.35 -0.36 -0.41\nSP 0.53 1.00 0.22 0.53 -0.33 -0.31 -0.33\nHP 0.25 0.22 1.00 0.24 -0.23 -0.30 -0.29\nDP 0.53 0.53 0.24 1.00 -0.30 -0.33 -0.34\nO1 -0.35 -0.33 -0.23 -0.30 1.00 0.66 0.74\nO2 -0.36 -0.31 -0.30 -0.33 0.66 1.00 0.71\nO3 -0.41 -0.33 -0.29 -0.34 0.74 0.71 1.00\n\n\nAs we have discussed in the previous chapters, the starting point of every simulation-based power analysis is to specify the population parameters of the variables of interest. In our example, we can estimate the population parameters from the study by Bergh et al. (2016). We start by calculating the means of the variables, rounding them generously, and storing them in a vector called means_vector.\n\n#store means\nmeans_vector <- c(mean(EP), mean(SP), mean(HP), mean(DP), mean(O1), mean(O2), mean(O3)) |> round(2)\n\n#Let's take a look\nmeans_vector\n\n[1] 1.99 2.11 1.22 2.06 3.55 3.49 3.61\n\n\nWe also need the variance-covariance matrix of our variables in order to simulate data. Luckily, we can estimate this from the Bergh et al. data as well. There are two ways to do this. First, we can use the cov function to obtain the variance-covariance matrix. This matrix incorporates the variance of each variable on the diagonal, and the covariances in the remaining cells.\n\n#store covariances\ncov_mat <- cov(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2)\n\n#Let's take a look\ncov_mat\n\n EP SP HP DP O1 O2 O3\nEP 0.51 0.26 0.28 0.20 -0.12 -0.12 -0.15\nSP 0.26 0.47 0.24 0.19 -0.11 -0.10 -0.12\nHP 0.28 0.24 2.44 0.20 -0.18 -0.22 -0.23\nDP 0.20 0.19 0.20 0.28 -0.08 -0.08 -0.09\nO1 -0.12 -0.11 -0.18 -0.08 0.23 0.15 0.18\nO2 -0.12 -0.10 -0.22 -0.08 0.15 0.22 0.17\nO3 -0.15 -0.12 -0.23 -0.09 0.18 0.17 0.26\n\n\nThis works well as long as we have a data set (e.g., from a pilot study or published work) to estimate the variances and covariances. In other cases, however, we might not have access to such a data set. In this case, we might only have a correlation table that was provided in a published paper. But that’s no problem either, as we can transform the correlations and standard deviations of the variables of interest into a variance-covariance matrix. The following chunk shows how this works by using the cor2cov function from the MBESS package.\n\n#store correlation matrix \ncor_mat <- cor(cbind(EP, SP, HP, DP, O1, O2, O3)) \n\n#store standard deviations\nsd_vector <- c(sd(EP), sd(SP), sd(HP), sd(DP), sd(O1), sd(O2), sd(O3))\n\n#transform correlations and standard deviations into variance-covariance matrix\ncov_mat2 <- MBESS::cor2cov(cor.mat = cor_mat, sd = sd_vector) |> as.data.frame() |> round(2)\n\n#Let's take a look\ncov_mat2\n\n EP SP HP DP O1 O2 O3\nEP 0.51 0.26 0.28 0.20 -0.12 -0.12 -0.15\nSP 0.26 0.47 0.24 0.19 -0.11 -0.10 -0.12\nHP 0.28 0.24 2.44 0.20 -0.18 -0.22 -0.23\nDP 0.20 0.19 0.20 0.28 -0.08 -0.08 -0.09\nO1 -0.12 -0.11 -0.18 -0.08 0.23 0.15 0.18\nO2 -0.12 -0.10 -0.22 -0.08 0.15 0.22 0.17\nO3 -0.15 -0.12 -0.23 -0.09 0.18 0.17 0.26\n\n\nLet’s do a plausibility check: Did the two ways to estimate the variance-covariance matrix lead to the same results?\n\ncov_mat == cov_mat2\n\n EP SP HP DP O1 O2 O3\nEP TRUE TRUE TRUE TRUE TRUE TRUE TRUE\nSP TRUE TRUE TRUE TRUE TRUE TRUE TRUE\nHP TRUE TRUE TRUE TRUE TRUE TRUE TRUE\nDP TRUE TRUE TRUE TRUE TRUE TRUE TRUE\nO1 TRUE TRUE TRUE TRUE TRUE TRUE TRUE\nO2 TRUE TRUE TRUE TRUE TRUE TRUE TRUE\nO3 TRUE TRUE TRUE TRUE TRUE TRUE TRUE\n\n\nIndeed, this worked! Both procedures lead to the exact same variance-covariance matrix. Now that we have an approximation of the variance-covariance matrix, we use the rmvnorm function from the Rfast package to simulate data from a multivariate normal distribution. The following code simulates n = 50 observations from the specified population.\n\n#Set seed to make results reproducible\nset.seed(21364)\n\n#simulate data\nmy_first_simulated_data <- Rfast::rmvnorm(n = 50, mu=means_vector, sigma = cov_mat) |> as.data.frame()\n\n#Let's take a look\nhead(my_first_simulated_data)\n\n EP SP HP DP O1 O2 O3\n1 1.175231 2.1563276 -0.4498951 1.535653 3.520299 3.768441 3.877645\n2 2.615520 1.7527931 -0.2546117 1.728349 3.048912 2.816513 2.743648\n3 1.849380 2.0383562 -0.3877381 2.055153 3.516697 3.354251 3.792305\n4 1.901085 2.5438523 2.0475777 2.150921 3.824178 3.631459 3.947051\n5 2.182503 2.1326429 -0.2088395 2.674773 3.948864 3.642066 4.005110\n6 1.691124 0.9746677 1.9193559 1.652397 4.184485 3.236144 3.669261\n\n\nWe could now fit a SEM to this simulated data set and check whether the regression coefficient modelling the association between openness to experience and generalized prejudice is significant at an \\alpha-level of .005. We will work with the lavaan package to fit SEMs.\n\n#specify SEM\nmodel_sem <- \"generalized_prejudice =~ EP + DP + SP + HP\n openness =~ O1 + O2 + O3\n generalized_prejudice ~ openness\"\n\n#fit the SEM to the simulated data set\nfit_sem <- sem(model_sem, data = my_first_simulated_data)\n\n#display the results\nsummary(fit_sem)\n\nlavaan 0.6.15 ended normally after 36 iterations\n\n Estimator ML\n Optimization method NLMINB\n Number of model parameters 15\n\n Number of observations 50\n\nModel Test User Model:\n \n Test statistic 20.528\n Degrees of freedom 13\n P-value (Chi-square) 0.083\n\nParameter Estimates:\n\n Standard errors Standard\n Information Expected\n Information saturated (h1) model Structured\n\nLatent Variables:\n Estimate Std.Err z-value P(>|z|)\n generalized_prejudice =~ \n EP 1.000 \n DP 0.891 0.298 2.989 0.003\n SP 1.156 0.383 3.022 0.003\n HP 0.321 0.677 0.474 0.636\n openness =~ \n O1 1.000 \n O2 0.857 0.155 5.544 0.000\n O3 1.370 0.225 6.086 0.000\n\nRegressions:\n Estimate Std.Err z-value P(>|z|)\n generalized_prejudice ~ \n openness -0.259 0.204 -1.270 0.204\n\nVariances:\n Estimate Std.Err z-value P(>|z|)\n .EP 0.352 0.082 4.277 0.000\n .DP 0.084 0.037 2.292 0.022\n .SP 0.165 0.064 2.577 0.010\n .HP 2.475 0.496 4.990 0.000\n .O1 0.065 0.019 3.433 0.001\n .O2 0.067 0.017 3.996 0.000\n .O3 0.045 0.027 1.655 0.098\n .generlzd_prjdc 0.138 0.078 1.766 0.077\n openness 0.116 0.036 3.182 0.001\n\n\nThe results show that in this case, the regression coefficient is -0.26 which is significant with p = 0.204. But, actually, it is not our primary interest to see whether this particular simulated data set results in a significant regression coefficient. Rather, we want to know how many of a theoretically infinite number of simulations yield a significant p-value of the focal regression coefficient. Thus, as in the previous chapters, we now repeatedly simulate data sets of a certain size (say, 50 observations) from the specified population and store the results of the focal test (here: the p-value of the regression coefficient) in a vector called p_values.\n\n#Set seed to make results reproducible\nset.seed(21364)\n\n#let's do 500 iterations\niterations <- 500\n\n#prepare an empty NA vector with 500 slots\np_values <- rep(NA, iterations)\n\n#sample size per iteration\nn <- 50\n\n\n#simulate data\nfor(i in 1:iterations){\n\n simulated_data <- Rfast::rmvnorm(n = n, mu = means_vector, sigma = cov_mat) |> as.data.frame()\n fit_sem_simulated <- sem(model_sem, data = simulated_data)\n \n p_values[i] <- parameterestimates(fit_sem_simulated)[8,]$pvalue\n \n}\n\nHow many of our 500 virtual samples would have found a significant p-value (i.e., p < .005)?\n\n#frequency table\ntable(p_values < .005)\n\n\nFALSE TRUE \n 144 356 \n\n#percentage of significant results\nsum(p_values < .005)/iterations*100\n\n[1] 71.2\n\n\nOnly 71.2% of samples with the same size of n=50 result in a significant p-value. We conclude that n=50 observations seems to be insufficient, as the power with these parameters is lower than 80%." - }, - { - "objectID": "SEM.html#sample-size-planning-find-the-necessary-sample-size", - "href": "SEM.html#sample-size-planning-find-the-necessary-sample-size", - "title": "Ch. 5: Structural Equation Models", - "section": "Sample size planning: Find the necessary sample size", - "text": "Sample size planning: Find the necessary sample size\nBut how many observations do we need to find the presumed effect with a power of 80%? Like before, we can now systematically vary certain parameters (e.g., sample size) of our simulation and see how that affects power. We could, for example, vary the sample size in a range from 30 to 200. Running these simulations typically requires quite some computing time.\n\n#Set seed to make results reproducible\nset.seed(21364)\n\n#test ns between 30 and 200\nns_sem <- seq(30, 200, by=10) \n\n#prepare empty vector to store results\nresult_sem <- data.frame()\n\n#set number of iterations\niterations_sem <- 500\n\n#write function\nsim_sem <- function(n, model, mu, sigma) {\n \n\n simulated_data <- Rfast::rmvnorm(n = n, mu = mu, sigma = sigma) |> as.data.frame()\n fit_sem_simulated <- sem(model, data = simulated_data)\n p_value_sem <- parameterestimates(fit_sem_simulated)[8,]$pvalue\n return(p_value_sem)\n \n }\n\n\n#replicate function with varying ns\nfor (n in ns_sem) { \n \np_values_sem <- future_replicate(iterations_sem, sim_sem(n = n, model = model_sem, mu = means_vector, sigma = cov_mat), future.seed=TRUE) \nresult_sem <- rbind(result_sem, data.frame(\n n = n,\n power = sum(p_values_sem < .005)/iterations_sem)\n )\n\n#The following line of code can be used to track the progress of the simulations \n#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time\n#I have deactivated this here; to enable it, just remove the \"#\" sign at the beginning of the next line\n#message(paste(\"Progress info: Simulations completed for n =\", n))\n\n\n}\n\nLet’s plot the results:\n\nggplot(result_sem, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = \"red\")\n\n\n\n\nThis graph suggests that we need a sample size of approximately 50 participants to reach a power of 80% with the given population estimates. That’s all it takes to run a power analysis for a SEM!\nIn the following two sub-paragraphs, we would like to present two alternatives and/or extensions to this first way of doing a power analysis for a SEM. First, we would like to present an alternative way to simulate data for SEMs, i.e. by using a built-in function in the lavaan package. Second, we would like to show how a “safeguard” approach to power analysis can be used within the context of SEM." - }, - { - "objectID": "SEM.html#using-the-lavaan-syntax-to-simulate-data", - "href": "SEM.html#using-the-lavaan-syntax-to-simulate-data", - "title": "Ch. 5: Structural Equation Models", - "section": "Using the lavaan syntax to simulate data", - "text": "Using the lavaan syntax to simulate data\nIn our opening example in this chapter, we used the rmvnorm function from the Rfastpackage to simulate data based on the means of the manifest variables as well as their variance-covariance matrix. An alternative to this procedure is to use a built-in function in the lavaan package, that is, the simulateData() function. The main idea here is to provide this function with a lavaan model that specifies all relevant population parameters and then to use this function to directly simulate data.\nMore specifically, we need to incorporate the factor loadings, regression coefficients and (residual) variances of all latent and manifest variables in the lavaan syntax. As these parameters are hardly ever known without a previous study, we will again draw on the results from the data by Bergh et al (2016). Let’s again take a look at the results of our SEM when applying it to this data set.\n\n#fit the SEM to the pilot data set\nfit_bergh <- sem(model_sem, data = Bergh)\n\n#display the results\nsummary(fit_bergh)\n\nlavaan 0.6.15 ended normally after 40 iterations\n\n Estimator ML\n Optimization method NLMINB\n Number of model parameters 15\n\n Number of observations 861\n\nModel Test User Model:\n \n Test statistic 40.574\n Degrees of freedom 13\n P-value (Chi-square) 0.000\n\nParameter Estimates:\n\n Standard errors Standard\n Information Expected\n Information saturated (h1) model Structured\n\nLatent Variables:\n Estimate Std.Err z-value P(>|z|)\n generalized_prejudice =~ \n EP 1.000 \n DP 0.710 0.041 17.383 0.000\n SP 0.907 0.052 17.337 0.000\n HP 1.042 0.112 9.267 0.000\n openness =~ \n O1 1.000 \n O2 0.932 0.035 26.255 0.000\n O3 1.143 0.040 28.923 0.000\n\nRegressions:\n Estimate Std.Err z-value P(>|z|)\n generalized_prejudice ~ \n openness -0.766 0.056 -13.641 0.000\n\nVariances:\n Estimate Std.Err z-value P(>|z|)\n .EP 0.219 0.016 13.403 0.000\n .DP 0.141 0.009 14.972 0.000\n .SP 0.233 0.015 15.079 0.000\n .HP 2.124 0.106 19.965 0.000\n .O1 0.073 0.005 14.423 0.000\n .O2 0.079 0.005 15.805 0.000\n .O3 0.052 0.005 9.823 0.000\n .generlzd_prjdc 0.191 0.018 10.362 0.000\n openness 0.161 0.011 14.210 0.000\n\n\nIn order to use the simulateData() function, we can take the estimates from this previous study and plug them into the lavaan syntax in the following chunk.\n\n#Set seed to make results reproducible\nset.seed(21364)\n\n#specify SEM\nmodel_fully_specified <- \n\n\"generalized_prejudice =~ 1*EP + 0.71*DP + 0.91*SP + 1*HP\nopenness =~ 1*O1 + 0.93*O2 + 1.14*O3\ngeneralized_prejudice ~ -0.77*openness\n\n\ngeneralized_prejudice ~~ 0.19*generalized_prejudice\nopenness ~~ 0.16*openness\nEP ~~ 0.21*EP\nDP ~~ 0.14*DP\nSP ~~ 0.23*SP\nHP ~~ 2.12*HP\nO1 ~~ 0.07*O1\nO2 ~~ 0.08*O2\nO3 ~~ 0.05*O3\n\n\"\n\n#lets try this\nsim_lavaan <- simulateData(model_fully_specified, sample.nobs=100)\nhead(sim_lavaan)\n\n EP DP SP HP O1 O2\n1 0.009175975 -0.4818076 -0.29785829 -3.5273250 -0.02254289 -0.1394437\n2 -0.204483904 -0.4787598 0.15013918 -3.1894504 -0.56917823 -0.2076622\n3 0.456774325 -0.6544974 0.05802164 -2.3337546 0.26092341 0.7767911\n4 -1.153264826 -0.3216415 -0.91603659 -2.0334785 -0.16649019 -0.1387532\n5 0.512683482 0.3138148 0.28465681 0.1202821 -0.41602783 -0.2699893\n6 -1.064347791 -0.3287166 -0.62215235 -2.4007914 0.60927732 -0.1814121\n O3\n1 -0.22583796\n2 -1.17504320\n3 0.11402119\n4 0.06726909\n5 -0.62531893\n6 0.13393737\n\n\nThe next step is to integrate this code into our first simulation-based power analysis in this chapter, that is, to replace the data simulation process using the rmvnorm function from the Rfast package with this new method using simulateData() from the lavaan package. To this end, I am adapting the power analysis SEM chunk from above accordingly.\n\n#Set seed to make results reproducible\nset.seed(21364)\n\n#test ns between 30 and 200\nns_sem <- seq(30, 200, by=10) \n\n#prepare empty vector to store results\nresult_sem <- data.frame()\n\n#set number of iterations\niterations_sem <- 500\n\n#write function\nsim_sem_lavaan <- function(n, model) {\n \n \n sim_lavaan <- simulateData(model = model, sample.nobs=n)\n fit_sem_simulated <- sem(model_sem, data = sim_lavaan)\n p_value_sem <- parameterestimates(fit_sem_simulated)[8,]$pvalue\n return(p_value_sem)\n \n}\n\n\n#replicate function with varying ns\nfor (n in ns_sem) { \n \n p_values_sem <- future_replicate(iterations_sem, sim_sem_lavaan(n = n, model = model_fully_specified), future.seed=TRUE) \n result_sem <- rbind(result_sem, data.frame(\n n = n,\n power = sum(p_values_sem < .005, na.rm = TRUE)/iterations_sem)\n )\n \n#The following line of code can be used to track the progress of the simulations \n#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time\n#I have deactivated this here; to enable it, just remove the \"#\" sign at the beginning of the next line\n#message(paste(\"Progress info: Simulations completed for n =\", n))\n\n}\n\nLet’s plot this again.\n\nggplot(result_sem, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = \"red\")\n\n\n\n\nHere, we conclude that approx. 55 participants would be needed to achieve 80% power. The slight difference compared to the previous power analysis should be explained by the fact that we rounded the numbers that define our statistical populations and that we only used 500 Monte Carlo iterations – these differences should decrease with an increasing number of iterations.\n\n\n\n\n\n\nTip\n\n\n\nWang and Rhemtulla (2021) developed a shiny app that can do power analyses for SEMs in a similar fashion, but that additionally provides a point-and-click interface. You can use it here, for instance, to replicate the results of this simulation-based power analysis: https://yilinandrewang.shinyapps.io/pwrSEM/" - }, - { - "objectID": "SEM.html#a-safeguard-power-approach-for-sems", - "href": "SEM.html#a-safeguard-power-approach-for-sems", - "title": "Ch. 5: Structural Equation Models", - "section": "A safeguard power approach for SEMs", - "text": "A safeguard power approach for SEMs\nOf note, the two power analyses for our SEM we have conducted so far used the observed effect size from the Bergh et al. (2016) data set as an estimate of the “true” effect size. But, as this point estimate may be imprecise (e.g., because of publication bias), it seems reasonable to use a more conservative estimate of the true effect size. One more conservative approach in this context is the safeguard power approach (Perugini et al., 2014), which we have already applied in Chapter 1 (Linear Model I: a single dichotomous predictor).\nBasically, all need to do in order to account for variability of observed effect sizes is to calculate a 60%-confidence interval around the point estimate of the observed effect size from our pilot data and to use the more conservative bound of this confidence interval (here: the upper bound) as our new effect size estimate. This can be easily done with the parameterestimates function from the lavaan package which takes the level of the confidence interval as an input parameter. Let’s use this function on our object fit_bergh which stores the results of our SEM in the Bergh data set.\n\nparameterestimates(fit_bergh, level = .60)\n\n lhs op rhs est se z pvalue\n1 generalized_prejudice =~ EP 1.000 0.000 NA NA\n2 generalized_prejudice =~ DP 0.710 0.041 17.383 0\n3 generalized_prejudice =~ SP 0.907 0.052 17.337 0\n4 generalized_prejudice =~ HP 1.042 0.112 9.267 0\n5 openness =~ O1 1.000 0.000 NA NA\n6 openness =~ O2 0.932 0.035 26.255 0\n7 openness =~ O3 1.143 0.040 28.923 0\n8 generalized_prejudice ~ openness -0.766 0.056 -13.641 0\n9 EP ~~ EP 0.219 0.016 13.403 0\n10 DP ~~ DP 0.141 0.009 14.972 0\n11 SP ~~ SP 0.233 0.015 15.079 0\n12 HP ~~ HP 2.124 0.106 19.965 0\n13 O1 ~~ O1 0.073 0.005 14.423 0\n14 O2 ~~ O2 0.079 0.005 15.805 0\n15 O3 ~~ O3 0.052 0.005 9.823 0\n16 generalized_prejudice ~~ generalized_prejudice 0.191 0.018 10.362 0\n17 openness ~~ openness 0.161 0.011 14.210 0\n ci.lower ci.upper\n1 1.000 1.000\n2 0.676 0.744\n3 0.863 0.951\n4 0.948 1.137\n5 1.000 1.000\n6 0.902 0.962\n7 1.110 1.176\n8 -0.814 -0.719\n9 0.205 0.233\n10 0.133 0.148\n11 0.220 0.246\n12 2.034 2.213\n13 0.069 0.078\n14 0.074 0.083\n15 0.048 0.057\n16 0.175 0.206\n17 0.152 0.171\n\n\nThis output shows that the upper bound of the 60% confidence interval around the focal regression coefficient is -0.72. We can now use this as our new and more conservative effect size estimate. We can for example insert this value into our simulation using the lavaan syntax. For this purpose, we copy the chunk from above and simply replace the previous effect size estimate (-0.77) with the new estimate (-0.72), while keeping all other parameters that define this data set.\n\n#Set seed to make results reproducible\nset.seed(21364)\n\n#specify SEM\nmodel_fully_specified_safeguard <- \n\n\"generalized_prejudice =~ 1*EP + 0.71*DP + 0.91*SP + 1*HP\nopenness =~ 1*O1 + 0.93*O2 + 1.14*O3\ngeneralized_prejudice ~ -0.72*openness\n\n\ngeneralized_prejudice ~~ 0.19*generalized_prejudice\nopenness ~~ 0.16*openness\nEP ~~ 0.21*EP\nDP ~~ 0.14*DP\nSP ~~ 0.23*SP\nHP ~~ 2.12*HP\nO1 ~~ 0.07*O1\nO2 ~~ 0.08*O2\nO3 ~~ 0.05*O3\n\n\"\n\n#lets try this\nsim_lavaan_safeguard <- simulateData(model_fully_specified_safeguard, sample.nobs=100)\nhead(sim_lavaan_safeguard)\n\n EP DP SP HP O1 O2\n1 0.03163582 -0.4674398 -0.2762416 -3.5075535 -0.04391145 -0.1593227\n2 -0.19674199 -0.4764434 0.1605740 -3.1573002 -0.58997086 -0.2268869\n3 0.48315832 -0.6351419 0.0799879 -2.3272372 0.25229795 0.7683784\n4 -1.13893054 -0.3131634 -0.9001980 -2.0386232 -0.18372798 -0.1543441\n5 0.49985196 0.3039113 0.2740270 0.1386227 -0.41638579 -0.2703941\n6 -1.04098556 -0.3130325 -0.5996895 -2.4067345 0.59293247 -0.1965773\n O3\n1 -0.25024294\n2 -1.19986667\n3 0.10431698\n4 0.04700113\n5 -0.62587386\n6 0.11528181\n\n\nNow, everything is ready for the actual safeguard power analysis. We can re-use the sim_sem_lavaan function we have defined above. Let’s see what we get here!\n\n#Set seed to make results reproducible\nset.seed(21364)\n\n#prepare empty vector to store results\nresult_sem_safeguard <- data.frame()\n\n#replicate function with varying ns\nfor (n in ns_sem) { \n \n p_values_sem_safeguard <- future_replicate(iterations_sem, sim_sem_lavaan(n = n, model = model_fully_specified_safeguard), future.seed=TRUE) \n result_sem_safeguard <- rbind(result_sem_safeguard, data.frame(\n n = n,\n power = sum(p_values_sem_safeguard < .005, na.rm = TRUE)/iterations_sem)\n )\n \n#The following line of code can be used to track the progress of the simulations \n#This can be helpful for simulations with a high number of iterations and/or a large parameter space which require a lot of time\n#I have deactivated this here; to enable it, just remove the \"#\" sign at the beginning of the next line\n#message(paste(\"Progress info: Simulations completed for n =\", n))\n \n}\n\nggplot(result_sem_safeguard, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = \"red\")\n\n\n\n\nThis safeguard power analysis yields a required sample size of ca. 63 participants.\n\n\n\n\n\n\nNote\n\n\n\nIn addition to this safeguard power approach, we would also have liked to derive a smallest effect size of interest (SESOI). However, no prior studies on SESOI in the context of personality/stereotypes were available and the measures/response scales used by Bergh et al (2016) were only vaguely reported in their manuscript, thereby making it difficult to derive a meaningful SESOI. We therefore only report a safeguard approach but no SESOI approach here." - }, - { - "objectID": "SEM_fit_index.html", - "href": "SEM_fit_index.html", - "title": "Bonus: Fit indices in SEM", - "section": "", - "text": "#install.packages(c(\"future.apply\", \"ggplot2\", \"lavaan\", \"MPsychoR\"), dependencies = TRUE)\n\nrequire(future.apply)\nrequire(ggplot2)\nrequire(lavaan)\nrequire(MPsychoR)\n\nIn this chapter, we will turn to simulation-based power analysis for fit indices in the context of SEM. We will build on the model we have introduced in Chapter 5 (Structural Equation Modelling (SEM)), it is therefore recommendable to read this chapter first. The following chunk specifies this model, in which (latent) generalized prejudice is predicted by (latent) openness to experience (as conceptualized in the big five personality traits). We fit this model to the dataset by Bergh et al. (2016) in order to get a first impression of the model fit.\n\ndata(\"Bergh\")\n\nmodel_sem <- \"generalized_prejudice =~ EP + DP + SP + HP\n openness =~ O1 + O2 + O3\n generalized_prejudice ~ openness\"\n\n#fit the SEM to the pilot data set\nfit <- sem(model_sem, data = Bergh)\n\nsummary(fit, fit.measures = TRUE)\n\nlavaan 0.6.15 ended normally after 40 iterations\n\n Estimator ML\n Optimization method NLMINB\n Number of model parameters 15\n\n Number of observations 861\n\nModel Test User Model:\n \n Test statistic 40.574\n Degrees of freedom 13\n P-value (Chi-square) 0.000\n\nModel Test Baseline Model:\n\n Test statistic 2395.448\n Degrees of freedom 21\n P-value 0.000\n\nUser Model versus Baseline Model:\n\n Comparative Fit Index (CFI) 0.988\n Tucker-Lewis Index (TLI) 0.981\n\nLoglikelihood and Information Criteria:\n\n Loglikelihood user model (H0) -4740.818\n Loglikelihood unrestricted model (H1) -4720.530\n \n Akaike (AIC) 9511.635\n Bayesian (BIC) 9583.007\n Sample-size adjusted Bayesian (SABIC) 9535.371\n\nRoot Mean Square Error of Approximation:\n\n RMSEA 0.050\n 90 Percent confidence interval - lower 0.033\n 90 Percent confidence interval - upper 0.067\n P-value H_0: RMSEA <= 0.050 0.482\n P-value H_0: RMSEA >= 0.080 0.002\n\nStandardized Root Mean Square Residual:\n\n SRMR 0.038\n\nParameter Estimates:\n\n Standard errors Standard\n Information Expected\n Information saturated (h1) model Structured\n\nLatent Variables:\n Estimate Std.Err z-value P(>|z|)\n generalized_prejudice =~ \n EP 1.000 \n DP 0.710 0.041 17.383 0.000\n SP 0.907 0.052 17.337 0.000\n HP 1.042 0.112 9.267 0.000\n openness =~ \n O1 1.000 \n O2 0.932 0.035 26.255 0.000\n O3 1.143 0.040 28.923 0.000\n\nRegressions:\n Estimate Std.Err z-value P(>|z|)\n generalized_prejudice ~ \n openness -0.766 0.056 -13.641 0.000\n\nVariances:\n Estimate Std.Err z-value P(>|z|)\n .EP 0.219 0.016 13.403 0.000\n .DP 0.141 0.009 14.972 0.000\n .SP 0.233 0.015 15.079 0.000\n .HP 2.124 0.106 19.965 0.000\n .O1 0.073 0.005 14.423 0.000\n .O2 0.079 0.005 15.805 0.000\n .O3 0.052 0.005 9.823 0.000\n .generlzd_prjdc 0.191 0.018 10.362 0.000\n openness 0.161 0.011 14.210 0.000\n\n\nThere are many different fit indices displayed in this output, for example the Comparative Fit Index (CFI), the Root Mean Square Error of Approximation (RMSEA) and the Standardized Root Mean Square Residual (SRMR). We can not go into the details of the interpretations of the fit indices here, but it is important to know that many of these indices are not very sensitive to sample size. Therefore, running a power analysis for these fit indices is not really meaningful. But instead of analyzing how one of these indices varies as a function of sample size, we can optimize the precision of one of these indices. For example, the lavaan output from above displays the 90% confidence interval for the RMSEA index. We could, for example, plan to find a certain sample size that ensures that the confidence interval around the RMSEA estimate has a certain maximum size, that is, the RMSEA estimate is sufficiently precise. This what we will learn to do in this chapter.\nAs a starting point, we again define the population parameters (i.e., the means, variances, and co-variances of all measured variables). We use the study by Bergh et al. (2016) to estimate these parameters (just as we did in Chapter 5).\n\nattach(Bergh)\n\n#store means\nmeans_vector <- c(mean(EP), mean(SP), mean(HP), mean(DP), mean(O1), mean(O2), mean(O3)) |> round(2)\n\n#store covariances\ncov_mat <- cov(cbind(EP, SP, HP, DP, O1, O2, O3)) |> round(2)\n\nWith these parameters, we can simulate data using the rmvnorm function from the Rfast package. The only difference to the simulation described in Chapter 5 is that here, we do not calculate and store the p-value of the regression coefficient, but rather, we compute the width of the RMSEA confidence interval and store it in a vector. We then count the number of simulations that yield a confidence interval with a maximum size of, say, .10. The next chunk shows how this is done.\n\nset.seed(9875234)\n\n#test ns between 50 and 200\nns <- seq(50, 200, by=10) \n\n#prepare empty vector to store results\nresult <- data.frame()\n\n#set number of iterations\niterations <- 1000\n\n#write function\nsim_sem <- function(n, model, mu, sigma) {\n \n\n simulated_data <- Rfast::rmvnorm(n = n, mu = mu, sigma = sigma) |> as.data.frame()\n fit_sem_simulated <- sem(model_sem, data = simulated_data)\n rmsea_ci_width <- as.numeric(fitMeasures(fit_sem_simulated)[\"rmsea.ci.upper\"] - fitMeasures(fit_sem_simulated)[\"rmsea.ci.lower\"])\n return(rmsea_ci_width)\n \n }\n\n\n#replicate function with varying ns\nfor (n in ns) { \n \nrmsea_ci_width <- future_replicate(iterations, sim_sem(n = n, model = model_sem, mu = means_vector, sigma = cov_mat), future.seed=TRUE) \nresult <- rbind(result, data.frame(\n n = n,\n power = sum(rmsea_ci_width < .1)/iterations)\n )\n\n}\n\nLet’s plot this.\n\nggplot(result, aes(x=n, y=power)) + geom_point() + geom_line() + scale_x_continuous(n.breaks = 18, limits = c(30,200)) + scale_y_continuous(n.breaks = 10, limits = c(0,1)) + geom_hline(yintercept= 0.8, color = \"red\")\n\n\n\n\nThis analysis suggests that approx. 168 participants are needed to obtain a 90%-confidence interval around the RMSEA coefficient that is not larger than .10 in 80% of the cases." - } -] \ No newline at end of file diff --git a/docs/site_libs/bootstrap/bootstrap-icons.css b/docs/site_libs/bootstrap/bootstrap-icons.css deleted file mode 100644 index f51d04b..0000000 --- a/docs/site_libs/bootstrap/bootstrap-icons.css +++ /dev/null @@ -1,1704 +0,0 @@ -@font-face { - font-family: "bootstrap-icons"; - src: -url("./bootstrap-icons.woff?524846017b983fc8ded9325d94ed40f3") format("woff"); -} - -.bi::before, -[class^="bi-"]::before, -[class*=" bi-"]::before { - display: inline-block; - font-family: bootstrap-icons !important; - font-style: normal; - font-weight: normal !important; - font-variant: normal; - text-transform: none; - line-height: 1; - vertical-align: -.125em; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.bi-123::before { content: "\f67f"; } -.bi-alarm-fill::before { content: "\f101"; } -.bi-alarm::before { content: "\f102"; } -.bi-align-bottom::before { content: "\f103"; } -.bi-align-center::before { content: "\f104"; } -.bi-align-end::before { content: "\f105"; } -.bi-align-middle::before { content: "\f106"; } -.bi-align-start::before { content: "\f107"; } -.bi-align-top::before { content: "\f108"; } -.bi-alt::before { content: "\f109"; } -.bi-app-indicator::before { content: "\f10a"; } -.bi-app::before { content: "\f10b"; } -.bi-archive-fill::before { content: "\f10c"; } -.bi-archive::before { content: "\f10d"; } -.bi-arrow-90deg-down::before { content: "\f10e"; } -.bi-arrow-90deg-left::before { content: "\f10f"; } -.bi-arrow-90deg-right::before { content: "\f110"; } -.bi-arrow-90deg-up::before { content: "\f111"; } -.bi-arrow-bar-down::before { content: "\f112"; } -.bi-arrow-bar-left::before { content: "\f113"; } -.bi-arrow-bar-right::before { content: "\f114"; } -.bi-arrow-bar-up::before { content: "\f115"; } -.bi-arrow-clockwise::before { content: "\f116"; } -.bi-arrow-counterclockwise::before { content: "\f117"; } -.bi-arrow-down-circle-fill::before { content: "\f118"; } -.bi-arrow-down-circle::before { content: "\f119"; } -.bi-arrow-down-left-circle-fill::before { content: "\f11a"; } -.bi-arrow-down-left-circle::before { content: "\f11b"; } -.bi-arrow-down-left-square-fill::before { content: "\f11c"; } -.bi-arrow-down-left-square::before { content: "\f11d"; } -.bi-arrow-down-left::before { content: "\f11e"; } -.bi-arrow-down-right-circle-fill::before { content: "\f11f"; } -.bi-arrow-down-right-circle::before { content: "\f120"; } -.bi-arrow-down-right-square-fill::before { content: "\f121"; } -.bi-arrow-down-right-square::before { content: "\f122"; } -.bi-arrow-down-right::before { content: "\f123"; } -.bi-arrow-down-short::before { content: "\f124"; } -.bi-arrow-down-square-fill::before { content: "\f125"; } -.bi-arrow-down-square::before { content: "\f126"; } -.bi-arrow-down-up::before { content: "\f127"; } -.bi-arrow-down::before { content: "\f128"; } -.bi-arrow-left-circle-fill::before { content: "\f129"; } -.bi-arrow-left-circle::before { content: "\f12a"; } -.bi-arrow-left-right::before { content: "\f12b"; } -.bi-arrow-left-short::before { content: "\f12c"; } -.bi-arrow-left-square-fill::before { content: "\f12d"; } -.bi-arrow-left-square::before { content: "\f12e"; } -.bi-arrow-left::before { content: "\f12f"; } -.bi-arrow-repeat::before { content: "\f130"; } -.bi-arrow-return-left::before { content: "\f131"; } -.bi-arrow-return-right::before { content: "\f132"; } -.bi-arrow-right-circle-fill::before { content: "\f133"; } -.bi-arrow-right-circle::before { content: "\f134"; } -.bi-arrow-right-short::before { content: "\f135"; } -.bi-arrow-right-square-fill::before { content: "\f136"; } -.bi-arrow-right-square::before { content: "\f137"; } -.bi-arrow-right::before { content: "\f138"; } -.bi-arrow-up-circle-fill::before { content: "\f139"; } -.bi-arrow-up-circle::before { content: "\f13a"; } -.bi-arrow-up-left-circle-fill::before { content: "\f13b"; } -.bi-arrow-up-left-circle::before { content: "\f13c"; } -.bi-arrow-up-left-square-fill::before { content: "\f13d"; } -.bi-arrow-up-left-square::before { content: "\f13e"; } -.bi-arrow-up-left::before { content: "\f13f"; } -.bi-arrow-up-right-circle-fill::before { content: "\f140"; } -.bi-arrow-up-right-circle::before { content: "\f141"; } -.bi-arrow-up-right-square-fill::before { content: "\f142"; } -.bi-arrow-up-right-square::before { content: "\f143"; } -.bi-arrow-up-right::before { content: "\f144"; } -.bi-arrow-up-short::before { content: "\f145"; } -.bi-arrow-up-square-fill::before { content: "\f146"; } -.bi-arrow-up-square::before { content: "\f147"; } -.bi-arrow-up::before { content: "\f148"; } -.bi-arrows-angle-contract::before { content: "\f149"; } -.bi-arrows-angle-expand::before { content: "\f14a"; } -.bi-arrows-collapse::before { content: "\f14b"; } -.bi-arrows-expand::before { content: "\f14c"; } -.bi-arrows-fullscreen::before { content: "\f14d"; } -.bi-arrows-move::before { content: "\f14e"; } -.bi-aspect-ratio-fill::before { content: "\f14f"; } -.bi-aspect-ratio::before { content: "\f150"; } -.bi-asterisk::before { content: "\f151"; } -.bi-at::before { content: "\f152"; } -.bi-award-fill::before { content: "\f153"; } -.bi-award::before { content: "\f154"; } -.bi-back::before { content: "\f155"; } -.bi-backspace-fill::before { content: "\f156"; } -.bi-backspace-reverse-fill::before { content: "\f157"; } -.bi-backspace-reverse::before { content: "\f158"; } -.bi-backspace::before { content: "\f159"; } -.bi-badge-3d-fill::before { content: "\f15a"; } -.bi-badge-3d::before { content: "\f15b"; } -.bi-badge-4k-fill::before { content: "\f15c"; } -.bi-badge-4k::before { content: "\f15d"; } -.bi-badge-8k-fill::before { content: "\f15e"; } -.bi-badge-8k::before { content: "\f15f"; } -.bi-badge-ad-fill::before { content: "\f160"; } -.bi-badge-ad::before { content: "\f161"; } -.bi-badge-ar-fill::before { content: "\f162"; } -.bi-badge-ar::before { content: "\f163"; } -.bi-badge-cc-fill::before { content: "\f164"; } -.bi-badge-cc::before { content: "\f165"; } -.bi-badge-hd-fill::before { content: "\f166"; } -.bi-badge-hd::before { content: "\f167"; } -.bi-badge-tm-fill::before { content: "\f168"; } -.bi-badge-tm::before { content: "\f169"; } -.bi-badge-vo-fill::before { content: "\f16a"; } -.bi-badge-vo::before { content: "\f16b"; } -.bi-badge-vr-fill::before { content: "\f16c"; } -.bi-badge-vr::before { content: "\f16d"; } -.bi-badge-wc-fill::before { content: "\f16e"; } -.bi-badge-wc::before { content: "\f16f"; } -.bi-bag-check-fill::before { content: "\f170"; } -.bi-bag-check::before { content: "\f171"; } -.bi-bag-dash-fill::before { content: "\f172"; } -.bi-bag-dash::before { content: "\f173"; } -.bi-bag-fill::before { content: "\f174"; } -.bi-bag-plus-fill::before { content: "\f175"; } -.bi-bag-plus::before { content: "\f176"; } -.bi-bag-x-fill::before { content: "\f177"; } -.bi-bag-x::before { content: "\f178"; } -.bi-bag::before { content: "\f179"; } -.bi-bar-chart-fill::before { content: "\f17a"; } -.bi-bar-chart-line-fill::before { content: "\f17b"; } -.bi-bar-chart-line::before { content: "\f17c"; } -.bi-bar-chart-steps::before { content: "\f17d"; } -.bi-bar-chart::before { content: "\f17e"; } -.bi-basket-fill::before { content: "\f17f"; } -.bi-basket::before { content: "\f180"; } -.bi-basket2-fill::before { content: "\f181"; } -.bi-basket2::before { content: "\f182"; } -.bi-basket3-fill::before { content: "\f183"; } -.bi-basket3::before { content: "\f184"; } -.bi-battery-charging::before { content: "\f185"; } -.bi-battery-full::before { content: "\f186"; } -.bi-battery-half::before { content: "\f187"; } -.bi-battery::before { content: "\f188"; } -.bi-bell-fill::before { content: "\f189"; } -.bi-bell::before { content: "\f18a"; } -.bi-bezier::before { content: "\f18b"; } -.bi-bezier2::before { content: "\f18c"; } -.bi-bicycle::before { content: "\f18d"; } -.bi-binoculars-fill::before { content: "\f18e"; } -.bi-binoculars::before { content: "\f18f"; } -.bi-blockquote-left::before { content: "\f190"; } -.bi-blockquote-right::before { content: "\f191"; } -.bi-book-fill::before { content: "\f192"; } -.bi-book-half::before { content: "\f193"; } -.bi-book::before { content: "\f194"; } -.bi-bookmark-check-fill::before { content: "\f195"; } -.bi-bookmark-check::before { content: "\f196"; } -.bi-bookmark-dash-fill::before { content: "\f197"; } -.bi-bookmark-dash::before { content: "\f198"; } -.bi-bookmark-fill::before { content: "\f199"; } -.bi-bookmark-heart-fill::before { content: "\f19a"; } -.bi-bookmark-heart::before { content: "\f19b"; } -.bi-bookmark-plus-fill::before { content: "\f19c"; } -.bi-bookmark-plus::before { content: "\f19d"; } -.bi-bookmark-star-fill::before { content: "\f19e"; } -.bi-bookmark-star::before { content: "\f19f"; } -.bi-bookmark-x-fill::before { content: "\f1a0"; } -.bi-bookmark-x::before { content: "\f1a1"; } -.bi-bookmark::before { content: "\f1a2"; } -.bi-bookmarks-fill::before { content: "\f1a3"; } -.bi-bookmarks::before { content: "\f1a4"; } -.bi-bookshelf::before { content: "\f1a5"; } -.bi-bootstrap-fill::before { content: "\f1a6"; } -.bi-bootstrap-reboot::before { content: "\f1a7"; } -.bi-bootstrap::before { content: "\f1a8"; } -.bi-border-all::before { content: "\f1a9"; } -.bi-border-bottom::before { content: "\f1aa"; } -.bi-border-center::before { content: "\f1ab"; } -.bi-border-inner::before { content: "\f1ac"; } -.bi-border-left::before { content: "\f1ad"; } -.bi-border-middle::before { content: "\f1ae"; } -.bi-border-outer::before { content: "\f1af"; } -.bi-border-right::before { content: "\f1b0"; } -.bi-border-style::before { content: "\f1b1"; } -.bi-border-top::before { content: "\f1b2"; } -.bi-border-width::before { content: "\f1b3"; } -.bi-border::before { content: "\f1b4"; } -.bi-bounding-box-circles::before { content: "\f1b5"; } -.bi-bounding-box::before { content: "\f1b6"; } -.bi-box-arrow-down-left::before { content: "\f1b7"; } -.bi-box-arrow-down-right::before { content: "\f1b8"; } -.bi-box-arrow-down::before { content: "\f1b9"; } -.bi-box-arrow-in-down-left::before { content: "\f1ba"; } -.bi-box-arrow-in-down-right::before { content: "\f1bb"; } -.bi-box-arrow-in-down::before { content: "\f1bc"; } -.bi-box-arrow-in-left::before { content: "\f1bd"; } -.bi-box-arrow-in-right::before { content: "\f1be"; } -.bi-box-arrow-in-up-left::before { content: "\f1bf"; } -.bi-box-arrow-in-up-right::before { content: "\f1c0"; } -.bi-box-arrow-in-up::before { content: "\f1c1"; } -.bi-box-arrow-left::before { content: "\f1c2"; } -.bi-box-arrow-right::before { content: "\f1c3"; } -.bi-box-arrow-up-left::before { content: "\f1c4"; } -.bi-box-arrow-up-right::before { content: "\f1c5"; } -.bi-box-arrow-up::before { content: "\f1c6"; } -.bi-box-seam::before { content: "\f1c7"; } -.bi-box::before { content: "\f1c8"; } -.bi-braces::before { content: "\f1c9"; } -.bi-bricks::before { content: "\f1ca"; } -.bi-briefcase-fill::before { content: "\f1cb"; } -.bi-briefcase::before { content: "\f1cc"; } -.bi-brightness-alt-high-fill::before { content: "\f1cd"; } -.bi-brightness-alt-high::before { content: "\f1ce"; } -.bi-brightness-alt-low-fill::before { content: "\f1cf"; } -.bi-brightness-alt-low::before { content: "\f1d0"; } -.bi-brightness-high-fill::before { content: "\f1d1"; } -.bi-brightness-high::before { content: "\f1d2"; } -.bi-brightness-low-fill::before { content: "\f1d3"; } -.bi-brightness-low::before { content: "\f1d4"; } -.bi-broadcast-pin::before { content: "\f1d5"; } -.bi-broadcast::before { content: "\f1d6"; } -.bi-brush-fill::before { content: "\f1d7"; } -.bi-brush::before { content: "\f1d8"; } -.bi-bucket-fill::before { content: "\f1d9"; } -.bi-bucket::before { content: "\f1da"; } -.bi-bug-fill::before { content: "\f1db"; } -.bi-bug::before { content: "\f1dc"; } -.bi-building::before { content: "\f1dd"; } -.bi-bullseye::before { content: "\f1de"; } -.bi-calculator-fill::before { content: "\f1df"; } -.bi-calculator::before { content: "\f1e0"; } -.bi-calendar-check-fill::before { content: "\f1e1"; } -.bi-calendar-check::before { content: "\f1e2"; } -.bi-calendar-date-fill::before { content: "\f1e3"; } -.bi-calendar-date::before { content: "\f1e4"; } -.bi-calendar-day-fill::before { content: "\f1e5"; } -.bi-calendar-day::before { content: "\f1e6"; } -.bi-calendar-event-fill::before { content: "\f1e7"; } -.bi-calendar-event::before { content: "\f1e8"; } -.bi-calendar-fill::before { content: "\f1e9"; } -.bi-calendar-minus-fill::before { content: "\f1ea"; } -.bi-calendar-minus::before { content: "\f1eb"; } -.bi-calendar-month-fill::before { content: "\f1ec"; } -.bi-calendar-month::before { content: "\f1ed"; } -.bi-calendar-plus-fill::before { content: "\f1ee"; } -.bi-calendar-plus::before { content: "\f1ef"; } -.bi-calendar-range-fill::before { content: "\f1f0"; } -.bi-calendar-range::before { content: "\f1f1"; } -.bi-calendar-week-fill::before { content: "\f1f2"; } -.bi-calendar-week::before { content: "\f1f3"; } -.bi-calendar-x-fill::before { content: "\f1f4"; } -.bi-calendar-x::before { content: "\f1f5"; } -.bi-calendar::before { content: "\f1f6"; } -.bi-calendar2-check-fill::before { content: "\f1f7"; } -.bi-calendar2-check::before { content: "\f1f8"; } -.bi-calendar2-date-fill::before { content: "\f1f9"; } -.bi-calendar2-date::before { content: "\f1fa"; } -.bi-calendar2-day-fill::before { content: "\f1fb"; } -.bi-calendar2-day::before { content: "\f1fc"; } -.bi-calendar2-event-fill::before { content: "\f1fd"; } -.bi-calendar2-event::before { content: "\f1fe"; } -.bi-calendar2-fill::before { content: "\f1ff"; } -.bi-calendar2-minus-fill::before { content: "\f200"; } -.bi-calendar2-minus::before { content: "\f201"; } -.bi-calendar2-month-fill::before { content: "\f202"; } -.bi-calendar2-month::before { content: "\f203"; } -.bi-calendar2-plus-fill::before { content: "\f204"; } -.bi-calendar2-plus::before { content: "\f205"; } -.bi-calendar2-range-fill::before { content: "\f206"; } -.bi-calendar2-range::before { content: "\f207"; } -.bi-calendar2-week-fill::before { content: "\f208"; } -.bi-calendar2-week::before { content: "\f209"; } -.bi-calendar2-x-fill::before { content: "\f20a"; } -.bi-calendar2-x::before { content: "\f20b"; } -.bi-calendar2::before { content: "\f20c"; } -.bi-calendar3-event-fill::before { content: "\f20d"; } -.bi-calendar3-event::before { content: "\f20e"; } -.bi-calendar3-fill::before { content: "\f20f"; } -.bi-calendar3-range-fill::before { content: "\f210"; } -.bi-calendar3-range::before { content: "\f211"; } -.bi-calendar3-week-fill::before { content: "\f212"; } -.bi-calendar3-week::before { content: "\f213"; } -.bi-calendar3::before { content: "\f214"; } -.bi-calendar4-event::before { content: "\f215"; } -.bi-calendar4-range::before { content: "\f216"; } -.bi-calendar4-week::before { content: "\f217"; } -.bi-calendar4::before { content: "\f218"; } -.bi-camera-fill::before { content: "\f219"; } -.bi-camera-reels-fill::before { content: "\f21a"; } -.bi-camera-reels::before { content: "\f21b"; } -.bi-camera-video-fill::before { content: "\f21c"; } -.bi-camera-video-off-fill::before { content: "\f21d"; } -.bi-camera-video-off::before { content: "\f21e"; } -.bi-camera-video::before { content: "\f21f"; } -.bi-camera::before { content: "\f220"; } -.bi-camera2::before { content: "\f221"; } -.bi-capslock-fill::before { content: "\f222"; } -.bi-capslock::before { content: "\f223"; } -.bi-card-checklist::before { content: "\f224"; } -.bi-card-heading::before { content: "\f225"; } -.bi-card-image::before { content: "\f226"; } -.bi-card-list::before { content: "\f227"; } -.bi-card-text::before { content: "\f228"; } -.bi-caret-down-fill::before { content: "\f229"; } -.bi-caret-down-square-fill::before { content: "\f22a"; } -.bi-caret-down-square::before { content: "\f22b"; } -.bi-caret-down::before { content: "\f22c"; } -.bi-caret-left-fill::before { content: "\f22d"; } -.bi-caret-left-square-fill::before { content: "\f22e"; } -.bi-caret-left-square::before { content: "\f22f"; } -.bi-caret-left::before { content: "\f230"; } -.bi-caret-right-fill::before { content: "\f231"; } -.bi-caret-right-square-fill::before { content: "\f232"; } -.bi-caret-right-square::before { content: "\f233"; } -.bi-caret-right::before { content: "\f234"; } -.bi-caret-up-fill::before { content: "\f235"; } -.bi-caret-up-square-fill::before { content: "\f236"; } -.bi-caret-up-square::before { content: "\f237"; } -.bi-caret-up::before { content: "\f238"; } -.bi-cart-check-fill::before { content: "\f239"; } -.bi-cart-check::before { content: "\f23a"; } -.bi-cart-dash-fill::before { content: "\f23b"; } -.bi-cart-dash::before { content: "\f23c"; } -.bi-cart-fill::before { content: "\f23d"; } -.bi-cart-plus-fill::before { content: "\f23e"; } -.bi-cart-plus::before { content: "\f23f"; } -.bi-cart-x-fill::before { content: "\f240"; } -.bi-cart-x::before { content: "\f241"; } -.bi-cart::before { content: "\f242"; } -.bi-cart2::before { content: "\f243"; } -.bi-cart3::before { content: "\f244"; } -.bi-cart4::before { content: "\f245"; } -.bi-cash-stack::before { content: "\f246"; } -.bi-cash::before { content: "\f247"; } -.bi-cast::before { content: "\f248"; } -.bi-chat-dots-fill::before { content: "\f249"; } -.bi-chat-dots::before { content: "\f24a"; } -.bi-chat-fill::before { content: "\f24b"; } -.bi-chat-left-dots-fill::before { content: "\f24c"; } -.bi-chat-left-dots::before { content: "\f24d"; } -.bi-chat-left-fill::before { content: "\f24e"; } -.bi-chat-left-quote-fill::before { content: "\f24f"; } -.bi-chat-left-quote::before { content: "\f250"; } -.bi-chat-left-text-fill::before { content: "\f251"; } -.bi-chat-left-text::before { content: "\f252"; } -.bi-chat-left::before { content: "\f253"; } -.bi-chat-quote-fill::before { content: "\f254"; } -.bi-chat-quote::before { content: "\f255"; } -.bi-chat-right-dots-fill::before { content: "\f256"; } -.bi-chat-right-dots::before { content: "\f257"; } -.bi-chat-right-fill::before { content: "\f258"; } -.bi-chat-right-quote-fill::before { content: "\f259"; } -.bi-chat-right-quote::before { content: "\f25a"; } -.bi-chat-right-text-fill::before { content: "\f25b"; } -.bi-chat-right-text::before { content: "\f25c"; } -.bi-chat-right::before { content: "\f25d"; } -.bi-chat-square-dots-fill::before { content: "\f25e"; } -.bi-chat-square-dots::before { content: "\f25f"; } -.bi-chat-square-fill::before { content: "\f260"; } -.bi-chat-square-quote-fill::before { content: "\f261"; } -.bi-chat-square-quote::before { content: "\f262"; } -.bi-chat-square-text-fill::before { content: "\f263"; } -.bi-chat-square-text::before { content: "\f264"; } -.bi-chat-square::before { content: "\f265"; } -.bi-chat-text-fill::before { content: "\f266"; } -.bi-chat-text::before { content: "\f267"; } -.bi-chat::before { content: "\f268"; } -.bi-check-all::before { content: "\f269"; } -.bi-check-circle-fill::before { content: "\f26a"; } -.bi-check-circle::before { content: "\f26b"; } -.bi-check-square-fill::before { content: "\f26c"; } -.bi-check-square::before { content: "\f26d"; } -.bi-check::before { content: "\f26e"; } -.bi-check2-all::before { content: "\f26f"; } -.bi-check2-circle::before { content: "\f270"; } -.bi-check2-square::before { content: "\f271"; } -.bi-check2::before { content: "\f272"; } -.bi-chevron-bar-contract::before { content: "\f273"; } -.bi-chevron-bar-down::before { content: "\f274"; } -.bi-chevron-bar-expand::before { content: "\f275"; } -.bi-chevron-bar-left::before { content: "\f276"; } -.bi-chevron-bar-right::before { content: "\f277"; } -.bi-chevron-bar-up::before { content: "\f278"; } -.bi-chevron-compact-down::before { content: "\f279"; } -.bi-chevron-compact-left::before { content: "\f27a"; } -.bi-chevron-compact-right::before { content: "\f27b"; } -.bi-chevron-compact-up::before { content: "\f27c"; } -.bi-chevron-contract::before { content: "\f27d"; } -.bi-chevron-double-down::before { content: "\f27e"; } -.bi-chevron-double-left::before { content: "\f27f"; } -.bi-chevron-double-right::before { content: "\f280"; } -.bi-chevron-double-up::before { content: "\f281"; } -.bi-chevron-down::before { content: "\f282"; } -.bi-chevron-expand::before { content: "\f283"; } -.bi-chevron-left::before { content: "\f284"; } -.bi-chevron-right::before { content: "\f285"; } -.bi-chevron-up::before { content: "\f286"; } -.bi-circle-fill::before { content: "\f287"; } -.bi-circle-half::before { content: "\f288"; } -.bi-circle-square::before { content: "\f289"; } -.bi-circle::before { content: "\f28a"; } -.bi-clipboard-check::before { content: "\f28b"; } -.bi-clipboard-data::before { content: "\f28c"; } -.bi-clipboard-minus::before { content: "\f28d"; } -.bi-clipboard-plus::before { content: "\f28e"; } -.bi-clipboard-x::before { content: "\f28f"; } -.bi-clipboard::before { content: "\f290"; } -.bi-clock-fill::before { content: "\f291"; } -.bi-clock-history::before { content: "\f292"; } -.bi-clock::before { content: "\f293"; } -.bi-cloud-arrow-down-fill::before { content: "\f294"; } -.bi-cloud-arrow-down::before { content: "\f295"; } -.bi-cloud-arrow-up-fill::before { content: "\f296"; } -.bi-cloud-arrow-up::before { content: "\f297"; } -.bi-cloud-check-fill::before { content: "\f298"; } -.bi-cloud-check::before { content: "\f299"; } -.bi-cloud-download-fill::before { content: "\f29a"; } -.bi-cloud-download::before { content: "\f29b"; } -.bi-cloud-drizzle-fill::before { content: "\f29c"; } -.bi-cloud-drizzle::before { content: "\f29d"; } -.bi-cloud-fill::before { content: "\f29e"; } -.bi-cloud-fog-fill::before { content: "\f29f"; } -.bi-cloud-fog::before { content: "\f2a0"; } -.bi-cloud-fog2-fill::before { content: "\f2a1"; } -.bi-cloud-fog2::before { content: "\f2a2"; } -.bi-cloud-hail-fill::before { content: "\f2a3"; } -.bi-cloud-hail::before { content: "\f2a4"; } -.bi-cloud-haze-1::before { content: "\f2a5"; } -.bi-cloud-haze-fill::before { content: "\f2a6"; } -.bi-cloud-haze::before { content: "\f2a7"; } -.bi-cloud-haze2-fill::before { content: "\f2a8"; } -.bi-cloud-lightning-fill::before { content: "\f2a9"; } -.bi-cloud-lightning-rain-fill::before { content: "\f2aa"; } -.bi-cloud-lightning-rain::before { content: "\f2ab"; } -.bi-cloud-lightning::before { content: "\f2ac"; } -.bi-cloud-minus-fill::before { content: "\f2ad"; } -.bi-cloud-minus::before { content: "\f2ae"; } -.bi-cloud-moon-fill::before { content: "\f2af"; } -.bi-cloud-moon::before { content: "\f2b0"; } -.bi-cloud-plus-fill::before { content: "\f2b1"; } -.bi-cloud-plus::before { content: "\f2b2"; } -.bi-cloud-rain-fill::before { content: "\f2b3"; } -.bi-cloud-rain-heavy-fill::before { content: "\f2b4"; } -.bi-cloud-rain-heavy::before { content: "\f2b5"; } -.bi-cloud-rain::before { content: "\f2b6"; } -.bi-cloud-slash-fill::before { content: "\f2b7"; } -.bi-cloud-slash::before { content: "\f2b8"; } -.bi-cloud-sleet-fill::before { content: "\f2b9"; } -.bi-cloud-sleet::before { content: "\f2ba"; } -.bi-cloud-snow-fill::before { content: "\f2bb"; } -.bi-cloud-snow::before { content: "\f2bc"; } -.bi-cloud-sun-fill::before { content: "\f2bd"; } -.bi-cloud-sun::before { content: "\f2be"; } -.bi-cloud-upload-fill::before { content: "\f2bf"; } -.bi-cloud-upload::before { content: "\f2c0"; } -.bi-cloud::before { content: "\f2c1"; } -.bi-clouds-fill::before { content: "\f2c2"; } -.bi-clouds::before { content: "\f2c3"; } -.bi-cloudy-fill::before { content: "\f2c4"; } -.bi-cloudy::before { content: "\f2c5"; } -.bi-code-slash::before { content: "\f2c6"; } -.bi-code-square::before { content: "\f2c7"; } -.bi-code::before { content: "\f2c8"; } -.bi-collection-fill::before { content: "\f2c9"; } -.bi-collection-play-fill::before { content: "\f2ca"; } -.bi-collection-play::before { content: "\f2cb"; } -.bi-collection::before { content: "\f2cc"; } -.bi-columns-gap::before { content: "\f2cd"; } -.bi-columns::before { content: "\f2ce"; } -.bi-command::before { content: "\f2cf"; } -.bi-compass-fill::before { content: "\f2d0"; } -.bi-compass::before { content: "\f2d1"; } -.bi-cone-striped::before { content: "\f2d2"; } -.bi-cone::before { content: "\f2d3"; } -.bi-controller::before { content: "\f2d4"; } -.bi-cpu-fill::before { content: "\f2d5"; } -.bi-cpu::before { content: "\f2d6"; } -.bi-credit-card-2-back-fill::before { content: "\f2d7"; } -.bi-credit-card-2-back::before { content: "\f2d8"; } -.bi-credit-card-2-front-fill::before { content: "\f2d9"; } -.bi-credit-card-2-front::before { content: "\f2da"; } -.bi-credit-card-fill::before { content: "\f2db"; } -.bi-credit-card::before { content: "\f2dc"; } -.bi-crop::before { content: "\f2dd"; } -.bi-cup-fill::before { content: "\f2de"; } -.bi-cup-straw::before { content: "\f2df"; } -.bi-cup::before { content: "\f2e0"; } -.bi-cursor-fill::before { content: "\f2e1"; } -.bi-cursor-text::before { content: "\f2e2"; } -.bi-cursor::before { content: "\f2e3"; } -.bi-dash-circle-dotted::before { content: "\f2e4"; } -.bi-dash-circle-fill::before { content: "\f2e5"; } -.bi-dash-circle::before { content: "\f2e6"; } -.bi-dash-square-dotted::before { content: "\f2e7"; } -.bi-dash-square-fill::before { content: "\f2e8"; } -.bi-dash-square::before { content: "\f2e9"; } -.bi-dash::before { content: "\f2ea"; } -.bi-diagram-2-fill::before { content: "\f2eb"; } -.bi-diagram-2::before { content: "\f2ec"; } -.bi-diagram-3-fill::before { content: "\f2ed"; } -.bi-diagram-3::before { content: "\f2ee"; } -.bi-diamond-fill::before { content: "\f2ef"; } -.bi-diamond-half::before { content: "\f2f0"; } -.bi-diamond::before { content: "\f2f1"; } -.bi-dice-1-fill::before { content: "\f2f2"; } -.bi-dice-1::before { content: "\f2f3"; } -.bi-dice-2-fill::before { content: "\f2f4"; } -.bi-dice-2::before { content: "\f2f5"; } -.bi-dice-3-fill::before { content: "\f2f6"; } -.bi-dice-3::before { content: "\f2f7"; } -.bi-dice-4-fill::before { content: "\f2f8"; } -.bi-dice-4::before { content: "\f2f9"; } -.bi-dice-5-fill::before { content: "\f2fa"; } -.bi-dice-5::before { content: "\f2fb"; } -.bi-dice-6-fill::before { content: "\f2fc"; } -.bi-dice-6::before { content: "\f2fd"; } -.bi-disc-fill::before { content: "\f2fe"; } -.bi-disc::before { content: "\f2ff"; } -.bi-discord::before { content: "\f300"; } -.bi-display-fill::before { content: "\f301"; } -.bi-display::before { content: "\f302"; } -.bi-distribute-horizontal::before { content: "\f303"; } -.bi-distribute-vertical::before { content: "\f304"; } -.bi-door-closed-fill::before { content: "\f305"; } -.bi-door-closed::before { content: "\f306"; } -.bi-door-open-fill::before { content: "\f307"; } -.bi-door-open::before { content: "\f308"; } -.bi-dot::before { content: "\f309"; } -.bi-download::before { content: "\f30a"; } -.bi-droplet-fill::before { content: "\f30b"; } -.bi-droplet-half::before { content: "\f30c"; } -.bi-droplet::before { content: "\f30d"; } -.bi-earbuds::before { content: "\f30e"; } -.bi-easel-fill::before { content: "\f30f"; } -.bi-easel::before { content: "\f310"; } -.bi-egg-fill::before { content: "\f311"; } -.bi-egg-fried::before { content: "\f312"; } -.bi-egg::before { content: "\f313"; } -.bi-eject-fill::before { content: "\f314"; } -.bi-eject::before { content: "\f315"; } -.bi-emoji-angry-fill::before { content: "\f316"; } -.bi-emoji-angry::before { content: "\f317"; } -.bi-emoji-dizzy-fill::before { content: "\f318"; } -.bi-emoji-dizzy::before { content: "\f319"; } -.bi-emoji-expressionless-fill::before { content: "\f31a"; } -.bi-emoji-expressionless::before { content: "\f31b"; } -.bi-emoji-frown-fill::before { content: "\f31c"; } -.bi-emoji-frown::before { content: "\f31d"; } -.bi-emoji-heart-eyes-fill::before { content: "\f31e"; } -.bi-emoji-heart-eyes::before { content: "\f31f"; } -.bi-emoji-laughing-fill::before { content: "\f320"; } -.bi-emoji-laughing::before { content: "\f321"; } -.bi-emoji-neutral-fill::before { content: "\f322"; } -.bi-emoji-neutral::before { content: "\f323"; } -.bi-emoji-smile-fill::before { content: "\f324"; } -.bi-emoji-smile-upside-down-fill::before { content: "\f325"; } -.bi-emoji-smile-upside-down::before { content: "\f326"; } -.bi-emoji-smile::before { content: "\f327"; } -.bi-emoji-sunglasses-fill::before { content: "\f328"; } -.bi-emoji-sunglasses::before { content: "\f329"; } -.bi-emoji-wink-fill::before { content: "\f32a"; } -.bi-emoji-wink::before { content: "\f32b"; } -.bi-envelope-fill::before { content: "\f32c"; } -.bi-envelope-open-fill::before { content: "\f32d"; } -.bi-envelope-open::before { content: "\f32e"; } -.bi-envelope::before { content: "\f32f"; } -.bi-eraser-fill::before { content: "\f330"; } -.bi-eraser::before { content: "\f331"; } -.bi-exclamation-circle-fill::before { content: "\f332"; } -.bi-exclamation-circle::before { content: "\f333"; } -.bi-exclamation-diamond-fill::before { content: "\f334"; } -.bi-exclamation-diamond::before { content: "\f335"; } -.bi-exclamation-octagon-fill::before { content: "\f336"; } -.bi-exclamation-octagon::before { content: "\f337"; } -.bi-exclamation-square-fill::before { content: "\f338"; } -.bi-exclamation-square::before { content: "\f339"; } -.bi-exclamation-triangle-fill::before { content: "\f33a"; } -.bi-exclamation-triangle::before { content: "\f33b"; } -.bi-exclamation::before { content: "\f33c"; } -.bi-exclude::before { content: "\f33d"; } -.bi-eye-fill::before { content: "\f33e"; } -.bi-eye-slash-fill::before { content: "\f33f"; } -.bi-eye-slash::before { content: "\f340"; } -.bi-eye::before { content: "\f341"; } -.bi-eyedropper::before { content: "\f342"; } -.bi-eyeglasses::before { content: "\f343"; } -.bi-facebook::before { content: "\f344"; } -.bi-file-arrow-down-fill::before { content: "\f345"; } -.bi-file-arrow-down::before { content: "\f346"; } -.bi-file-arrow-up-fill::before { content: "\f347"; } -.bi-file-arrow-up::before { content: "\f348"; } -.bi-file-bar-graph-fill::before { content: "\f349"; } -.bi-file-bar-graph::before { content: "\f34a"; } -.bi-file-binary-fill::before { content: "\f34b"; } -.bi-file-binary::before { content: "\f34c"; } -.bi-file-break-fill::before { content: "\f34d"; } -.bi-file-break::before { content: "\f34e"; } -.bi-file-check-fill::before { content: "\f34f"; } -.bi-file-check::before { content: "\f350"; } -.bi-file-code-fill::before { content: "\f351"; } -.bi-file-code::before { content: "\f352"; } -.bi-file-diff-fill::before { content: "\f353"; } -.bi-file-diff::before { content: "\f354"; } -.bi-file-earmark-arrow-down-fill::before { content: "\f355"; } -.bi-file-earmark-arrow-down::before { content: "\f356"; } -.bi-file-earmark-arrow-up-fill::before { content: "\f357"; } -.bi-file-earmark-arrow-up::before { content: "\f358"; } -.bi-file-earmark-bar-graph-fill::before { content: "\f359"; } -.bi-file-earmark-bar-graph::before { content: "\f35a"; } -.bi-file-earmark-binary-fill::before { content: "\f35b"; } -.bi-file-earmark-binary::before { content: "\f35c"; } -.bi-file-earmark-break-fill::before { content: "\f35d"; } -.bi-file-earmark-break::before { content: "\f35e"; } -.bi-file-earmark-check-fill::before { content: "\f35f"; } -.bi-file-earmark-check::before { content: "\f360"; } -.bi-file-earmark-code-fill::before { content: "\f361"; } -.bi-file-earmark-code::before { content: "\f362"; } -.bi-file-earmark-diff-fill::before { content: "\f363"; } -.bi-file-earmark-diff::before { content: "\f364"; } -.bi-file-earmark-easel-fill::before { content: "\f365"; } -.bi-file-earmark-easel::before { content: "\f366"; } -.bi-file-earmark-excel-fill::before { content: "\f367"; } -.bi-file-earmark-excel::before { content: "\f368"; } -.bi-file-earmark-fill::before { content: "\f369"; } -.bi-file-earmark-font-fill::before { content: "\f36a"; } -.bi-file-earmark-font::before { content: "\f36b"; } -.bi-file-earmark-image-fill::before { content: "\f36c"; } -.bi-file-earmark-image::before { content: "\f36d"; } -.bi-file-earmark-lock-fill::before { content: "\f36e"; } -.bi-file-earmark-lock::before { content: "\f36f"; } -.bi-file-earmark-lock2-fill::before { content: "\f370"; } -.bi-file-earmark-lock2::before { content: "\f371"; } -.bi-file-earmark-medical-fill::before { content: "\f372"; } -.bi-file-earmark-medical::before { content: "\f373"; } -.bi-file-earmark-minus-fill::before { content: "\f374"; } -.bi-file-earmark-minus::before { content: "\f375"; } -.bi-file-earmark-music-fill::before { content: "\f376"; } -.bi-file-earmark-music::before { content: "\f377"; } -.bi-file-earmark-person-fill::before { content: "\f378"; } -.bi-file-earmark-person::before { content: "\f379"; } -.bi-file-earmark-play-fill::before { content: "\f37a"; } -.bi-file-earmark-play::before { content: "\f37b"; } -.bi-file-earmark-plus-fill::before { content: "\f37c"; } -.bi-file-earmark-plus::before { content: "\f37d"; } -.bi-file-earmark-post-fill::before { content: "\f37e"; } -.bi-file-earmark-post::before { content: "\f37f"; } -.bi-file-earmark-ppt-fill::before { content: "\f380"; } -.bi-file-earmark-ppt::before { content: "\f381"; } -.bi-file-earmark-richtext-fill::before { content: "\f382"; } -.bi-file-earmark-richtext::before { content: "\f383"; } -.bi-file-earmark-ruled-fill::before { content: "\f384"; } -.bi-file-earmark-ruled::before { content: "\f385"; } -.bi-file-earmark-slides-fill::before { content: "\f386"; } -.bi-file-earmark-slides::before { content: "\f387"; } -.bi-file-earmark-spreadsheet-fill::before { content: "\f388"; } -.bi-file-earmark-spreadsheet::before { content: "\f389"; } -.bi-file-earmark-text-fill::before { content: "\f38a"; } -.bi-file-earmark-text::before { content: "\f38b"; } -.bi-file-earmark-word-fill::before { content: "\f38c"; } -.bi-file-earmark-word::before { content: "\f38d"; } -.bi-file-earmark-x-fill::before { content: "\f38e"; } -.bi-file-earmark-x::before { content: "\f38f"; } -.bi-file-earmark-zip-fill::before { content: "\f390"; } -.bi-file-earmark-zip::before { content: "\f391"; } -.bi-file-earmark::before { content: "\f392"; } -.bi-file-easel-fill::before { content: "\f393"; } -.bi-file-easel::before { content: "\f394"; } -.bi-file-excel-fill::before { content: "\f395"; } -.bi-file-excel::before { content: "\f396"; } -.bi-file-fill::before { content: "\f397"; } -.bi-file-font-fill::before { content: "\f398"; } -.bi-file-font::before { content: "\f399"; } -.bi-file-image-fill::before { content: "\f39a"; } -.bi-file-image::before { content: "\f39b"; } -.bi-file-lock-fill::before { content: "\f39c"; } -.bi-file-lock::before { content: "\f39d"; } -.bi-file-lock2-fill::before { content: "\f39e"; } -.bi-file-lock2::before { content: "\f39f"; } -.bi-file-medical-fill::before { content: "\f3a0"; } -.bi-file-medical::before { content: "\f3a1"; } -.bi-file-minus-fill::before { content: "\f3a2"; } -.bi-file-minus::before { content: "\f3a3"; } -.bi-file-music-fill::before { content: "\f3a4"; } -.bi-file-music::before { content: "\f3a5"; } -.bi-file-person-fill::before { content: "\f3a6"; } -.bi-file-person::before { content: "\f3a7"; } -.bi-file-play-fill::before { content: "\f3a8"; } -.bi-file-play::before { content: "\f3a9"; } -.bi-file-plus-fill::before { content: "\f3aa"; } -.bi-file-plus::before { content: "\f3ab"; } -.bi-file-post-fill::before { content: "\f3ac"; } -.bi-file-post::before { content: "\f3ad"; } -.bi-file-ppt-fill::before { content: "\f3ae"; } -.bi-file-ppt::before { content: "\f3af"; } -.bi-file-richtext-fill::before { content: "\f3b0"; } -.bi-file-richtext::before { content: "\f3b1"; } -.bi-file-ruled-fill::before { content: "\f3b2"; } -.bi-file-ruled::before { content: "\f3b3"; } -.bi-file-slides-fill::before { content: "\f3b4"; } -.bi-file-slides::before { content: "\f3b5"; } -.bi-file-spreadsheet-fill::before { content: "\f3b6"; } -.bi-file-spreadsheet::before { content: "\f3b7"; } -.bi-file-text-fill::before { content: "\f3b8"; } -.bi-file-text::before { content: "\f3b9"; } -.bi-file-word-fill::before { content: "\f3ba"; } -.bi-file-word::before { content: "\f3bb"; } -.bi-file-x-fill::before { content: "\f3bc"; } -.bi-file-x::before { content: "\f3bd"; } -.bi-file-zip-fill::before { content: "\f3be"; } -.bi-file-zip::before { content: "\f3bf"; } -.bi-file::before { content: "\f3c0"; } -.bi-files-alt::before { content: "\f3c1"; } -.bi-files::before { content: "\f3c2"; } -.bi-film::before { content: "\f3c3"; } -.bi-filter-circle-fill::before { content: "\f3c4"; } -.bi-filter-circle::before { content: "\f3c5"; } -.bi-filter-left::before { content: "\f3c6"; } -.bi-filter-right::before { content: "\f3c7"; } -.bi-filter-square-fill::before { content: "\f3c8"; } -.bi-filter-square::before { content: "\f3c9"; } -.bi-filter::before { content: "\f3ca"; } -.bi-flag-fill::before { content: "\f3cb"; } -.bi-flag::before { content: "\f3cc"; } -.bi-flower1::before { content: "\f3cd"; } -.bi-flower2::before { content: "\f3ce"; } -.bi-flower3::before { content: "\f3cf"; } -.bi-folder-check::before { content: "\f3d0"; } -.bi-folder-fill::before { content: "\f3d1"; } -.bi-folder-minus::before { content: "\f3d2"; } -.bi-folder-plus::before { content: "\f3d3"; } -.bi-folder-symlink-fill::before { content: "\f3d4"; } -.bi-folder-symlink::before { content: "\f3d5"; } -.bi-folder-x::before { content: "\f3d6"; } -.bi-folder::before { content: "\f3d7"; } -.bi-folder2-open::before { content: "\f3d8"; } -.bi-folder2::before { content: "\f3d9"; } -.bi-fonts::before { content: "\f3da"; } -.bi-forward-fill::before { content: "\f3db"; } -.bi-forward::before { content: "\f3dc"; } -.bi-front::before { content: "\f3dd"; } -.bi-fullscreen-exit::before { content: "\f3de"; } -.bi-fullscreen::before { content: "\f3df"; } -.bi-funnel-fill::before { content: "\f3e0"; } -.bi-funnel::before { content: "\f3e1"; } -.bi-gear-fill::before { content: "\f3e2"; } -.bi-gear-wide-connected::before { content: "\f3e3"; } -.bi-gear-wide::before { content: "\f3e4"; } -.bi-gear::before { content: "\f3e5"; } -.bi-gem::before { content: "\f3e6"; } -.bi-geo-alt-fill::before { content: "\f3e7"; } -.bi-geo-alt::before { content: "\f3e8"; } -.bi-geo-fill::before { content: "\f3e9"; } -.bi-geo::before { content: "\f3ea"; } -.bi-gift-fill::before { content: "\f3eb"; } -.bi-gift::before { content: "\f3ec"; } -.bi-github::before { content: "\f3ed"; } -.bi-globe::before { content: "\f3ee"; } -.bi-globe2::before { content: "\f3ef"; } -.bi-google::before { content: "\f3f0"; } -.bi-graph-down::before { content: "\f3f1"; } -.bi-graph-up::before { content: "\f3f2"; } -.bi-grid-1x2-fill::before { content: "\f3f3"; } -.bi-grid-1x2::before { content: "\f3f4"; } -.bi-grid-3x2-gap-fill::before { content: "\f3f5"; } -.bi-grid-3x2-gap::before { content: "\f3f6"; } -.bi-grid-3x2::before { content: "\f3f7"; } -.bi-grid-3x3-gap-fill::before { content: "\f3f8"; } -.bi-grid-3x3-gap::before { content: "\f3f9"; } -.bi-grid-3x3::before { content: "\f3fa"; } -.bi-grid-fill::before { content: "\f3fb"; } -.bi-grid::before { content: "\f3fc"; } -.bi-grip-horizontal::before { content: "\f3fd"; } -.bi-grip-vertical::before { content: "\f3fe"; } -.bi-hammer::before { content: "\f3ff"; } -.bi-hand-index-fill::before { content: "\f400"; } -.bi-hand-index-thumb-fill::before { content: "\f401"; } -.bi-hand-index-thumb::before { content: "\f402"; } -.bi-hand-index::before { content: "\f403"; } -.bi-hand-thumbs-down-fill::before { content: "\f404"; } -.bi-hand-thumbs-down::before { content: "\f405"; } -.bi-hand-thumbs-up-fill::before { content: "\f406"; } -.bi-hand-thumbs-up::before { content: "\f407"; } -.bi-handbag-fill::before { content: "\f408"; } -.bi-handbag::before { content: "\f409"; } -.bi-hash::before { content: "\f40a"; } -.bi-hdd-fill::before { content: "\f40b"; } -.bi-hdd-network-fill::before { content: "\f40c"; } -.bi-hdd-network::before { content: "\f40d"; } -.bi-hdd-rack-fill::before { content: "\f40e"; } -.bi-hdd-rack::before { content: "\f40f"; } -.bi-hdd-stack-fill::before { content: "\f410"; } -.bi-hdd-stack::before { content: "\f411"; } -.bi-hdd::before { content: "\f412"; } -.bi-headphones::before { content: "\f413"; } -.bi-headset::before { content: "\f414"; } -.bi-heart-fill::before { content: "\f415"; } -.bi-heart-half::before { content: "\f416"; } -.bi-heart::before { content: "\f417"; } -.bi-heptagon-fill::before { content: "\f418"; } -.bi-heptagon-half::before { content: "\f419"; } -.bi-heptagon::before { content: "\f41a"; } -.bi-hexagon-fill::before { content: "\f41b"; } -.bi-hexagon-half::before { content: "\f41c"; } -.bi-hexagon::before { content: "\f41d"; } -.bi-hourglass-bottom::before { content: "\f41e"; } -.bi-hourglass-split::before { content: "\f41f"; } -.bi-hourglass-top::before { content: "\f420"; } -.bi-hourglass::before { content: "\f421"; } -.bi-house-door-fill::before { content: "\f422"; } -.bi-house-door::before { content: "\f423"; } -.bi-house-fill::before { content: "\f424"; } -.bi-house::before { content: "\f425"; } -.bi-hr::before { content: "\f426"; } -.bi-hurricane::before { content: "\f427"; } -.bi-image-alt::before { content: "\f428"; } -.bi-image-fill::before { content: "\f429"; } -.bi-image::before { content: "\f42a"; } -.bi-images::before { content: "\f42b"; } -.bi-inbox-fill::before { content: "\f42c"; } -.bi-inbox::before { content: "\f42d"; } -.bi-inboxes-fill::before { content: "\f42e"; } -.bi-inboxes::before { content: "\f42f"; } -.bi-info-circle-fill::before { content: "\f430"; } -.bi-info-circle::before { content: "\f431"; } -.bi-info-square-fill::before { content: "\f432"; } -.bi-info-square::before { content: "\f433"; } -.bi-info::before { content: "\f434"; } -.bi-input-cursor-text::before { content: "\f435"; } -.bi-input-cursor::before { content: "\f436"; } -.bi-instagram::before { content: "\f437"; } -.bi-intersect::before { content: "\f438"; } -.bi-journal-album::before { content: "\f439"; } -.bi-journal-arrow-down::before { content: "\f43a"; } -.bi-journal-arrow-up::before { content: "\f43b"; } -.bi-journal-bookmark-fill::before { content: "\f43c"; } -.bi-journal-bookmark::before { content: "\f43d"; } -.bi-journal-check::before { content: "\f43e"; } -.bi-journal-code::before { content: "\f43f"; } -.bi-journal-medical::before { content: "\f440"; } -.bi-journal-minus::before { content: "\f441"; } -.bi-journal-plus::before { content: "\f442"; } -.bi-journal-richtext::before { content: "\f443"; } -.bi-journal-text::before { content: "\f444"; } -.bi-journal-x::before { content: "\f445"; } -.bi-journal::before { content: "\f446"; } -.bi-journals::before { content: "\f447"; } -.bi-joystick::before { content: "\f448"; } -.bi-justify-left::before { content: "\f449"; } -.bi-justify-right::before { content: "\f44a"; } -.bi-justify::before { content: "\f44b"; } -.bi-kanban-fill::before { content: "\f44c"; } -.bi-kanban::before { content: "\f44d"; } -.bi-key-fill::before { content: "\f44e"; } -.bi-key::before { content: "\f44f"; } -.bi-keyboard-fill::before { content: "\f450"; } -.bi-keyboard::before { content: "\f451"; } -.bi-ladder::before { content: "\f452"; } -.bi-lamp-fill::before { content: "\f453"; } -.bi-lamp::before { content: "\f454"; } -.bi-laptop-fill::before { content: "\f455"; } -.bi-laptop::before { content: "\f456"; } -.bi-layer-backward::before { content: "\f457"; } -.bi-layer-forward::before { content: "\f458"; } -.bi-layers-fill::before { content: "\f459"; } -.bi-layers-half::before { content: "\f45a"; } -.bi-layers::before { content: "\f45b"; } -.bi-layout-sidebar-inset-reverse::before { content: "\f45c"; } -.bi-layout-sidebar-inset::before { content: "\f45d"; } -.bi-layout-sidebar-reverse::before { content: "\f45e"; } -.bi-layout-sidebar::before { content: "\f45f"; } -.bi-layout-split::before { content: "\f460"; } -.bi-layout-text-sidebar-reverse::before { content: "\f461"; } -.bi-layout-text-sidebar::before { content: "\f462"; } -.bi-layout-text-window-reverse::before { content: "\f463"; } -.bi-layout-text-window::before { content: "\f464"; } -.bi-layout-three-columns::before { content: "\f465"; } -.bi-layout-wtf::before { content: "\f466"; } -.bi-life-preserver::before { content: "\f467"; } -.bi-lightbulb-fill::before { content: "\f468"; } -.bi-lightbulb-off-fill::before { content: "\f469"; } -.bi-lightbulb-off::before { content: "\f46a"; } -.bi-lightbulb::before { content: "\f46b"; } -.bi-lightning-charge-fill::before { content: "\f46c"; } -.bi-lightning-charge::before { content: "\f46d"; } -.bi-lightning-fill::before { content: "\f46e"; } -.bi-lightning::before { content: "\f46f"; } -.bi-link-45deg::before { content: "\f470"; } -.bi-link::before { content: "\f471"; } -.bi-linkedin::before { content: "\f472"; } -.bi-list-check::before { content: "\f473"; } -.bi-list-nested::before { content: "\f474"; } -.bi-list-ol::before { content: "\f475"; } -.bi-list-stars::before { content: "\f476"; } -.bi-list-task::before { content: "\f477"; } -.bi-list-ul::before { content: "\f478"; } -.bi-list::before { content: "\f479"; } -.bi-lock-fill::before { content: "\f47a"; } -.bi-lock::before { content: "\f47b"; } -.bi-mailbox::before { content: "\f47c"; } -.bi-mailbox2::before { content: "\f47d"; } -.bi-map-fill::before { content: "\f47e"; } -.bi-map::before { content: "\f47f"; } -.bi-markdown-fill::before { content: "\f480"; } -.bi-markdown::before { content: "\f481"; } -.bi-mask::before { content: "\f482"; } -.bi-megaphone-fill::before { content: "\f483"; } -.bi-megaphone::before { content: "\f484"; } -.bi-menu-app-fill::before { content: "\f485"; } -.bi-menu-app::before { content: "\f486"; } -.bi-menu-button-fill::before { content: "\f487"; } -.bi-menu-button-wide-fill::before { content: "\f488"; } -.bi-menu-button-wide::before { content: "\f489"; } -.bi-menu-button::before { content: "\f48a"; } -.bi-menu-down::before { content: "\f48b"; } -.bi-menu-up::before { content: "\f48c"; } -.bi-mic-fill::before { content: "\f48d"; } -.bi-mic-mute-fill::before { content: "\f48e"; } -.bi-mic-mute::before { content: "\f48f"; } -.bi-mic::before { content: "\f490"; } -.bi-minecart-loaded::before { content: "\f491"; } -.bi-minecart::before { content: "\f492"; } -.bi-moisture::before { content: "\f493"; } -.bi-moon-fill::before { content: "\f494"; } -.bi-moon-stars-fill::before { content: "\f495"; } -.bi-moon-stars::before { content: "\f496"; } -.bi-moon::before { content: "\f497"; } -.bi-mouse-fill::before { content: "\f498"; } -.bi-mouse::before { content: "\f499"; } -.bi-mouse2-fill::before { content: "\f49a"; } -.bi-mouse2::before { content: "\f49b"; } -.bi-mouse3-fill::before { content: "\f49c"; } -.bi-mouse3::before { content: "\f49d"; } -.bi-music-note-beamed::before { content: "\f49e"; } -.bi-music-note-list::before { content: "\f49f"; } -.bi-music-note::before { content: "\f4a0"; } -.bi-music-player-fill::before { content: "\f4a1"; } -.bi-music-player::before { content: "\f4a2"; } -.bi-newspaper::before { content: "\f4a3"; } -.bi-node-minus-fill::before { content: "\f4a4"; } -.bi-node-minus::before { content: "\f4a5"; } -.bi-node-plus-fill::before { content: "\f4a6"; } -.bi-node-plus::before { content: "\f4a7"; } -.bi-nut-fill::before { content: "\f4a8"; } -.bi-nut::before { content: "\f4a9"; } -.bi-octagon-fill::before { content: "\f4aa"; } -.bi-octagon-half::before { content: "\f4ab"; } -.bi-octagon::before { content: "\f4ac"; } -.bi-option::before { content: "\f4ad"; } -.bi-outlet::before { content: "\f4ae"; } -.bi-paint-bucket::before { content: "\f4af"; } -.bi-palette-fill::before { content: "\f4b0"; } -.bi-palette::before { content: "\f4b1"; } -.bi-palette2::before { content: "\f4b2"; } -.bi-paperclip::before { content: "\f4b3"; } -.bi-paragraph::before { content: "\f4b4"; } -.bi-patch-check-fill::before { content: "\f4b5"; } -.bi-patch-check::before { content: "\f4b6"; } -.bi-patch-exclamation-fill::before { content: "\f4b7"; } -.bi-patch-exclamation::before { content: "\f4b8"; } -.bi-patch-minus-fill::before { content: "\f4b9"; } -.bi-patch-minus::before { content: "\f4ba"; } -.bi-patch-plus-fill::before { content: "\f4bb"; } -.bi-patch-plus::before { content: "\f4bc"; } -.bi-patch-question-fill::before { content: "\f4bd"; } -.bi-patch-question::before { content: "\f4be"; } -.bi-pause-btn-fill::before { content: "\f4bf"; } -.bi-pause-btn::before { content: "\f4c0"; } -.bi-pause-circle-fill::before { content: "\f4c1"; } -.bi-pause-circle::before { content: "\f4c2"; } -.bi-pause-fill::before { content: "\f4c3"; } -.bi-pause::before { content: "\f4c4"; } -.bi-peace-fill::before { content: "\f4c5"; } -.bi-peace::before { content: "\f4c6"; } -.bi-pen-fill::before { content: "\f4c7"; } -.bi-pen::before { content: "\f4c8"; } -.bi-pencil-fill::before { content: "\f4c9"; } -.bi-pencil-square::before { content: "\f4ca"; } -.bi-pencil::before { content: "\f4cb"; } -.bi-pentagon-fill::before { content: "\f4cc"; } -.bi-pentagon-half::before { content: "\f4cd"; } -.bi-pentagon::before { content: "\f4ce"; } -.bi-people-fill::before { content: "\f4cf"; } -.bi-people::before { content: "\f4d0"; } -.bi-percent::before { content: "\f4d1"; } -.bi-person-badge-fill::before { content: "\f4d2"; } -.bi-person-badge::before { content: "\f4d3"; } -.bi-person-bounding-box::before { content: "\f4d4"; } -.bi-person-check-fill::before { content: "\f4d5"; } -.bi-person-check::before { content: "\f4d6"; } -.bi-person-circle::before { content: "\f4d7"; } -.bi-person-dash-fill::before { content: "\f4d8"; } -.bi-person-dash::before { content: "\f4d9"; } -.bi-person-fill::before { content: "\f4da"; } -.bi-person-lines-fill::before { content: "\f4db"; } -.bi-person-plus-fill::before { content: "\f4dc"; } -.bi-person-plus::before { content: "\f4dd"; } -.bi-person-square::before { content: "\f4de"; } -.bi-person-x-fill::before { content: "\f4df"; } -.bi-person-x::before { content: "\f4e0"; } -.bi-person::before { content: "\f4e1"; } -.bi-phone-fill::before { content: "\f4e2"; } -.bi-phone-landscape-fill::before { content: "\f4e3"; } -.bi-phone-landscape::before { content: "\f4e4"; } -.bi-phone-vibrate-fill::before { content: "\f4e5"; } -.bi-phone-vibrate::before { content: "\f4e6"; } -.bi-phone::before { content: "\f4e7"; } -.bi-pie-chart-fill::before { content: "\f4e8"; } -.bi-pie-chart::before { content: "\f4e9"; } -.bi-pin-angle-fill::before { content: "\f4ea"; } -.bi-pin-angle::before { content: "\f4eb"; } -.bi-pin-fill::before { content: "\f4ec"; } -.bi-pin::before { content: "\f4ed"; } -.bi-pip-fill::before { content: "\f4ee"; } -.bi-pip::before { content: "\f4ef"; } -.bi-play-btn-fill::before { content: "\f4f0"; } -.bi-play-btn::before { content: "\f4f1"; } -.bi-play-circle-fill::before { content: "\f4f2"; } -.bi-play-circle::before { content: "\f4f3"; } -.bi-play-fill::before { content: "\f4f4"; } -.bi-play::before { content: "\f4f5"; } -.bi-plug-fill::before { content: "\f4f6"; } -.bi-plug::before { content: "\f4f7"; } -.bi-plus-circle-dotted::before { content: "\f4f8"; } -.bi-plus-circle-fill::before { content: "\f4f9"; } -.bi-plus-circle::before { content: "\f4fa"; } -.bi-plus-square-dotted::before { content: "\f4fb"; } -.bi-plus-square-fill::before { content: "\f4fc"; } -.bi-plus-square::before { content: "\f4fd"; } -.bi-plus::before { content: "\f4fe"; } -.bi-power::before { content: "\f4ff"; } -.bi-printer-fill::before { content: "\f500"; } -.bi-printer::before { content: "\f501"; } -.bi-puzzle-fill::before { content: "\f502"; } -.bi-puzzle::before { content: "\f503"; } -.bi-question-circle-fill::before { content: "\f504"; } -.bi-question-circle::before { content: "\f505"; } -.bi-question-diamond-fill::before { content: "\f506"; } -.bi-question-diamond::before { content: "\f507"; } -.bi-question-octagon-fill::before { content: "\f508"; } -.bi-question-octagon::before { content: "\f509"; } -.bi-question-square-fill::before { content: "\f50a"; } -.bi-question-square::before { content: "\f50b"; } -.bi-question::before { content: "\f50c"; } -.bi-rainbow::before { content: "\f50d"; } -.bi-receipt-cutoff::before { content: "\f50e"; } -.bi-receipt::before { content: "\f50f"; } -.bi-reception-0::before { content: "\f510"; } -.bi-reception-1::before { content: "\f511"; } -.bi-reception-2::before { content: "\f512"; } -.bi-reception-3::before { content: "\f513"; } -.bi-reception-4::before { content: "\f514"; } -.bi-record-btn-fill::before { content: "\f515"; } -.bi-record-btn::before { content: "\f516"; } -.bi-record-circle-fill::before { content: "\f517"; } -.bi-record-circle::before { content: "\f518"; } -.bi-record-fill::before { content: "\f519"; } -.bi-record::before { content: "\f51a"; } -.bi-record2-fill::before { content: "\f51b"; } -.bi-record2::before { content: "\f51c"; } -.bi-reply-all-fill::before { content: "\f51d"; } -.bi-reply-all::before { content: "\f51e"; } -.bi-reply-fill::before { content: "\f51f"; } -.bi-reply::before { content: "\f520"; } -.bi-rss-fill::before { content: "\f521"; } -.bi-rss::before { content: "\f522"; } -.bi-rulers::before { content: "\f523"; } -.bi-save-fill::before { content: "\f524"; } -.bi-save::before { content: "\f525"; } -.bi-save2-fill::before { content: "\f526"; } -.bi-save2::before { content: "\f527"; } -.bi-scissors::before { content: "\f528"; } -.bi-screwdriver::before { content: "\f529"; } -.bi-search::before { content: "\f52a"; } -.bi-segmented-nav::before { content: "\f52b"; } -.bi-server::before { content: "\f52c"; } -.bi-share-fill::before { content: "\f52d"; } -.bi-share::before { content: "\f52e"; } -.bi-shield-check::before { content: "\f52f"; } -.bi-shield-exclamation::before { content: "\f530"; } -.bi-shield-fill-check::before { content: "\f531"; } -.bi-shield-fill-exclamation::before { content: "\f532"; } -.bi-shield-fill-minus::before { content: "\f533"; } -.bi-shield-fill-plus::before { content: "\f534"; } -.bi-shield-fill-x::before { content: "\f535"; } -.bi-shield-fill::before { content: "\f536"; } -.bi-shield-lock-fill::before { content: "\f537"; } -.bi-shield-lock::before { content: "\f538"; } -.bi-shield-minus::before { content: "\f539"; } -.bi-shield-plus::before { content: "\f53a"; } -.bi-shield-shaded::before { content: "\f53b"; } -.bi-shield-slash-fill::before { content: "\f53c"; } -.bi-shield-slash::before { content: "\f53d"; } -.bi-shield-x::before { content: "\f53e"; } -.bi-shield::before { content: "\f53f"; } -.bi-shift-fill::before { content: "\f540"; } -.bi-shift::before { content: "\f541"; } -.bi-shop-window::before { content: "\f542"; } -.bi-shop::before { content: "\f543"; } -.bi-shuffle::before { content: "\f544"; } -.bi-signpost-2-fill::before { content: "\f545"; } -.bi-signpost-2::before { content: "\f546"; } -.bi-signpost-fill::before { content: "\f547"; } -.bi-signpost-split-fill::before { content: "\f548"; } -.bi-signpost-split::before { content: "\f549"; } -.bi-signpost::before { content: "\f54a"; } -.bi-sim-fill::before { content: "\f54b"; } -.bi-sim::before { content: "\f54c"; } -.bi-skip-backward-btn-fill::before { content: "\f54d"; } -.bi-skip-backward-btn::before { content: "\f54e"; } -.bi-skip-backward-circle-fill::before { content: "\f54f"; } -.bi-skip-backward-circle::before { content: "\f550"; } -.bi-skip-backward-fill::before { content: "\f551"; } -.bi-skip-backward::before { content: "\f552"; } -.bi-skip-end-btn-fill::before { content: "\f553"; } -.bi-skip-end-btn::before { content: "\f554"; } -.bi-skip-end-circle-fill::before { content: "\f555"; } -.bi-skip-end-circle::before { content: "\f556"; } -.bi-skip-end-fill::before { content: "\f557"; } -.bi-skip-end::before { content: "\f558"; } -.bi-skip-forward-btn-fill::before { content: "\f559"; } -.bi-skip-forward-btn::before { content: "\f55a"; } -.bi-skip-forward-circle-fill::before { content: "\f55b"; } -.bi-skip-forward-circle::before { content: "\f55c"; } -.bi-skip-forward-fill::before { content: "\f55d"; } -.bi-skip-forward::before { content: "\f55e"; } -.bi-skip-start-btn-fill::before { content: "\f55f"; } -.bi-skip-start-btn::before { content: "\f560"; } -.bi-skip-start-circle-fill::before { content: "\f561"; } -.bi-skip-start-circle::before { content: "\f562"; } -.bi-skip-start-fill::before { content: "\f563"; } -.bi-skip-start::before { content: "\f564"; } -.bi-slack::before { content: "\f565"; } -.bi-slash-circle-fill::before { content: "\f566"; } -.bi-slash-circle::before { content: "\f567"; } -.bi-slash-square-fill::before { content: "\f568"; } -.bi-slash-square::before { content: "\f569"; } -.bi-slash::before { content: "\f56a"; } -.bi-sliders::before { content: "\f56b"; } -.bi-smartwatch::before { content: "\f56c"; } -.bi-snow::before { content: "\f56d"; } -.bi-snow2::before { content: "\f56e"; } -.bi-snow3::before { content: "\f56f"; } -.bi-sort-alpha-down-alt::before { content: "\f570"; } -.bi-sort-alpha-down::before { content: "\f571"; } -.bi-sort-alpha-up-alt::before { content: "\f572"; } -.bi-sort-alpha-up::before { content: "\f573"; } -.bi-sort-down-alt::before { content: "\f574"; } -.bi-sort-down::before { content: "\f575"; } -.bi-sort-numeric-down-alt::before { content: "\f576"; } -.bi-sort-numeric-down::before { content: "\f577"; } -.bi-sort-numeric-up-alt::before { content: "\f578"; } -.bi-sort-numeric-up::before { content: "\f579"; } -.bi-sort-up-alt::before { content: "\f57a"; } -.bi-sort-up::before { content: "\f57b"; } -.bi-soundwave::before { content: "\f57c"; } -.bi-speaker-fill::before { content: "\f57d"; } -.bi-speaker::before { content: "\f57e"; } -.bi-speedometer::before { content: "\f57f"; } -.bi-speedometer2::before { content: "\f580"; } -.bi-spellcheck::before { content: "\f581"; } -.bi-square-fill::before { content: "\f582"; } -.bi-square-half::before { content: "\f583"; } -.bi-square::before { content: "\f584"; } -.bi-stack::before { content: "\f585"; } -.bi-star-fill::before { content: "\f586"; } -.bi-star-half::before { content: "\f587"; } -.bi-star::before { content: "\f588"; } -.bi-stars::before { content: "\f589"; } -.bi-stickies-fill::before { content: "\f58a"; } -.bi-stickies::before { content: "\f58b"; } -.bi-sticky-fill::before { content: "\f58c"; } -.bi-sticky::before { content: "\f58d"; } -.bi-stop-btn-fill::before { content: "\f58e"; } -.bi-stop-btn::before { content: "\f58f"; } -.bi-stop-circle-fill::before { content: "\f590"; } -.bi-stop-circle::before { content: "\f591"; } -.bi-stop-fill::before { content: "\f592"; } -.bi-stop::before { content: "\f593"; } -.bi-stoplights-fill::before { content: "\f594"; } -.bi-stoplights::before { content: "\f595"; } -.bi-stopwatch-fill::before { content: "\f596"; } -.bi-stopwatch::before { content: "\f597"; } -.bi-subtract::before { content: "\f598"; } -.bi-suit-club-fill::before { content: "\f599"; } -.bi-suit-club::before { content: "\f59a"; } -.bi-suit-diamond-fill::before { content: "\f59b"; } -.bi-suit-diamond::before { content: "\f59c"; } -.bi-suit-heart-fill::before { content: "\f59d"; } -.bi-suit-heart::before { content: "\f59e"; } -.bi-suit-spade-fill::before { content: "\f59f"; } -.bi-suit-spade::before { content: "\f5a0"; } -.bi-sun-fill::before { content: "\f5a1"; } -.bi-sun::before { content: "\f5a2"; } -.bi-sunglasses::before { content: "\f5a3"; } -.bi-sunrise-fill::before { content: "\f5a4"; } -.bi-sunrise::before { content: "\f5a5"; } -.bi-sunset-fill::before { content: "\f5a6"; } -.bi-sunset::before { content: "\f5a7"; } -.bi-symmetry-horizontal::before { content: "\f5a8"; } -.bi-symmetry-vertical::before { content: "\f5a9"; } -.bi-table::before { content: "\f5aa"; } -.bi-tablet-fill::before { content: "\f5ab"; } -.bi-tablet-landscape-fill::before { content: "\f5ac"; } -.bi-tablet-landscape::before { content: "\f5ad"; } -.bi-tablet::before { content: "\f5ae"; } -.bi-tag-fill::before { content: "\f5af"; } -.bi-tag::before { content: "\f5b0"; } -.bi-tags-fill::before { content: "\f5b1"; } -.bi-tags::before { content: "\f5b2"; } -.bi-telegram::before { content: "\f5b3"; } -.bi-telephone-fill::before { content: "\f5b4"; } -.bi-telephone-forward-fill::before { content: "\f5b5"; } -.bi-telephone-forward::before { content: "\f5b6"; } -.bi-telephone-inbound-fill::before { content: "\f5b7"; } -.bi-telephone-inbound::before { content: "\f5b8"; } -.bi-telephone-minus-fill::before { content: "\f5b9"; } -.bi-telephone-minus::before { content: "\f5ba"; } -.bi-telephone-outbound-fill::before { content: "\f5bb"; } -.bi-telephone-outbound::before { content: "\f5bc"; } -.bi-telephone-plus-fill::before { content: "\f5bd"; } -.bi-telephone-plus::before { content: "\f5be"; } -.bi-telephone-x-fill::before { content: "\f5bf"; } -.bi-telephone-x::before { content: "\f5c0"; } -.bi-telephone::before { content: "\f5c1"; } -.bi-terminal-fill::before { content: "\f5c2"; } -.bi-terminal::before { content: "\f5c3"; } -.bi-text-center::before { content: "\f5c4"; } -.bi-text-indent-left::before { content: "\f5c5"; } -.bi-text-indent-right::before { content: "\f5c6"; } -.bi-text-left::before { content: "\f5c7"; } -.bi-text-paragraph::before { content: "\f5c8"; } -.bi-text-right::before { content: "\f5c9"; } -.bi-textarea-resize::before { content: "\f5ca"; } -.bi-textarea-t::before { content: "\f5cb"; } -.bi-textarea::before { content: "\f5cc"; } -.bi-thermometer-half::before { content: "\f5cd"; } -.bi-thermometer-high::before { content: "\f5ce"; } -.bi-thermometer-low::before { content: "\f5cf"; } -.bi-thermometer-snow::before { content: "\f5d0"; } -.bi-thermometer-sun::before { content: "\f5d1"; } -.bi-thermometer::before { content: "\f5d2"; } -.bi-three-dots-vertical::before { content: "\f5d3"; } -.bi-three-dots::before { content: "\f5d4"; } -.bi-toggle-off::before { content: "\f5d5"; } -.bi-toggle-on::before { content: "\f5d6"; } -.bi-toggle2-off::before { content: "\f5d7"; } -.bi-toggle2-on::before { content: "\f5d8"; } -.bi-toggles::before { content: "\f5d9"; } -.bi-toggles2::before { content: "\f5da"; } -.bi-tools::before { content: "\f5db"; } -.bi-tornado::before { content: "\f5dc"; } -.bi-trash-fill::before { content: "\f5dd"; } -.bi-trash::before { content: "\f5de"; } -.bi-trash2-fill::before { content: "\f5df"; } -.bi-trash2::before { content: "\f5e0"; } -.bi-tree-fill::before { content: "\f5e1"; } -.bi-tree::before { content: "\f5e2"; } -.bi-triangle-fill::before { content: "\f5e3"; } -.bi-triangle-half::before { content: "\f5e4"; } -.bi-triangle::before { content: "\f5e5"; } -.bi-trophy-fill::before { content: "\f5e6"; } -.bi-trophy::before { content: "\f5e7"; } -.bi-tropical-storm::before { content: "\f5e8"; } -.bi-truck-flatbed::before { content: "\f5e9"; } -.bi-truck::before { content: "\f5ea"; } -.bi-tsunami::before { content: "\f5eb"; } -.bi-tv-fill::before { content: "\f5ec"; } -.bi-tv::before { content: "\f5ed"; } -.bi-twitch::before { content: "\f5ee"; } -.bi-twitter::before { content: "\f5ef"; } -.bi-type-bold::before { content: "\f5f0"; } -.bi-type-h1::before { content: "\f5f1"; } -.bi-type-h2::before { content: "\f5f2"; } -.bi-type-h3::before { content: "\f5f3"; } -.bi-type-italic::before { content: "\f5f4"; } -.bi-type-strikethrough::before { content: "\f5f5"; } -.bi-type-underline::before { content: "\f5f6"; } -.bi-type::before { content: "\f5f7"; } -.bi-ui-checks-grid::before { content: "\f5f8"; } -.bi-ui-checks::before { content: "\f5f9"; } -.bi-ui-radios-grid::before { content: "\f5fa"; } -.bi-ui-radios::before { content: "\f5fb"; } -.bi-umbrella-fill::before { content: "\f5fc"; } -.bi-umbrella::before { content: "\f5fd"; } -.bi-union::before { content: "\f5fe"; } -.bi-unlock-fill::before { content: "\f5ff"; } -.bi-unlock::before { content: "\f600"; } -.bi-upc-scan::before { content: "\f601"; } -.bi-upc::before { content: "\f602"; } -.bi-upload::before { content: "\f603"; } -.bi-vector-pen::before { content: "\f604"; } -.bi-view-list::before { content: "\f605"; } -.bi-view-stacked::before { content: "\f606"; } -.bi-vinyl-fill::before { content: "\f607"; } -.bi-vinyl::before { content: "\f608"; } -.bi-voicemail::before { content: "\f609"; } -.bi-volume-down-fill::before { content: "\f60a"; } -.bi-volume-down::before { content: "\f60b"; } -.bi-volume-mute-fill::before { content: "\f60c"; } -.bi-volume-mute::before { content: "\f60d"; } -.bi-volume-off-fill::before { content: "\f60e"; } -.bi-volume-off::before { content: "\f60f"; } -.bi-volume-up-fill::before { content: "\f610"; } -.bi-volume-up::before { content: "\f611"; } -.bi-vr::before { content: "\f612"; } -.bi-wallet-fill::before { content: "\f613"; } -.bi-wallet::before { content: "\f614"; } -.bi-wallet2::before { content: "\f615"; } -.bi-watch::before { content: "\f616"; } -.bi-water::before { content: "\f617"; } -.bi-whatsapp::before { content: "\f618"; } -.bi-wifi-1::before { content: "\f619"; } -.bi-wifi-2::before { content: "\f61a"; } -.bi-wifi-off::before { content: "\f61b"; } -.bi-wifi::before { content: "\f61c"; } -.bi-wind::before { content: "\f61d"; } -.bi-window-dock::before { content: "\f61e"; } -.bi-window-sidebar::before { content: "\f61f"; } -.bi-window::before { content: "\f620"; } -.bi-wrench::before { content: "\f621"; } -.bi-x-circle-fill::before { content: "\f622"; } -.bi-x-circle::before { content: "\f623"; } -.bi-x-diamond-fill::before { content: "\f624"; } -.bi-x-diamond::before { content: "\f625"; } -.bi-x-octagon-fill::before { content: "\f626"; } -.bi-x-octagon::before { content: "\f627"; } -.bi-x-square-fill::before { content: "\f628"; } -.bi-x-square::before { content: "\f629"; } -.bi-x::before { content: "\f62a"; } -.bi-youtube::before { content: "\f62b"; } -.bi-zoom-in::before { content: "\f62c"; } -.bi-zoom-out::before { content: "\f62d"; } -.bi-bank::before { content: "\f62e"; } -.bi-bank2::before { content: "\f62f"; } -.bi-bell-slash-fill::before { content: "\f630"; } -.bi-bell-slash::before { content: "\f631"; } -.bi-cash-coin::before { content: "\f632"; } -.bi-check-lg::before { content: "\f633"; } -.bi-coin::before { content: "\f634"; } -.bi-currency-bitcoin::before { content: "\f635"; } -.bi-currency-dollar::before { content: "\f636"; } -.bi-currency-euro::before { content: "\f637"; } -.bi-currency-exchange::before { content: "\f638"; } -.bi-currency-pound::before { content: "\f639"; } -.bi-currency-yen::before { content: "\f63a"; } -.bi-dash-lg::before { content: "\f63b"; } -.bi-exclamation-lg::before { content: "\f63c"; } -.bi-file-earmark-pdf-fill::before { content: "\f63d"; } -.bi-file-earmark-pdf::before { content: "\f63e"; } -.bi-file-pdf-fill::before { content: "\f63f"; } -.bi-file-pdf::before { content: "\f640"; } -.bi-gender-ambiguous::before { content: "\f641"; } -.bi-gender-female::before { content: "\f642"; } -.bi-gender-male::before { content: "\f643"; } -.bi-gender-trans::before { content: "\f644"; } -.bi-headset-vr::before { content: "\f645"; } -.bi-info-lg::before { content: "\f646"; } -.bi-mastodon::before { content: "\f647"; } -.bi-messenger::before { content: "\f648"; } -.bi-piggy-bank-fill::before { content: "\f649"; } -.bi-piggy-bank::before { content: "\f64a"; } -.bi-pin-map-fill::before { content: "\f64b"; } -.bi-pin-map::before { content: "\f64c"; } -.bi-plus-lg::before { content: "\f64d"; } -.bi-question-lg::before { content: "\f64e"; } -.bi-recycle::before { content: "\f64f"; } -.bi-reddit::before { content: "\f650"; } -.bi-safe-fill::before { content: "\f651"; } -.bi-safe2-fill::before { content: "\f652"; } -.bi-safe2::before { content: "\f653"; } -.bi-sd-card-fill::before { content: "\f654"; } -.bi-sd-card::before { content: "\f655"; } -.bi-skype::before { content: "\f656"; } -.bi-slash-lg::before { content: "\f657"; } -.bi-translate::before { content: "\f658"; } -.bi-x-lg::before { content: "\f659"; } -.bi-safe::before { content: "\f65a"; } -.bi-apple::before { content: "\f65b"; } -.bi-microsoft::before { content: "\f65d"; } -.bi-windows::before { content: "\f65e"; } -.bi-behance::before { content: "\f65c"; } -.bi-dribbble::before { content: "\f65f"; } -.bi-line::before { content: "\f660"; } -.bi-medium::before { content: "\f661"; } -.bi-paypal::before { content: "\f662"; } -.bi-pinterest::before { content: "\f663"; } -.bi-signal::before { content: "\f664"; } -.bi-snapchat::before { content: "\f665"; } -.bi-spotify::before { content: "\f666"; } -.bi-stack-overflow::before { content: "\f667"; } -.bi-strava::before { content: "\f668"; } -.bi-wordpress::before { content: "\f669"; } -.bi-vimeo::before { content: "\f66a"; } -.bi-activity::before { content: "\f66b"; } -.bi-easel2-fill::before { content: "\f66c"; } -.bi-easel2::before { content: "\f66d"; } -.bi-easel3-fill::before { content: "\f66e"; } -.bi-easel3::before { content: "\f66f"; } -.bi-fan::before { content: "\f670"; } -.bi-fingerprint::before { content: "\f671"; } -.bi-graph-down-arrow::before { content: "\f672"; } -.bi-graph-up-arrow::before { content: "\f673"; } -.bi-hypnotize::before { content: "\f674"; } -.bi-magic::before { content: "\f675"; } -.bi-person-rolodex::before { content: "\f676"; } -.bi-person-video::before { content: "\f677"; } -.bi-person-video2::before { content: "\f678"; } -.bi-person-video3::before { content: "\f679"; } -.bi-person-workspace::before { content: "\f67a"; } -.bi-radioactive::before { content: "\f67b"; } -.bi-webcam-fill::before { content: "\f67c"; } -.bi-webcam::before { content: "\f67d"; } -.bi-yin-yang::before { content: "\f67e"; } -.bi-bandaid-fill::before { content: "\f680"; } -.bi-bandaid::before { content: "\f681"; } -.bi-bluetooth::before { content: "\f682"; } -.bi-body-text::before { content: "\f683"; } -.bi-boombox::before { content: "\f684"; } -.bi-boxes::before { content: "\f685"; } -.bi-dpad-fill::before { content: "\f686"; } -.bi-dpad::before { content: "\f687"; } -.bi-ear-fill::before { content: "\f688"; } -.bi-ear::before { content: "\f689"; } -.bi-envelope-check-1::before { content: "\f68a"; } -.bi-envelope-check-fill::before { content: "\f68b"; } -.bi-envelope-check::before { content: "\f68c"; } -.bi-envelope-dash-1::before { content: "\f68d"; } -.bi-envelope-dash-fill::before { content: "\f68e"; } -.bi-envelope-dash::before { content: "\f68f"; } -.bi-envelope-exclamation-1::before { content: "\f690"; } -.bi-envelope-exclamation-fill::before { content: "\f691"; } -.bi-envelope-exclamation::before { content: "\f692"; } -.bi-envelope-plus-fill::before { content: "\f693"; } -.bi-envelope-plus::before { content: "\f694"; } -.bi-envelope-slash-1::before { content: "\f695"; } -.bi-envelope-slash-fill::before { content: "\f696"; } -.bi-envelope-slash::before { content: "\f697"; } -.bi-envelope-x-1::before { content: "\f698"; } -.bi-envelope-x-fill::before { content: "\f699"; } -.bi-envelope-x::before { content: "\f69a"; } -.bi-explicit-fill::before { content: "\f69b"; } -.bi-explicit::before { content: "\f69c"; } -.bi-git::before { content: "\f69d"; } -.bi-infinity::before { content: "\f69e"; } -.bi-list-columns-reverse::before { content: "\f69f"; } -.bi-list-columns::before { content: "\f6a0"; } -.bi-meta::before { content: "\f6a1"; } -.bi-mortorboard-fill::before { content: "\f6a2"; } -.bi-mortorboard::before { content: "\f6a3"; } -.bi-nintendo-switch::before { content: "\f6a4"; } -.bi-pc-display-horizontal::before { content: "\f6a5"; } -.bi-pc-display::before { content: "\f6a6"; } -.bi-pc-horizontal::before { content: "\f6a7"; } -.bi-pc::before { content: "\f6a8"; } -.bi-playstation::before { content: "\f6a9"; } -.bi-plus-slash-minus::before { content: "\f6aa"; } -.bi-projector-fill::before { content: "\f6ab"; } -.bi-projector::before { content: "\f6ac"; } -.bi-qr-code-scan::before { content: "\f6ad"; } -.bi-qr-code::before { content: "\f6ae"; } -.bi-quora::before { content: "\f6af"; } -.bi-quote::before { content: "\f6b0"; } -.bi-robot::before { content: "\f6b1"; } -.bi-send-check-fill::before { content: "\f6b2"; } -.bi-send-check::before { content: "\f6b3"; } -.bi-send-dash-fill::before { content: "\f6b4"; } -.bi-send-dash::before { content: "\f6b5"; } -.bi-send-exclamation-1::before { content: "\f6b6"; } -.bi-send-exclamation-fill::before { content: "\f6b7"; } -.bi-send-exclamation::before { content: "\f6b8"; } -.bi-send-fill::before { content: "\f6b9"; } -.bi-send-plus-fill::before { content: "\f6ba"; } -.bi-send-plus::before { content: "\f6bb"; } -.bi-send-slash-fill::before { content: "\f6bc"; } -.bi-send-slash::before { content: "\f6bd"; } -.bi-send-x-fill::before { content: "\f6be"; } -.bi-send-x::before { content: "\f6bf"; } -.bi-send::before { content: "\f6c0"; } -.bi-steam::before { content: "\f6c1"; } -.bi-terminal-dash-1::before { content: "\f6c2"; } -.bi-terminal-dash::before { content: "\f6c3"; } -.bi-terminal-plus::before { content: "\f6c4"; } -.bi-terminal-split::before { content: "\f6c5"; } -.bi-ticket-detailed-fill::before { content: "\f6c6"; } -.bi-ticket-detailed::before { content: "\f6c7"; } -.bi-ticket-fill::before { content: "\f6c8"; } -.bi-ticket-perforated-fill::before { content: "\f6c9"; } -.bi-ticket-perforated::before { content: "\f6ca"; } -.bi-ticket::before { content: "\f6cb"; } -.bi-tiktok::before { content: "\f6cc"; } -.bi-window-dash::before { content: "\f6cd"; } -.bi-window-desktop::before { content: "\f6ce"; } -.bi-window-fullscreen::before { content: "\f6cf"; } -.bi-window-plus::before { content: "\f6d0"; } -.bi-window-split::before { content: "\f6d1"; } -.bi-window-stack::before { content: "\f6d2"; } -.bi-window-x::before { content: "\f6d3"; } -.bi-xbox::before { content: "\f6d4"; } -.bi-ethernet::before { content: "\f6d5"; } -.bi-hdmi-fill::before { content: "\f6d6"; } -.bi-hdmi::before { content: "\f6d7"; } -.bi-usb-c-fill::before { content: "\f6d8"; } -.bi-usb-c::before { content: "\f6d9"; } -.bi-usb-fill::before { content: "\f6da"; } -.bi-usb-plug-fill::before { content: "\f6db"; } -.bi-usb-plug::before { content: "\f6dc"; } -.bi-usb-symbol::before { content: "\f6dd"; } -.bi-usb::before { content: "\f6de"; } -.bi-boombox-fill::before { content: "\f6df"; } -.bi-displayport-1::before { content: "\f6e0"; } -.bi-displayport::before { content: "\f6e1"; } -.bi-gpu-card::before { content: "\f6e2"; } -.bi-memory::before { content: "\f6e3"; } -.bi-modem-fill::before { content: "\f6e4"; } -.bi-modem::before { content: "\f6e5"; } -.bi-motherboard-fill::before { content: "\f6e6"; } -.bi-motherboard::before { content: "\f6e7"; } -.bi-optical-audio-fill::before { content: "\f6e8"; } -.bi-optical-audio::before { content: "\f6e9"; } -.bi-pci-card::before { content: "\f6ea"; } -.bi-router-fill::before { content: "\f6eb"; } -.bi-router::before { content: "\f6ec"; } -.bi-ssd-fill::before { content: "\f6ed"; } -.bi-ssd::before { content: "\f6ee"; } -.bi-thunderbolt-fill::before { content: "\f6ef"; } -.bi-thunderbolt::before { content: "\f6f0"; } -.bi-usb-drive-fill::before { content: "\f6f1"; } -.bi-usb-drive::before { content: "\f6f2"; } -.bi-usb-micro-fill::before { content: "\f6f3"; } -.bi-usb-micro::before { content: "\f6f4"; } -.bi-usb-mini-fill::before { content: "\f6f5"; } -.bi-usb-mini::before { content: "\f6f6"; } -.bi-cloud-haze2::before { content: "\f6f7"; } -.bi-device-hdd-fill::before { content: "\f6f8"; } -.bi-device-hdd::before { content: "\f6f9"; } -.bi-device-ssd-fill::before { content: "\f6fa"; } -.bi-device-ssd::before { content: "\f6fb"; } -.bi-displayport-fill::before { content: "\f6fc"; } -.bi-mortarboard-fill::before { content: "\f6fd"; } -.bi-mortarboard::before { content: "\f6fe"; } -.bi-terminal-x::before { content: "\f6ff"; } -.bi-arrow-through-heart-fill::before { content: "\f700"; } -.bi-arrow-through-heart::before { content: "\f701"; } -.bi-badge-sd-fill::before { content: "\f702"; } -.bi-badge-sd::before { content: "\f703"; } -.bi-bag-heart-fill::before { content: "\f704"; } -.bi-bag-heart::before { content: "\f705"; } -.bi-balloon-fill::before { content: "\f706"; } -.bi-balloon-heart-fill::before { content: "\f707"; } -.bi-balloon-heart::before { content: "\f708"; } -.bi-balloon::before { content: "\f709"; } -.bi-box2-fill::before { content: "\f70a"; } -.bi-box2-heart-fill::before { content: "\f70b"; } -.bi-box2-heart::before { content: "\f70c"; } -.bi-box2::before { content: "\f70d"; } -.bi-braces-asterisk::before { content: "\f70e"; } -.bi-calendar-heart-fill::before { content: "\f70f"; } -.bi-calendar-heart::before { content: "\f710"; } -.bi-calendar2-heart-fill::before { content: "\f711"; } -.bi-calendar2-heart::before { content: "\f712"; } -.bi-chat-heart-fill::before { content: "\f713"; } -.bi-chat-heart::before { content: "\f714"; } -.bi-chat-left-heart-fill::before { content: "\f715"; } -.bi-chat-left-heart::before { content: "\f716"; } -.bi-chat-right-heart-fill::before { content: "\f717"; } -.bi-chat-right-heart::before { content: "\f718"; } -.bi-chat-square-heart-fill::before { content: "\f719"; } -.bi-chat-square-heart::before { content: "\f71a"; } -.bi-clipboard-check-fill::before { content: "\f71b"; } -.bi-clipboard-data-fill::before { content: "\f71c"; } -.bi-clipboard-fill::before { content: "\f71d"; } -.bi-clipboard-heart-fill::before { content: "\f71e"; } -.bi-clipboard-heart::before { content: "\f71f"; } -.bi-clipboard-minus-fill::before { content: "\f720"; } -.bi-clipboard-plus-fill::before { content: "\f721"; } -.bi-clipboard-pulse::before { content: "\f722"; } -.bi-clipboard-x-fill::before { content: "\f723"; } -.bi-clipboard2-check-fill::before { content: "\f724"; } -.bi-clipboard2-check::before { content: "\f725"; } -.bi-clipboard2-data-fill::before { content: "\f726"; } -.bi-clipboard2-data::before { content: "\f727"; } -.bi-clipboard2-fill::before { content: "\f728"; } -.bi-clipboard2-heart-fill::before { content: "\f729"; } -.bi-clipboard2-heart::before { content: "\f72a"; } -.bi-clipboard2-minus-fill::before { content: "\f72b"; } -.bi-clipboard2-minus::before { content: "\f72c"; } -.bi-clipboard2-plus-fill::before { content: "\f72d"; } -.bi-clipboard2-plus::before { content: "\f72e"; } -.bi-clipboard2-pulse-fill::before { content: "\f72f"; } -.bi-clipboard2-pulse::before { content: "\f730"; } -.bi-clipboard2-x-fill::before { content: "\f731"; } -.bi-clipboard2-x::before { content: "\f732"; } -.bi-clipboard2::before { content: "\f733"; } -.bi-emoji-kiss-fill::before { content: "\f734"; } -.bi-emoji-kiss::before { content: "\f735"; } -.bi-envelope-heart-fill::before { content: "\f736"; } -.bi-envelope-heart::before { content: "\f737"; } -.bi-envelope-open-heart-fill::before { content: "\f738"; } -.bi-envelope-open-heart::before { content: "\f739"; } -.bi-envelope-paper-fill::before { content: "\f73a"; } -.bi-envelope-paper-heart-fill::before { content: "\f73b"; } -.bi-envelope-paper-heart::before { content: "\f73c"; } -.bi-envelope-paper::before { content: "\f73d"; } -.bi-filetype-aac::before { content: "\f73e"; } -.bi-filetype-ai::before { content: "\f73f"; } -.bi-filetype-bmp::before { content: "\f740"; } -.bi-filetype-cs::before { content: "\f741"; } -.bi-filetype-css::before { content: "\f742"; } -.bi-filetype-csv::before { content: "\f743"; } -.bi-filetype-doc::before { content: "\f744"; } -.bi-filetype-docx::before { content: "\f745"; } -.bi-filetype-exe::before { content: "\f746"; } -.bi-filetype-gif::before { content: "\f747"; } -.bi-filetype-heic::before { content: "\f748"; } -.bi-filetype-html::before { content: "\f749"; } -.bi-filetype-java::before { content: "\f74a"; } -.bi-filetype-jpg::before { content: "\f74b"; } -.bi-filetype-js::before { content: "\f74c"; } -.bi-filetype-jsx::before { content: "\f74d"; } -.bi-filetype-key::before { content: "\f74e"; } -.bi-filetype-m4p::before { content: "\f74f"; } -.bi-filetype-md::before { content: "\f750"; } -.bi-filetype-mdx::before { content: "\f751"; } -.bi-filetype-mov::before { content: "\f752"; } -.bi-filetype-mp3::before { content: "\f753"; } -.bi-filetype-mp4::before { content: "\f754"; } -.bi-filetype-otf::before { content: "\f755"; } -.bi-filetype-pdf::before { content: "\f756"; } -.bi-filetype-php::before { content: "\f757"; } -.bi-filetype-png::before { content: "\f758"; } -.bi-filetype-ppt-1::before { content: "\f759"; } -.bi-filetype-ppt::before { content: "\f75a"; } -.bi-filetype-psd::before { content: "\f75b"; } -.bi-filetype-py::before { content: "\f75c"; } -.bi-filetype-raw::before { content: "\f75d"; } -.bi-filetype-rb::before { content: "\f75e"; } -.bi-filetype-sass::before { content: "\f75f"; } -.bi-filetype-scss::before { content: "\f760"; } -.bi-filetype-sh::before { content: "\f761"; } -.bi-filetype-svg::before { content: "\f762"; } -.bi-filetype-tiff::before { content: "\f763"; } -.bi-filetype-tsx::before { content: "\f764"; } -.bi-filetype-ttf::before { content: "\f765"; } -.bi-filetype-txt::before { content: "\f766"; } -.bi-filetype-wav::before { content: "\f767"; } -.bi-filetype-woff::before { content: "\f768"; } -.bi-filetype-xls-1::before { content: "\f769"; } -.bi-filetype-xls::before { content: "\f76a"; } -.bi-filetype-xml::before { content: "\f76b"; } -.bi-filetype-yml::before { content: "\f76c"; } -.bi-heart-arrow::before { content: "\f76d"; } -.bi-heart-pulse-fill::before { content: "\f76e"; } -.bi-heart-pulse::before { content: "\f76f"; } -.bi-heartbreak-fill::before { content: "\f770"; } -.bi-heartbreak::before { content: "\f771"; } -.bi-hearts::before { content: "\f772"; } -.bi-hospital-fill::before { content: "\f773"; } -.bi-hospital::before { content: "\f774"; } -.bi-house-heart-fill::before { content: "\f775"; } -.bi-house-heart::before { content: "\f776"; } -.bi-incognito::before { content: "\f777"; } -.bi-magnet-fill::before { content: "\f778"; } -.bi-magnet::before { content: "\f779"; } -.bi-person-heart::before { content: "\f77a"; } -.bi-person-hearts::before { content: "\f77b"; } -.bi-phone-flip::before { content: "\f77c"; } -.bi-plugin::before { content: "\f77d"; } -.bi-postage-fill::before { content: "\f77e"; } -.bi-postage-heart-fill::before { content: "\f77f"; } -.bi-postage-heart::before { content: "\f780"; } -.bi-postage::before { content: "\f781"; } -.bi-postcard-fill::before { content: "\f782"; } -.bi-postcard-heart-fill::before { content: "\f783"; } -.bi-postcard-heart::before { content: "\f784"; } -.bi-postcard::before { content: "\f785"; } -.bi-search-heart-fill::before { content: "\f786"; } -.bi-search-heart::before { content: "\f787"; } -.bi-sliders2-vertical::before { content: "\f788"; } -.bi-sliders2::before { content: "\f789"; } -.bi-trash3-fill::before { content: "\f78a"; } -.bi-trash3::before { content: "\f78b"; } -.bi-valentine::before { content: "\f78c"; } -.bi-valentine2::before { content: "\f78d"; } -.bi-wrench-adjustable-circle-fill::before { content: "\f78e"; } -.bi-wrench-adjustable-circle::before { content: "\f78f"; } -.bi-wrench-adjustable::before { content: "\f790"; } -.bi-filetype-json::before { content: "\f791"; } -.bi-filetype-pptx::before { content: "\f792"; } -.bi-filetype-xlsx::before { content: "\f793"; } diff --git a/docs/site_libs/bootstrap/bootstrap-icons.woff b/docs/site_libs/bootstrap/bootstrap-icons.woff deleted file mode 100644 index b26ccd1..0000000 Binary files a/docs/site_libs/bootstrap/bootstrap-icons.woff and /dev/null differ diff --git a/docs/site_libs/bootstrap/bootstrap.min.css b/docs/site_libs/bootstrap/bootstrap.min.css deleted file mode 100644 index 0009dd4..0000000 --- a/docs/site_libs/bootstrap/bootstrap.min.css +++ /dev/null @@ -1,10 +0,0 @@ -/*! - * Bootstrap v5.1.3 (https://getbootstrap.com/) - * Copyright 2011-2021 The Bootstrap Authors - * Copyright 2011-2021 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */@import"https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;700&display=swap";:root{--bs-blue: #2780e3;--bs-indigo: #6610f2;--bs-purple: #613d7c;--bs-pink: #e83e8c;--bs-red: #ff0039;--bs-orange: #f0ad4e;--bs-yellow: #ff7518;--bs-green: #3fb618;--bs-teal: #20c997;--bs-cyan: #9954bb;--bs-white: #fff;--bs-gray: #6c757d;--bs-gray-dark: #373a3c;--bs-gray-100: #f8f9fa;--bs-gray-200: #e9ecef;--bs-gray-300: #dee2e6;--bs-gray-400: #ced4da;--bs-gray-500: #adb5bd;--bs-gray-600: #6c757d;--bs-gray-700: #495057;--bs-gray-800: #373a3c;--bs-gray-900: #212529;--bs-default: #373a3c;--bs-primary: #2780e3;--bs-secondary: #373a3c;--bs-success: #3fb618;--bs-info: #9954bb;--bs-warning: #ff7518;--bs-danger: #ff0039;--bs-light: #f8f9fa;--bs-dark: #373a3c;--bs-default-rgb: 55, 58, 60;--bs-primary-rgb: 39, 128, 227;--bs-secondary-rgb: 55, 58, 60;--bs-success-rgb: 63, 182, 24;--bs-info-rgb: 153, 84, 187;--bs-warning-rgb: 255, 117, 24;--bs-danger-rgb: 255, 0, 57;--bs-light-rgb: 248, 249, 250;--bs-dark-rgb: 55, 58, 60;--bs-white-rgb: 255, 255, 255;--bs-black-rgb: 0, 0, 0;--bs-body-color-rgb: 55, 58, 60;--bs-body-bg-rgb: 255, 255, 255;--bs-font-sans-serif: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-root-font-size: 18px;--bs-body-font-family: var(--bs-font-sans-serif);--bs-body-font-size: 1rem;--bs-body-font-weight: 400;--bs-body-line-height: 1.5;--bs-body-color: #373a3c;--bs-body-bg: #fff}*,*::before,*::after{box-sizing:border-box}:root{font-size:var(--bs-root-font-size)}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h6,.h6,h5,.h5,h4,.h4,h3,.h3,h2,.h2,h1,.h1{margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2}h1,.h1{font-size:calc(1.345rem + 1.14vw)}@media(min-width: 1200px){h1,.h1{font-size:2.2rem}}h2,.h2{font-size:calc(1.3rem + 0.6vw)}@media(min-width: 1200px){h2,.h2{font-size:1.75rem}}h3,.h3{font-size:calc(1.275rem + 0.3vw)}@media(min-width: 1200px){h3,.h3{font-size:1.5rem}}h4,.h4{font-size:1.25rem}h5,.h5{font-size:1.1rem}h6,.h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-bs-original-title]{text-decoration:underline dotted;-webkit-text-decoration:underline dotted;-moz-text-decoration:underline dotted;-ms-text-decoration:underline dotted;-o-text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem;padding:.625rem 1.25rem;border-left:.25rem solid #e9ecef}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}b,strong{font-weight:bolder}small,.small{font-size:0.875em}mark,.mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:0.75em;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}a{color:#2780e3;text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}a:hover{color:#1f66b6}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr /* rtl:ignore */;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:0.875em;color:#000;background-color:#f7f7f7;padding:.5rem;border:1px solid #dee2e6}pre code{background-color:transparent;font-size:inherit;color:inherit;word-break:normal}code{font-size:0.875em;color:#9954bb;background-color:#f7f7f7;padding:.125rem .25rem;word-wrap:break-word}a>code{color:inherit}kbd{padding:.4rem .4rem;font-size:0.875em;color:#fff;background-color:#212529}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}thead,tbody,tfoot,tr,td,th{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + 0.3vw);line-height:inherit}@media(min-width: 1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none !important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:0.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:0.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:0.875em;color:#6c757d}.grid{display:grid;grid-template-rows:repeat(var(--bs-rows, 1), 1fr);grid-template-columns:repeat(var(--bs-columns, 12), 1fr);gap:var(--bs-gap, 1.5rem)}.grid .g-col-1{grid-column:auto/span 1}.grid .g-col-2{grid-column:auto/span 2}.grid .g-col-3{grid-column:auto/span 3}.grid .g-col-4{grid-column:auto/span 4}.grid .g-col-5{grid-column:auto/span 5}.grid .g-col-6{grid-column:auto/span 6}.grid .g-col-7{grid-column:auto/span 7}.grid .g-col-8{grid-column:auto/span 8}.grid .g-col-9{grid-column:auto/span 9}.grid .g-col-10{grid-column:auto/span 10}.grid .g-col-11{grid-column:auto/span 11}.grid .g-col-12{grid-column:auto/span 12}.grid .g-start-1{grid-column-start:1}.grid .g-start-2{grid-column-start:2}.grid .g-start-3{grid-column-start:3}.grid .g-start-4{grid-column-start:4}.grid .g-start-5{grid-column-start:5}.grid .g-start-6{grid-column-start:6}.grid .g-start-7{grid-column-start:7}.grid .g-start-8{grid-column-start:8}.grid .g-start-9{grid-column-start:9}.grid .g-start-10{grid-column-start:10}.grid .g-start-11{grid-column-start:11}@media(min-width: 576px){.grid .g-col-sm-1{grid-column:auto/span 1}.grid .g-col-sm-2{grid-column:auto/span 2}.grid .g-col-sm-3{grid-column:auto/span 3}.grid .g-col-sm-4{grid-column:auto/span 4}.grid .g-col-sm-5{grid-column:auto/span 5}.grid .g-col-sm-6{grid-column:auto/span 6}.grid .g-col-sm-7{grid-column:auto/span 7}.grid .g-col-sm-8{grid-column:auto/span 8}.grid .g-col-sm-9{grid-column:auto/span 9}.grid .g-col-sm-10{grid-column:auto/span 10}.grid .g-col-sm-11{grid-column:auto/span 11}.grid .g-col-sm-12{grid-column:auto/span 12}.grid .g-start-sm-1{grid-column-start:1}.grid .g-start-sm-2{grid-column-start:2}.grid .g-start-sm-3{grid-column-start:3}.grid .g-start-sm-4{grid-column-start:4}.grid .g-start-sm-5{grid-column-start:5}.grid .g-start-sm-6{grid-column-start:6}.grid .g-start-sm-7{grid-column-start:7}.grid .g-start-sm-8{grid-column-start:8}.grid .g-start-sm-9{grid-column-start:9}.grid .g-start-sm-10{grid-column-start:10}.grid .g-start-sm-11{grid-column-start:11}}@media(min-width: 768px){.grid .g-col-md-1{grid-column:auto/span 1}.grid .g-col-md-2{grid-column:auto/span 2}.grid .g-col-md-3{grid-column:auto/span 3}.grid .g-col-md-4{grid-column:auto/span 4}.grid .g-col-md-5{grid-column:auto/span 5}.grid .g-col-md-6{grid-column:auto/span 6}.grid .g-col-md-7{grid-column:auto/span 7}.grid .g-col-md-8{grid-column:auto/span 8}.grid .g-col-md-9{grid-column:auto/span 9}.grid .g-col-md-10{grid-column:auto/span 10}.grid .g-col-md-11{grid-column:auto/span 11}.grid .g-col-md-12{grid-column:auto/span 12}.grid .g-start-md-1{grid-column-start:1}.grid .g-start-md-2{grid-column-start:2}.grid .g-start-md-3{grid-column-start:3}.grid .g-start-md-4{grid-column-start:4}.grid .g-start-md-5{grid-column-start:5}.grid .g-start-md-6{grid-column-start:6}.grid .g-start-md-7{grid-column-start:7}.grid .g-start-md-8{grid-column-start:8}.grid .g-start-md-9{grid-column-start:9}.grid .g-start-md-10{grid-column-start:10}.grid .g-start-md-11{grid-column-start:11}}@media(min-width: 992px){.grid .g-col-lg-1{grid-column:auto/span 1}.grid .g-col-lg-2{grid-column:auto/span 2}.grid .g-col-lg-3{grid-column:auto/span 3}.grid .g-col-lg-4{grid-column:auto/span 4}.grid .g-col-lg-5{grid-column:auto/span 5}.grid .g-col-lg-6{grid-column:auto/span 6}.grid .g-col-lg-7{grid-column:auto/span 7}.grid .g-col-lg-8{grid-column:auto/span 8}.grid .g-col-lg-9{grid-column:auto/span 9}.grid .g-col-lg-10{grid-column:auto/span 10}.grid .g-col-lg-11{grid-column:auto/span 11}.grid .g-col-lg-12{grid-column:auto/span 12}.grid .g-start-lg-1{grid-column-start:1}.grid .g-start-lg-2{grid-column-start:2}.grid .g-start-lg-3{grid-column-start:3}.grid .g-start-lg-4{grid-column-start:4}.grid .g-start-lg-5{grid-column-start:5}.grid .g-start-lg-6{grid-column-start:6}.grid .g-start-lg-7{grid-column-start:7}.grid .g-start-lg-8{grid-column-start:8}.grid .g-start-lg-9{grid-column-start:9}.grid .g-start-lg-10{grid-column-start:10}.grid .g-start-lg-11{grid-column-start:11}}@media(min-width: 1200px){.grid .g-col-xl-1{grid-column:auto/span 1}.grid .g-col-xl-2{grid-column:auto/span 2}.grid .g-col-xl-3{grid-column:auto/span 3}.grid .g-col-xl-4{grid-column:auto/span 4}.grid .g-col-xl-5{grid-column:auto/span 5}.grid .g-col-xl-6{grid-column:auto/span 6}.grid .g-col-xl-7{grid-column:auto/span 7}.grid .g-col-xl-8{grid-column:auto/span 8}.grid .g-col-xl-9{grid-column:auto/span 9}.grid .g-col-xl-10{grid-column:auto/span 10}.grid .g-col-xl-11{grid-column:auto/span 11}.grid .g-col-xl-12{grid-column:auto/span 12}.grid .g-start-xl-1{grid-column-start:1}.grid .g-start-xl-2{grid-column-start:2}.grid .g-start-xl-3{grid-column-start:3}.grid .g-start-xl-4{grid-column-start:4}.grid .g-start-xl-5{grid-column-start:5}.grid .g-start-xl-6{grid-column-start:6}.grid .g-start-xl-7{grid-column-start:7}.grid .g-start-xl-8{grid-column-start:8}.grid .g-start-xl-9{grid-column-start:9}.grid .g-start-xl-10{grid-column-start:10}.grid .g-start-xl-11{grid-column-start:11}}@media(min-width: 1400px){.grid .g-col-xxl-1{grid-column:auto/span 1}.grid .g-col-xxl-2{grid-column:auto/span 2}.grid .g-col-xxl-3{grid-column:auto/span 3}.grid .g-col-xxl-4{grid-column:auto/span 4}.grid .g-col-xxl-5{grid-column:auto/span 5}.grid .g-col-xxl-6{grid-column:auto/span 6}.grid .g-col-xxl-7{grid-column:auto/span 7}.grid .g-col-xxl-8{grid-column:auto/span 8}.grid .g-col-xxl-9{grid-column:auto/span 9}.grid .g-col-xxl-10{grid-column:auto/span 10}.grid .g-col-xxl-11{grid-column:auto/span 11}.grid .g-col-xxl-12{grid-column:auto/span 12}.grid .g-start-xxl-1{grid-column-start:1}.grid .g-start-xxl-2{grid-column-start:2}.grid .g-start-xxl-3{grid-column-start:3}.grid .g-start-xxl-4{grid-column-start:4}.grid .g-start-xxl-5{grid-column-start:5}.grid .g-start-xxl-6{grid-column-start:6}.grid .g-start-xxl-7{grid-column-start:7}.grid .g-start-xxl-8{grid-column-start:8}.grid .g-start-xxl-9{grid-column-start:9}.grid .g-start-xxl-10{grid-column-start:10}.grid .g-start-xxl-11{grid-column-start:11}}.table{--bs-table-bg: transparent;--bs-table-accent-bg: transparent;--bs-table-striped-color: #373a3c;--bs-table-striped-bg: rgba(0, 0, 0, 0.05);--bs-table-active-color: #373a3c;--bs-table-active-bg: rgba(0, 0, 0, 0.1);--bs-table-hover-color: #373a3c;--bs-table-hover-bg: rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#373a3c;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:first-child){border-top:2px solid currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-accent-bg: var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg: var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover>*{--bs-table-accent-bg: var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg: #d4e6f9;--bs-table-striped-bg: #c9dbed;--bs-table-striped-color: #000;--bs-table-active-bg: #bfcfe0;--bs-table-active-color: #000;--bs-table-hover-bg: #c4d5e6;--bs-table-hover-color: #000;color:#000;border-color:#bfcfe0}.table-secondary{--bs-table-bg: #d7d8d8;--bs-table-striped-bg: #cccdcd;--bs-table-striped-color: #000;--bs-table-active-bg: #c2c2c2;--bs-table-active-color: #000;--bs-table-hover-bg: #c7c8c8;--bs-table-hover-color: #000;color:#000;border-color:#c2c2c2}.table-success{--bs-table-bg: #d9f0d1;--bs-table-striped-bg: #cee4c7;--bs-table-striped-color: #000;--bs-table-active-bg: #c3d8bc;--bs-table-active-color: #000;--bs-table-hover-bg: #c9dec1;--bs-table-hover-color: #000;color:#000;border-color:#c3d8bc}.table-info{--bs-table-bg: #ebddf1;--bs-table-striped-bg: #dfd2e5;--bs-table-striped-color: #000;--bs-table-active-bg: #d4c7d9;--bs-table-active-color: #000;--bs-table-hover-bg: #d9ccdf;--bs-table-hover-color: #000;color:#000;border-color:#d4c7d9}.table-warning{--bs-table-bg: #ffe3d1;--bs-table-striped-bg: #f2d8c7;--bs-table-striped-color: #000;--bs-table-active-bg: #e6ccbc;--bs-table-active-color: #000;--bs-table-hover-bg: #ecd2c1;--bs-table-hover-color: #000;color:#000;border-color:#e6ccbc}.table-danger{--bs-table-bg: #ffccd7;--bs-table-striped-bg: #f2c2cc;--bs-table-striped-color: #000;--bs-table-active-bg: #e6b8c2;--bs-table-active-color: #000;--bs-table-hover-bg: #ecbdc7;--bs-table-hover-color: #000;color:#000;border-color:#e6b8c2}.table-light{--bs-table-bg: #f8f9fa;--bs-table-striped-bg: #ecedee;--bs-table-striped-color: #000;--bs-table-active-bg: #dfe0e1;--bs-table-active-color: #000;--bs-table-hover-bg: #e5e6e7;--bs-table-hover-color: #000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg: #373a3c;--bs-table-striped-bg: #414446;--bs-table-striped-color: #fff;--bs-table-active-bg: #4b4e50;--bs-table-active-color: #fff;--bs-table-hover-bg: #46494b;--bs-table-hover-color: #fff;color:#fff;border-color:#4b4e50}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width: 575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width: 767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width: 991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width: 1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width: 1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label,.shiny-input-container .control-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(0.375rem + 1px);padding-bottom:calc(0.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(0.5rem + 1px);padding-bottom:calc(0.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(0.25rem + 1px);padding-bottom:calc(0.25rem + 1px);font-size:0.875rem}.form-text{margin-top:.25rem;font-size:0.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#373a3c;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;border-radius:0;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#373a3c;background-color:#fff;border-color:#93c0f1;outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-0.375rem -0.75rem;margin-inline-end:.75rem;color:#373a3c;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-0.375rem -0.75rem;margin-inline-end:.75rem;color:#373a3c;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-control::-webkit-file-upload-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#373a3c;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + 0.5rem + 2px);padding:.25rem .5rem;font-size:0.875rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-0.25rem -0.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-0.25rem -0.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-0.5rem -1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-0.5rem -1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + 0.75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + 0.5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em}.form-control-color::-webkit-color-swatch{height:1.5em}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#373a3c;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23373a3c' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:0;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}@media(prefers-reduced-motion: reduce){.form-select{transition:none}}.form-select:focus{border-color:#93c0f1;outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #373a3c}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:0.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.form-check,.shiny-input-container .checkbox,.shiny-input-container .radio{display:block;min-height:1.5rem;padding-left:0;margin-bottom:.125rem}.form-check .form-check-input,.form-check .shiny-input-container .checkbox input,.form-check .shiny-input-container .radio input,.shiny-input-container .checkbox .form-check-input,.shiny-input-container .checkbox .shiny-input-container .checkbox input,.shiny-input-container .checkbox .shiny-input-container .radio input,.shiny-input-container .radio .form-check-input,.shiny-input-container .radio .shiny-input-container .checkbox input,.shiny-input-container .radio .shiny-input-container .radio input{float:left;margin-left:0}.form-check-input,.shiny-input-container .checkbox input,.shiny-input-container .checkbox-inline input,.shiny-input-container .radio input,.shiny-input-container .radio-inline input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;color-adjust:exact;-webkit-print-color-adjust:exact}.form-check-input[type=radio],.shiny-input-container .checkbox input[type=radio],.shiny-input-container .checkbox-inline input[type=radio],.shiny-input-container .radio input[type=radio],.shiny-input-container .radio-inline input[type=radio]{border-radius:50%}.form-check-input:active,.shiny-input-container .checkbox input:active,.shiny-input-container .checkbox-inline input:active,.shiny-input-container .radio input:active,.shiny-input-container .radio-inline input:active{filter:brightness(90%)}.form-check-input:focus,.shiny-input-container .checkbox input:focus,.shiny-input-container .checkbox-inline input:focus,.shiny-input-container .radio input:focus,.shiny-input-container .radio-inline input:focus{border-color:#93c0f1;outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.form-check-input:checked,.shiny-input-container .checkbox input:checked,.shiny-input-container .checkbox-inline input:checked,.shiny-input-container .radio input:checked,.shiny-input-container .radio-inline input:checked{background-color:#2780e3;border-color:#2780e3}.form-check-input:checked[type=checkbox],.shiny-input-container .checkbox input:checked[type=checkbox],.shiny-input-container .checkbox-inline input:checked[type=checkbox],.shiny-input-container .radio input:checked[type=checkbox],.shiny-input-container .radio-inline input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio],.shiny-input-container .checkbox input:checked[type=radio],.shiny-input-container .checkbox-inline input:checked[type=radio],.shiny-input-container .radio input:checked[type=radio],.shiny-input-container .radio-inline input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate,.shiny-input-container .checkbox input[type=checkbox]:indeterminate,.shiny-input-container .checkbox-inline input[type=checkbox]:indeterminate,.shiny-input-container .radio input[type=checkbox]:indeterminate,.shiny-input-container .radio-inline input[type=checkbox]:indeterminate{background-color:#2780e3;border-color:#2780e3;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled,.shiny-input-container .checkbox input:disabled,.shiny-input-container .checkbox-inline input:disabled,.shiny-input-container .radio input:disabled,.shiny-input-container .radio-inline input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input[disabled]~.form-check-label,.form-check-input[disabled]~span,.form-check-input:disabled~.form-check-label,.form-check-input:disabled~span,.shiny-input-container .checkbox input[disabled]~.form-check-label,.shiny-input-container .checkbox input[disabled]~span,.shiny-input-container .checkbox input:disabled~.form-check-label,.shiny-input-container .checkbox input:disabled~span,.shiny-input-container .checkbox-inline input[disabled]~.form-check-label,.shiny-input-container .checkbox-inline input[disabled]~span,.shiny-input-container .checkbox-inline input:disabled~.form-check-label,.shiny-input-container .checkbox-inline input:disabled~span,.shiny-input-container .radio input[disabled]~.form-check-label,.shiny-input-container .radio input[disabled]~span,.shiny-input-container .radio input:disabled~.form-check-label,.shiny-input-container .radio input:disabled~span,.shiny-input-container .radio-inline input[disabled]~.form-check-label,.shiny-input-container .radio-inline input[disabled]~span,.shiny-input-container .radio-inline input:disabled~.form-check-label,.shiny-input-container .radio-inline input:disabled~span{opacity:.5}.form-check-label,.shiny-input-container .checkbox label,.shiny-input-container .checkbox-inline label,.shiny-input-container .radio label,.shiny-input-container .radio-inline label{cursor:pointer}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;transition:background-position .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2393c0f1'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline,.shiny-input-container .checkbox-inline,.shiny-input-container .radio-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.btn-check[disabled]+.btn,.btn-check:disabled+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(39,128,227,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(39,128,227,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;background-color:#2780e3;border:0;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}@media(prefers-reduced-motion: reduce){.form-range::-webkit-slider-thumb{transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#bed9f7}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#2780e3;border:0;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}@media(prefers-reduced-motion: reduce){.form-range::-moz-range-thumb{transition:none}}.form-range::-moz-range-thumb:active{background-color:#bed9f7}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media(prefers-reduced-motion: reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.input-group{position:relative;display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;align-items:stretch;-webkit-align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;-webkit-flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#373a3c;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn{padding:.25rem .5rem;font-size:0.875rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:#3fb618}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:0.875rem;color:#fff;background-color:rgba(63,182,24,.9)}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:#3fb618;padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%233fb618' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(0.375em + 0.1875rem) center;background-size:calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:#3fb618;box-shadow:0 0 0 .25rem rgba(63,182,24,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 0.75rem);background-position:top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem)}.was-validated .form-select:valid,.form-select.is-valid{border-color:#3fb618}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23373a3c' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%233fb618' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:#3fb618;box-shadow:0 0 0 .25rem rgba(63,182,24,.25)}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:#3fb618}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:#3fb618}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(63,182,24,.25)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:#3fb618}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.was-validated .input-group .form-control:valid,.input-group .form-control.is-valid,.was-validated .input-group .form-select:valid,.input-group .form-select.is-valid{z-index:1}.was-validated .input-group .form-control:valid:focus,.input-group .form-control.is-valid:focus,.was-validated .input-group .form-select:valid:focus,.input-group .form-select.is-valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:#ff0039}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:0.875rem;color:#fff;background-color:rgba(255,0,57,.9)}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:#ff0039;padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23ff0039'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ff0039' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(0.375em + 0.1875rem) center;background-size:calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:#ff0039;box-shadow:0 0 0 .25rem rgba(255,0,57,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 0.75rem);background-position:top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem)}.was-validated .form-select:invalid,.form-select.is-invalid{border-color:#ff0039}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23373a3c' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23ff0039'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ff0039' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:#ff0039;box-shadow:0 0 0 .25rem rgba(255,0,57,.25)}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:#ff0039}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:#ff0039}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .25rem rgba(255,0,57,.25)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:#ff0039}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.was-validated .input-group .form-control:invalid,.input-group .form-control.is-invalid,.was-validated .input-group .form-select:invalid,.input-group .form-select.is-invalid{z-index:2}.was-validated .input-group .form-control:invalid:focus,.input-group .form-control.is-invalid:focus,.was-validated .input-group .form-select:invalid:focus,.input-group .form-select.is-invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#373a3c;text-align:center;text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;vertical-align:middle;cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.btn{transition:none}}.btn:hover{color:#373a3c}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.btn:disabled,.btn.disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-default{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-default:hover{color:#fff;background-color:#2f3133;border-color:#2c2e30}.btn-check:focus+.btn-default,.btn-default:focus{color:#fff;background-color:#2f3133;border-color:#2c2e30;box-shadow:0 0 0 .25rem rgba(85,88,89,.5)}.btn-check:checked+.btn-default,.btn-check:active+.btn-default,.btn-default:active,.btn-default.active,.show>.btn-default.dropdown-toggle{color:#fff;background-color:#2c2e30;border-color:#292c2d}.btn-check:checked+.btn-default:focus,.btn-check:active+.btn-default:focus,.btn-default:active:focus,.btn-default.active:focus,.show>.btn-default.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(85,88,89,.5)}.btn-default:disabled,.btn-default.disabled{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-primary{color:#fff;background-color:#2780e3;border-color:#2780e3}.btn-primary:hover{color:#fff;background-color:#216dc1;border-color:#1f66b6}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#216dc1;border-color:#1f66b6;box-shadow:0 0 0 .25rem rgba(71,147,231,.5)}.btn-check:checked+.btn-primary,.btn-check:active+.btn-primary,.btn-primary:active,.btn-primary.active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#1f66b6;border-color:#1d60aa}.btn-check:checked+.btn-primary:focus,.btn-check:active+.btn-primary:focus,.btn-primary:active:focus,.btn-primary.active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(71,147,231,.5)}.btn-primary:disabled,.btn-primary.disabled{color:#fff;background-color:#2780e3;border-color:#2780e3}.btn-secondary{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-secondary:hover{color:#fff;background-color:#2f3133;border-color:#2c2e30}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#2f3133;border-color:#2c2e30;box-shadow:0 0 0 .25rem rgba(85,88,89,.5)}.btn-check:checked+.btn-secondary,.btn-check:active+.btn-secondary,.btn-secondary:active,.btn-secondary.active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#2c2e30;border-color:#292c2d}.btn-check:checked+.btn-secondary:focus,.btn-check:active+.btn-secondary:focus,.btn-secondary:active:focus,.btn-secondary.active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(85,88,89,.5)}.btn-secondary:disabled,.btn-secondary.disabled{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-success{color:#fff;background-color:#3fb618;border-color:#3fb618}.btn-success:hover{color:#fff;background-color:#369b14;border-color:#329213}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#369b14;border-color:#329213;box-shadow:0 0 0 .25rem rgba(92,193,59,.5)}.btn-check:checked+.btn-success,.btn-check:active+.btn-success,.btn-success:active,.btn-success.active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#329213;border-color:#2f8912}.btn-check:checked+.btn-success:focus,.btn-check:active+.btn-success:focus,.btn-success:active:focus,.btn-success.active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(92,193,59,.5)}.btn-success:disabled,.btn-success.disabled{color:#fff;background-color:#3fb618;border-color:#3fb618}.btn-info{color:#fff;background-color:#9954bb;border-color:#9954bb}.btn-info:hover{color:#fff;background-color:#82479f;border-color:#7a4396}.btn-check:focus+.btn-info,.btn-info:focus{color:#fff;background-color:#82479f;border-color:#7a4396;box-shadow:0 0 0 .25rem rgba(168,110,197,.5)}.btn-check:checked+.btn-info,.btn-check:active+.btn-info,.btn-info:active,.btn-info.active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#7a4396;border-color:#733f8c}.btn-check:checked+.btn-info:focus,.btn-check:active+.btn-info:focus,.btn-info:active:focus,.btn-info.active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(168,110,197,.5)}.btn-info:disabled,.btn-info.disabled{color:#fff;background-color:#9954bb;border-color:#9954bb}.btn-warning{color:#fff;background-color:#ff7518;border-color:#ff7518}.btn-warning:hover{color:#fff;background-color:#d96314;border-color:#cc5e13}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#fff;background-color:#d96314;border-color:#cc5e13;box-shadow:0 0 0 .25rem rgba(255,138,59,.5)}.btn-check:checked+.btn-warning,.btn-check:active+.btn-warning,.btn-warning:active,.btn-warning.active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#cc5e13;border-color:#bf5812}.btn-check:checked+.btn-warning:focus,.btn-check:active+.btn-warning:focus,.btn-warning:active:focus,.btn-warning.active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(255,138,59,.5)}.btn-warning:disabled,.btn-warning.disabled{color:#fff;background-color:#ff7518;border-color:#ff7518}.btn-danger{color:#fff;background-color:#ff0039;border-color:#ff0039}.btn-danger:hover{color:#fff;background-color:#d90030;border-color:#cc002e}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#d90030;border-color:#cc002e;box-shadow:0 0 0 .25rem rgba(255,38,87,.5)}.btn-check:checked+.btn-danger,.btn-check:active+.btn-danger,.btn-danger:active,.btn-danger.active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#cc002e;border-color:#bf002b}.btn-check:checked+.btn-danger:focus,.btn-check:active+.btn-danger:focus,.btn-danger:active:focus,.btn-danger.active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(255,38,87,.5)}.btn-danger:disabled,.btn-danger.disabled{color:#fff;background-color:#ff0039;border-color:#ff0039}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:checked+.btn-light,.btn-check:active+.btn-light,.btn-light:active,.btn-light.active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:checked+.btn-light:focus,.btn-check:active+.btn-light:focus,.btn-light:active:focus,.btn-light.active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light:disabled,.btn-light.disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-dark:hover{color:#fff;background-color:#2f3133;border-color:#2c2e30}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#2f3133;border-color:#2c2e30;box-shadow:0 0 0 .25rem rgba(85,88,89,.5)}.btn-check:checked+.btn-dark,.btn-check:active+.btn-dark,.btn-dark:active,.btn-dark.active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#2c2e30;border-color:#292c2d}.btn-check:checked+.btn-dark:focus,.btn-check:active+.btn-dark:focus,.btn-dark:active:focus,.btn-dark.active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(85,88,89,.5)}.btn-dark:disabled,.btn-dark.disabled{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-outline-default{color:#373a3c;border-color:#373a3c;background-color:transparent}.btn-outline-default:hover{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-check:focus+.btn-outline-default,.btn-outline-default:focus{box-shadow:0 0 0 .25rem rgba(55,58,60,.5)}.btn-check:checked+.btn-outline-default,.btn-check:active+.btn-outline-default,.btn-outline-default:active,.btn-outline-default.active,.btn-outline-default.dropdown-toggle.show{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-check:checked+.btn-outline-default:focus,.btn-check:active+.btn-outline-default:focus,.btn-outline-default:active:focus,.btn-outline-default.active:focus,.btn-outline-default.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(55,58,60,.5)}.btn-outline-default:disabled,.btn-outline-default.disabled{color:#373a3c;background-color:transparent}.btn-outline-primary{color:#2780e3;border-color:#2780e3;background-color:transparent}.btn-outline-primary:hover{color:#fff;background-color:#2780e3;border-color:#2780e3}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(39,128,227,.5)}.btn-check:checked+.btn-outline-primary,.btn-check:active+.btn-outline-primary,.btn-outline-primary:active,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show{color:#fff;background-color:#2780e3;border-color:#2780e3}.btn-check:checked+.btn-outline-primary:focus,.btn-check:active+.btn-outline-primary:focus,.btn-outline-primary:active:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(39,128,227,.5)}.btn-outline-primary:disabled,.btn-outline-primary.disabled{color:#2780e3;background-color:transparent}.btn-outline-secondary{color:#373a3c;border-color:#373a3c;background-color:transparent}.btn-outline-secondary:hover{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(55,58,60,.5)}.btn-check:checked+.btn-outline-secondary,.btn-check:active+.btn-outline-secondary,.btn-outline-secondary:active,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-check:checked+.btn-outline-secondary:focus,.btn-check:active+.btn-outline-secondary:focus,.btn-outline-secondary:active:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(55,58,60,.5)}.btn-outline-secondary:disabled,.btn-outline-secondary.disabled{color:#373a3c;background-color:transparent}.btn-outline-success{color:#3fb618;border-color:#3fb618;background-color:transparent}.btn-outline-success:hover{color:#fff;background-color:#3fb618;border-color:#3fb618}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(63,182,24,.5)}.btn-check:checked+.btn-outline-success,.btn-check:active+.btn-outline-success,.btn-outline-success:active,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show{color:#fff;background-color:#3fb618;border-color:#3fb618}.btn-check:checked+.btn-outline-success:focus,.btn-check:active+.btn-outline-success:focus,.btn-outline-success:active:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(63,182,24,.5)}.btn-outline-success:disabled,.btn-outline-success.disabled{color:#3fb618;background-color:transparent}.btn-outline-info{color:#9954bb;border-color:#9954bb;background-color:transparent}.btn-outline-info:hover{color:#fff;background-color:#9954bb;border-color:#9954bb}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(153,84,187,.5)}.btn-check:checked+.btn-outline-info,.btn-check:active+.btn-outline-info,.btn-outline-info:active,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show{color:#fff;background-color:#9954bb;border-color:#9954bb}.btn-check:checked+.btn-outline-info:focus,.btn-check:active+.btn-outline-info:focus,.btn-outline-info:active:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(153,84,187,.5)}.btn-outline-info:disabled,.btn-outline-info.disabled{color:#9954bb;background-color:transparent}.btn-outline-warning{color:#ff7518;border-color:#ff7518;background-color:transparent}.btn-outline-warning:hover{color:#fff;background-color:#ff7518;border-color:#ff7518}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,117,24,.5)}.btn-check:checked+.btn-outline-warning,.btn-check:active+.btn-outline-warning,.btn-outline-warning:active,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show{color:#fff;background-color:#ff7518;border-color:#ff7518}.btn-check:checked+.btn-outline-warning:focus,.btn-check:active+.btn-outline-warning:focus,.btn-outline-warning:active:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(255,117,24,.5)}.btn-outline-warning:disabled,.btn-outline-warning.disabled{color:#ff7518;background-color:transparent}.btn-outline-danger{color:#ff0039;border-color:#ff0039;background-color:transparent}.btn-outline-danger:hover{color:#fff;background-color:#ff0039;border-color:#ff0039}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(255,0,57,.5)}.btn-check:checked+.btn-outline-danger,.btn-check:active+.btn-outline-danger,.btn-outline-danger:active,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show{color:#fff;background-color:#ff0039;border-color:#ff0039}.btn-check:checked+.btn-outline-danger:focus,.btn-check:active+.btn-outline-danger:focus,.btn-outline-danger:active:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(255,0,57,.5)}.btn-outline-danger:disabled,.btn-outline-danger.disabled{color:#ff0039;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa;background-color:transparent}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:checked+.btn-outline-light,.btn-check:active+.btn-outline-light,.btn-outline-light:active,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:checked+.btn-outline-light:focus,.btn-check:active+.btn-outline-light:focus,.btn-outline-light:active:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light:disabled,.btn-outline-light.disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#373a3c;border-color:#373a3c;background-color:transparent}.btn-outline-dark:hover{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(55,58,60,.5)}.btn-check:checked+.btn-outline-dark,.btn-check:active+.btn-outline-dark,.btn-outline-dark:active,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show{color:#fff;background-color:#373a3c;border-color:#373a3c}.btn-check:checked+.btn-outline-dark:focus,.btn-check:active+.btn-outline-dark:focus,.btn-outline-dark:active:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(55,58,60,.5)}.btn-outline-dark:disabled,.btn-outline-dark.disabled{color:#373a3c;background-color:transparent}.btn-link{font-weight:400;color:#2780e3;text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}.btn-link:hover{color:#1f66b6}.btn-link:disabled,.btn-link.disabled{color:#6c757d}.btn-lg,.btn-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem;border-radius:0}.btn-sm,.btn-group-sm>.btn{padding:.25rem .5rem;font-size:0.875rem;border-radius:0}.fade{transition:opacity .15s linear}@media(prefers-reduced-motion: reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .2s ease}@media(prefers-reduced-motion: reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media(prefers-reduced-motion: reduce){.collapsing.collapse-horizontal{transition:none}}.dropup,.dropend,.dropdown,.dropstart{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#373a3c;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position: start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position: end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media(min-width: 576px){.dropdown-menu-sm-start{--bs-position: start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position: end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media(min-width: 768px){.dropdown-menu-md-start{--bs-position: start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position: end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media(min-width: 992px){.dropdown-menu-lg-start{--bs-position: start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position: end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media(min-width: 1200px){.dropdown-menu-xl-start{--bs-position: start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position: end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media(min-width: 1400px){.dropdown-menu-xxl-start{--bs-position: start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position: end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:hover,.dropdown-item:focus{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#2780e3}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:0.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#373a3c;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:hover,.dropdown-menu-dark .dropdown-item:focus{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#2780e3}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;flex:1 1 auto;-webkit-flex:1 1 auto}.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;justify-content:flex-start;-webkit-justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child){margin-left:-1px}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;-webkit-flex-direction:column;align-items:flex-start;-webkit-align-items:flex-start;justify-content:center;-webkit-justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:-1px}.nav{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#2780e3;text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media(prefers-reduced-motion: reduce){.nav-link{transition:none}}.nav-link:hover,.nav-link:focus{color:#1f66b6}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:none;border:1px solid transparent}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px}.nav-pills .nav-link{background:none;border:0}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#2780e3}.nav-fill>.nav-link,.nav-fill .nav-item{flex:1 1 auto;-webkit-flex:1 1 auto;text-align:center}.nav-justified>.nav-link,.nav-justified .nav-item{flex-basis:0;-webkit-flex-basis:0;flex-grow:1;-webkit-flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;align-items:center;-webkit-align-items:center;justify-content:space-between;-webkit-justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container-xxl,.navbar>.container-xl,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container,.navbar>.container-fluid{display:flex;display:-webkit-flex;flex-wrap:inherit;-webkit-flex-wrap:inherit;align-items:center;-webkit-align-items:center;justify-content:space-between;-webkit-justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;-webkit-flex-basis:100%;flex-grow:1;-webkit-flex-grow:1;align-items:center;-webkit-align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;transition:box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height, 75vh);overflow-y:auto}@media(min-width: 576px){.navbar-expand-sm{flex-wrap:nowrap;-webkit-flex-wrap:nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas-header{display:none}.navbar-expand-sm .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;-webkit-flex-grow:1;visibility:visible !important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-sm .offcanvas-top,.navbar-expand-sm .offcanvas-bottom{height:auto;border-top:0;border-bottom:0}.navbar-expand-sm .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible}}@media(min-width: 768px){.navbar-expand-md{flex-wrap:nowrap;-webkit-flex-wrap:nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas-header{display:none}.navbar-expand-md .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;-webkit-flex-grow:1;visibility:visible !important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-md .offcanvas-top,.navbar-expand-md .offcanvas-bottom{height:auto;border-top:0;border-bottom:0}.navbar-expand-md .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible}}@media(min-width: 992px){.navbar-expand-lg{flex-wrap:nowrap;-webkit-flex-wrap:nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas-header{display:none}.navbar-expand-lg .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;-webkit-flex-grow:1;visibility:visible !important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-lg .offcanvas-top,.navbar-expand-lg .offcanvas-bottom{height:auto;border-top:0;border-bottom:0}.navbar-expand-lg .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible}}@media(min-width: 1200px){.navbar-expand-xl{flex-wrap:nowrap;-webkit-flex-wrap:nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas-header{display:none}.navbar-expand-xl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;-webkit-flex-grow:1;visibility:visible !important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xl .offcanvas-top,.navbar-expand-xl .offcanvas-bottom{height:auto;border-top:0;border-bottom:0}.navbar-expand-xl .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible}}@media(min-width: 1400px){.navbar-expand-xxl{flex-wrap:nowrap;-webkit-flex-wrap:nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;-webkit-flex-grow:1;visibility:visible !important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xxl .offcanvas-top,.navbar-expand-xxl .offcanvas-bottom{height:auto;border-top:0;border-bottom:0}.navbar-expand-xxl .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;-webkit-flex-wrap:nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas-header{display:none}.navbar-expand .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;-webkit-flex-grow:1;visibility:visible !important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand .offcanvas-top,.navbar-expand .offcanvas-bottom{height:auto;border-top:0;border-bottom:0}.navbar-expand .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible}.navbar-light{background-color:#2780e3}.navbar-light .navbar-brand{color:#fdfeff}.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:focus{color:#fdfeff}.navbar-light .navbar-nav .nav-link{color:#fdfeff}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-link:focus{color:rgba(253,254,255,.8)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(253,254,255,.75)}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .nav-link.active{color:#fdfeff}.navbar-light .navbar-toggler{color:#fdfeff;border-color:rgba(253,254,255,.4)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23fdfeff' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:#fdfeff}.navbar-light .navbar-text a,.navbar-light .navbar-text a:hover,.navbar-light .navbar-text a:focus{color:#fdfeff}.navbar-dark{background-color:#2780e3}.navbar-dark .navbar-brand{color:#fdfeff}.navbar-dark .navbar-brand:hover,.navbar-dark .navbar-brand:focus{color:#fdfeff}.navbar-dark .navbar-nav .nav-link{color:#fdfeff}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-link:focus{color:rgba(253,254,255,.8)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(253,254,255,.75)}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active{color:#fdfeff}.navbar-dark .navbar-toggler{color:#fdfeff;border-color:rgba(253,254,255,.4)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23fdfeff' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:#fdfeff}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:hover,.navbar-dark .navbar-text a:focus{color:#fdfeff}.card{position:relative;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0}.card>.list-group:last-child{border-bottom-width:0}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;-webkit-flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-0.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:#adb5bd;border-bottom:1px solid rgba(0,0,0,.125)}.card-footer{padding:.5rem 1rem;background-color:#adb5bd;border-top:1px solid rgba(0,0,0,.125)}.card-header-tabs{margin-right:-0.5rem;margin-bottom:-0.5rem;margin-left:-0.5rem;border-bottom:0}.card-header-pills{margin-right:-0.5rem;margin-left:-0.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem}.card-img,.card-img-top,.card-img-bottom{width:100%}.card-group>.card{margin-bottom:.75rem}@media(min-width: 576px){.card-group{display:flex;display:-webkit-flex;flex-flow:row wrap;-webkit-flex-flow:row wrap}.card-group>.card{flex:1 0 0%;-webkit-flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}}.accordion-button{position:relative;display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#373a3c;text-align:left;background-color:#fff;border:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media(prefers-reduced-motion: reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#2373cc;background-color:#e9f2fc;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%232373cc'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;-webkit-flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23373a3c'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media(prefers-reduced-motion: reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#93c0f1;outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:not(:first-of-type){border-top:0}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.breadcrumb{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;display:-webkit-flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#2780e3;text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#1f66b6;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#1f66b6;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#2780e3;border-color:#2780e3}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:0.875rem}.badge{display:inline-block;padding:.35em .65em;font-size:0.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:0 solid transparent}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-default{color:#212324;background-color:#d7d8d8;border-color:#c3c4c5}.alert-default .alert-link{color:#1a1c1d}.alert-primary{color:#174d88;background-color:#d4e6f9;border-color:#bed9f7}.alert-primary .alert-link{color:#123e6d}.alert-secondary{color:#212324;background-color:#d7d8d8;border-color:#c3c4c5}.alert-secondary .alert-link{color:#1a1c1d}.alert-success{color:#266d0e;background-color:#d9f0d1;border-color:#c5e9ba}.alert-success .alert-link{color:#1e570b}.alert-info{color:#5c3270;background-color:#ebddf1;border-color:#e0cceb}.alert-info .alert-link{color:#4a285a}.alert-warning{color:#99460e;background-color:#ffe3d1;border-color:#ffd6ba}.alert-warning .alert-link{color:#7a380b}.alert-danger{color:#902;background-color:#ffccd7;border-color:#ffb3c4}.alert-danger .alert-link{color:#7a001b}.alert-light{color:#959596;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#777778}.alert-dark{color:#212324;background-color:#d7d8d8;border-color:#c3c4c5}.alert-dark .alert-link{color:#1a1c1d}@keyframes progress-bar-stripes{0%{background-position-x:.5rem}}.progress{display:flex;display:-webkit-flex;height:.5rem;overflow:hidden;font-size:0.75rem;background-color:#e9ecef}.progress-bar{display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;justify-content:center;-webkit-justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#2780e3;transition:width .6s ease}@media(prefers-reduced-motion: reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-size:.5rem .5rem}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion: reduce){.progress-bar-animated{animation:none}}.list-group{display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;padding-left:0;margin-bottom:0}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#373a3c;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#2780e3;border-color:#2780e3}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media(min-width: 576px){.list-group-horizontal-sm{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media(min-width: 768px){.list-group-horizontal-md{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media(min-width: 992px){.list-group-horizontal-lg{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media(min-width: 1200px){.list-group-horizontal-xl{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media(min-width: 1400px){.list-group-horizontal-xxl{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-default{color:#212324;background-color:#d7d8d8}.list-group-item-default.list-group-item-action:hover,.list-group-item-default.list-group-item-action:focus{color:#212324;background-color:#c2c2c2}.list-group-item-default.list-group-item-action.active{color:#fff;background-color:#212324;border-color:#212324}.list-group-item-primary{color:#174d88;background-color:#d4e6f9}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#174d88;background-color:#bfcfe0}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#174d88;border-color:#174d88}.list-group-item-secondary{color:#212324;background-color:#d7d8d8}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#212324;background-color:#c2c2c2}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#212324;border-color:#212324}.list-group-item-success{color:#266d0e;background-color:#d9f0d1}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#266d0e;background-color:#c3d8bc}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#266d0e;border-color:#266d0e}.list-group-item-info{color:#5c3270;background-color:#ebddf1}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#5c3270;background-color:#d4c7d9}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#5c3270;border-color:#5c3270}.list-group-item-warning{color:#99460e;background-color:#ffe3d1}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#99460e;background-color:#e6ccbc}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#99460e;border-color:#99460e}.list-group-item-danger{color:#902;background-color:#ffccd7}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#902;background-color:#e6b8c2}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#902;border-color:#902}.list-group-item-light{color:#959596;background-color:#fefefe}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#959596;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#959596;border-color:#959596}.list-group-item-dark{color:#212324;background-color:#d7d8d8}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#212324;background-color:#c2c2c2}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#212324;border-color:#212324}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25);opacity:1}.btn-close:disabled,.btn-close.disabled{pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:0.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{width:max-content;width:-webkit-max-content;width:-moz-max-content;width:-ms-max-content;width:-o-max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-header .btn-close{margin-right:-0.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1055;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0, -50px)}@media(prefers-reduced-motion: reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1050;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;display:-webkit-flex;flex-shrink:0;-webkit-flex-shrink:0;align-items:center;-webkit-align-items:center;justify-content:space-between;-webkit-justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6}.modal-header .btn-close{padding:.5rem .5rem;margin:-0.5rem -0.5rem -0.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;-webkit-flex:1 1 auto;padding:1rem}.modal-footer{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;flex-shrink:0;-webkit-flex-shrink:0;align-items:center;-webkit-align-items:center;justify-content:flex-end;-webkit-justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6}.modal-footer>*{margin:.25rem}@media(min-width: 576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media(min-width: 992px){.modal-lg,.modal-xl{max-width:800px}}@media(min-width: 1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width: 575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width: 767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width: 991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width: 1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width: 1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top,.bs-tooltip-auto[data-popper-placement^=top]{padding:.4rem 0}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:0}.bs-tooltip-top .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-end,.bs-tooltip-auto[data-popper-placement^=right]{padding:0 .4rem}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-end .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-bottom,.bs-tooltip-auto[data-popper-placement^=bottom]{padding:.4rem 0}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:0}.bs-tooltip-bottom .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-start,.bs-tooltip-auto[data-popper-placement^=left]{padding:0 .4rem}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-start .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000}.popover{position:absolute;top:0;left:0 /* rtl:ignore */;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2)}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::before,.popover .popover-arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-0.5rem - 1px)}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-0.5rem - 1px);width:.5rem;height:1rem}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-0.5rem - 1px)}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-bottom .popover-header::before,.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-0.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-0.5rem - 1px);width:.5rem;height:1rem}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#373a3c}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y;-webkit-touch-action:pan-y;-moz-touch-action:pan-y;-ms-touch-action:pan-y;-o-touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-o-backface-visibility:hidden;transition:transform .6s ease-in-out}@media(prefers-reduced-motion: reduce){.carousel-item{transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-start),.active.carousel-item-end{transform:translateX(100%)}.carousel-item-prev:not(.carousel-item-end),.active.carousel-item-start{transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end{z-index:1;opacity:1}.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{z-index:0;opacity:0;transition:opacity 0s .6s}@media(prefers-reduced-motion: reduce){.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;justify-content:center;-webkit-justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:none;border:0;opacity:.5;transition:opacity .15s ease}@media(prefers-reduced-motion: reduce){.carousel-control-prev,.carousel-control-next{transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;display:-webkit-flex;justify-content:center;-webkit-justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;-webkit-flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media(prefers-reduced-motion: reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-prev-icon,.carousel-dark .carousel-control-next-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@keyframes spinner-border{to{transform:rotate(360deg) /* rtl:ignore */}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-0.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-0.125em;background-color:currentColor;border-radius:50%;opacity:0;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media(prefers-reduced-motion: reduce){.spinner-border,.spinner-grow{animation-duration:1.5s;-webkit-animation-duration:1.5s;-moz-animation-duration:1.5s;-ms-animation-duration:1.5s;-o-animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1045;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media(prefers-reduced-motion: reduce){.offcanvas{transition:none}}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;justify-content:space-between;-webkit-justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-0.5rem;margin-right:-0.5rem;margin-bottom:-0.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;-webkit-flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentColor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{mask-image:linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);-webkit-mask-image:linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);mask-size:200% 100%;-webkit-mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{mask-position:-200% 0%;-webkit-mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.link-default{color:#373a3c}.link-default:hover,.link-default:focus{color:#2c2e30}.link-primary{color:#2780e3}.link-primary:hover,.link-primary:focus{color:#1f66b6}.link-secondary{color:#373a3c}.link-secondary:hover,.link-secondary:focus{color:#2c2e30}.link-success{color:#3fb618}.link-success:hover,.link-success:focus{color:#329213}.link-info{color:#9954bb}.link-info:hover,.link-info:focus{color:#7a4396}.link-warning{color:#ff7518}.link-warning:hover,.link-warning:focus{color:#cc5e13}.link-danger{color:#ff0039}.link-danger:hover,.link-danger:focus{color:#cc002e}.link-light{color:#f8f9fa}.link-light:hover,.link-light:focus{color:#f9fafb}.link-dark{color:#373a3c}.link-dark:hover,.link-dark:focus{color:#2c2e30}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio: 100%}.ratio-4x3{--bs-aspect-ratio: calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio: calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio: calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:sticky;top:0;z-index:1020}@media(min-width: 576px){.sticky-sm-top{position:sticky;top:0;z-index:1020}}@media(min-width: 768px){.sticky-md-top{position:sticky;top:0;z-index:1020}}@media(min-width: 992px){.sticky-lg-top{position:sticky;top:0;z-index:1020}}@media(min-width: 1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020}}@media(min-width: 1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020}}.hstack{display:flex;display:-webkit-flex;flex-direction:row;-webkit-flex-direction:row;align-items:center;-webkit-align-items:center;align-self:stretch;-webkit-align-self:stretch}.vstack{display:flex;display:-webkit-flex;flex:1 1 auto;-webkit-flex:1 1 auto;flex-direction:column;-webkit-flex-direction:column;align-self:stretch;-webkit-align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute !important;width:1px !important;height:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0, 0, 0, 0) !important;white-space:nowrap !important;border:0 !important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;-webkit-align-self:stretch;width:1px;min-height:1em;background-color:currentColor;opacity:.25}.align-baseline{vertical-align:baseline !important}.align-top{vertical-align:top !important}.align-middle{vertical-align:middle !important}.align-bottom{vertical-align:bottom !important}.align-text-bottom{vertical-align:text-bottom !important}.align-text-top{vertical-align:text-top !important}.float-start{float:left !important}.float-end{float:right !important}.float-none{float:none !important}.opacity-0{opacity:0 !important}.opacity-25{opacity:.25 !important}.opacity-50{opacity:.5 !important}.opacity-75{opacity:.75 !important}.opacity-100{opacity:1 !important}.overflow-auto{overflow:auto !important}.overflow-hidden{overflow:hidden !important}.overflow-visible{overflow:visible !important}.overflow-scroll{overflow:scroll !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-block{display:block !important}.d-grid{display:grid !important}.d-table{display:table !important}.d-table-row{display:table-row !important}.d-table-cell{display:table-cell !important}.d-flex{display:flex !important}.d-inline-flex{display:inline-flex !important}.d-none{display:none !important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15) !important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075) !important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175) !important}.shadow-none{box-shadow:none !important}.position-static{position:static !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.position-sticky{position:sticky !important}.top-0{top:0 !important}.top-50{top:50% !important}.top-100{top:100% !important}.bottom-0{bottom:0 !important}.bottom-50{bottom:50% !important}.bottom-100{bottom:100% !important}.start-0{left:0 !important}.start-50{left:50% !important}.start-100{left:100% !important}.end-0{right:0 !important}.end-50{right:50% !important}.end-100{right:100% !important}.translate-middle{transform:translate(-50%, -50%) !important}.translate-middle-x{transform:translateX(-50%) !important}.translate-middle-y{transform:translateY(-50%) !important}.border{border:1px solid #dee2e6 !important}.border-0{border:0 !important}.border-top{border-top:1px solid #dee2e6 !important}.border-top-0{border-top:0 !important}.border-end{border-right:1px solid #dee2e6 !important}.border-end-0{border-right:0 !important}.border-bottom{border-bottom:1px solid #dee2e6 !important}.border-bottom-0{border-bottom:0 !important}.border-start{border-left:1px solid #dee2e6 !important}.border-start-0{border-left:0 !important}.border-default{border-color:#373a3c !important}.border-primary{border-color:#2780e3 !important}.border-secondary{border-color:#373a3c !important}.border-success{border-color:#3fb618 !important}.border-info{border-color:#9954bb !important}.border-warning{border-color:#ff7518 !important}.border-danger{border-color:#ff0039 !important}.border-light{border-color:#f8f9fa !important}.border-dark{border-color:#373a3c !important}.border-white{border-color:#fff !important}.border-1{border-width:1px !important}.border-2{border-width:2px !important}.border-3{border-width:3px !important}.border-4{border-width:4px !important}.border-5{border-width:5px !important}.w-25{width:25% !important}.w-50{width:50% !important}.w-75{width:75% !important}.w-100{width:100% !important}.w-auto{width:auto !important}.mw-100{max-width:100% !important}.vw-100{width:100vw !important}.min-vw-100{min-width:100vw !important}.h-25{height:25% !important}.h-50{height:50% !important}.h-75{height:75% !important}.h-100{height:100% !important}.h-auto{height:auto !important}.mh-100{max-height:100% !important}.vh-100{height:100vh !important}.min-vh-100{min-height:100vh !important}.flex-fill{flex:1 1 auto !important}.flex-row{flex-direction:row !important}.flex-column{flex-direction:column !important}.flex-row-reverse{flex-direction:row-reverse !important}.flex-column-reverse{flex-direction:column-reverse !important}.flex-grow-0{flex-grow:0 !important}.flex-grow-1{flex-grow:1 !important}.flex-shrink-0{flex-shrink:0 !important}.flex-shrink-1{flex-shrink:1 !important}.flex-wrap{flex-wrap:wrap !important}.flex-nowrap{flex-wrap:nowrap !important}.flex-wrap-reverse{flex-wrap:wrap-reverse !important}.gap-0{gap:0 !important}.gap-1{gap:.25rem !important}.gap-2{gap:.5rem !important}.gap-3{gap:1rem !important}.gap-4{gap:1.5rem !important}.gap-5{gap:3rem !important}.justify-content-start{justify-content:flex-start !important}.justify-content-end{justify-content:flex-end !important}.justify-content-center{justify-content:center !important}.justify-content-between{justify-content:space-between !important}.justify-content-around{justify-content:space-around !important}.justify-content-evenly{justify-content:space-evenly !important}.align-items-start{align-items:flex-start !important}.align-items-end{align-items:flex-end !important}.align-items-center{align-items:center !important}.align-items-baseline{align-items:baseline !important}.align-items-stretch{align-items:stretch !important}.align-content-start{align-content:flex-start !important}.align-content-end{align-content:flex-end !important}.align-content-center{align-content:center !important}.align-content-between{align-content:space-between !important}.align-content-around{align-content:space-around !important}.align-content-stretch{align-content:stretch !important}.align-self-auto{align-self:auto !important}.align-self-start{align-self:flex-start !important}.align-self-end{align-self:flex-end !important}.align-self-center{align-self:center !important}.align-self-baseline{align-self:baseline !important}.align-self-stretch{align-self:stretch !important}.order-first{order:-1 !important}.order-0{order:0 !important}.order-1{order:1 !important}.order-2{order:2 !important}.order-3{order:3 !important}.order-4{order:4 !important}.order-5{order:5 !important}.order-last{order:6 !important}.m-0{margin:0 !important}.m-1{margin:.25rem !important}.m-2{margin:.5rem !important}.m-3{margin:1rem !important}.m-4{margin:1.5rem !important}.m-5{margin:3rem !important}.m-auto{margin:auto !important}.mx-0{margin-right:0 !important;margin-left:0 !important}.mx-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-3{margin-right:1rem !important;margin-left:1rem !important}.mx-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-5{margin-right:3rem !important;margin-left:3rem !important}.mx-auto{margin-right:auto !important;margin-left:auto !important}.my-0{margin-top:0 !important;margin-bottom:0 !important}.my-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-0{margin-top:0 !important}.mt-1{margin-top:.25rem !important}.mt-2{margin-top:.5rem !important}.mt-3{margin-top:1rem !important}.mt-4{margin-top:1.5rem !important}.mt-5{margin-top:3rem !important}.mt-auto{margin-top:auto !important}.me-0{margin-right:0 !important}.me-1{margin-right:.25rem !important}.me-2{margin-right:.5rem !important}.me-3{margin-right:1rem !important}.me-4{margin-right:1.5rem !important}.me-5{margin-right:3rem !important}.me-auto{margin-right:auto !important}.mb-0{margin-bottom:0 !important}.mb-1{margin-bottom:.25rem !important}.mb-2{margin-bottom:.5rem !important}.mb-3{margin-bottom:1rem !important}.mb-4{margin-bottom:1.5rem !important}.mb-5{margin-bottom:3rem !important}.mb-auto{margin-bottom:auto !important}.ms-0{margin-left:0 !important}.ms-1{margin-left:.25rem !important}.ms-2{margin-left:.5rem !important}.ms-3{margin-left:1rem !important}.ms-4{margin-left:1.5rem !important}.ms-5{margin-left:3rem !important}.ms-auto{margin-left:auto !important}.p-0{padding:0 !important}.p-1{padding:.25rem !important}.p-2{padding:.5rem !important}.p-3{padding:1rem !important}.p-4{padding:1.5rem !important}.p-5{padding:3rem !important}.px-0{padding-right:0 !important;padding-left:0 !important}.px-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-3{padding-right:1rem !important;padding-left:1rem !important}.px-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-5{padding-right:3rem !important;padding-left:3rem !important}.py-0{padding-top:0 !important;padding-bottom:0 !important}.py-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-0{padding-top:0 !important}.pt-1{padding-top:.25rem !important}.pt-2{padding-top:.5rem !important}.pt-3{padding-top:1rem !important}.pt-4{padding-top:1.5rem !important}.pt-5{padding-top:3rem !important}.pe-0{padding-right:0 !important}.pe-1{padding-right:.25rem !important}.pe-2{padding-right:.5rem !important}.pe-3{padding-right:1rem !important}.pe-4{padding-right:1.5rem !important}.pe-5{padding-right:3rem !important}.pb-0{padding-bottom:0 !important}.pb-1{padding-bottom:.25rem !important}.pb-2{padding-bottom:.5rem !important}.pb-3{padding-bottom:1rem !important}.pb-4{padding-bottom:1.5rem !important}.pb-5{padding-bottom:3rem !important}.ps-0{padding-left:0 !important}.ps-1{padding-left:.25rem !important}.ps-2{padding-left:.5rem !important}.ps-3{padding-left:1rem !important}.ps-4{padding-left:1.5rem !important}.ps-5{padding-left:3rem !important}.font-monospace{font-family:var(--bs-font-monospace) !important}.fs-1{font-size:calc(1.345rem + 1.14vw) !important}.fs-2{font-size:calc(1.3rem + 0.6vw) !important}.fs-3{font-size:calc(1.275rem + 0.3vw) !important}.fs-4{font-size:1.25rem !important}.fs-5{font-size:1.1rem !important}.fs-6{font-size:1rem !important}.fst-italic{font-style:italic !important}.fst-normal{font-style:normal !important}.fw-light{font-weight:300 !important}.fw-lighter{font-weight:lighter !important}.fw-normal{font-weight:400 !important}.fw-bold{font-weight:700 !important}.fw-bolder{font-weight:bolder !important}.lh-1{line-height:1 !important}.lh-sm{line-height:1.25 !important}.lh-base{line-height:1.5 !important}.lh-lg{line-height:2 !important}.text-start{text-align:left !important}.text-end{text-align:right !important}.text-center{text-align:center !important}.text-decoration-none{text-decoration:none !important}.text-decoration-underline{text-decoration:underline !important}.text-decoration-line-through{text-decoration:line-through !important}.text-lowercase{text-transform:lowercase !important}.text-uppercase{text-transform:uppercase !important}.text-capitalize{text-transform:capitalize !important}.text-wrap{white-space:normal !important}.text-nowrap{white-space:nowrap !important}.text-break{word-wrap:break-word !important;word-break:break-word !important}.text-default{--bs-text-opacity: 1;color:rgba(var(--bs-default-rgb), var(--bs-text-opacity)) !important}.text-primary{--bs-text-opacity: 1;color:rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important}.text-secondary{--bs-text-opacity: 1;color:rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important}.text-success{--bs-text-opacity: 1;color:rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important}.text-info{--bs-text-opacity: 1;color:rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important}.text-warning{--bs-text-opacity: 1;color:rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important}.text-danger{--bs-text-opacity: 1;color:rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important}.text-light{--bs-text-opacity: 1;color:rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important}.text-dark{--bs-text-opacity: 1;color:rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important}.text-black{--bs-text-opacity: 1;color:rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important}.text-white{--bs-text-opacity: 1;color:rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important}.text-body{--bs-text-opacity: 1;color:rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important}.text-muted{--bs-text-opacity: 1;color:#6c757d !important}.text-black-50{--bs-text-opacity: 1;color:rgba(0,0,0,.5) !important}.text-white-50{--bs-text-opacity: 1;color:rgba(255,255,255,.5) !important}.text-reset{--bs-text-opacity: 1;color:inherit !important}.text-opacity-25{--bs-text-opacity: 0.25}.text-opacity-50{--bs-text-opacity: 0.5}.text-opacity-75{--bs-text-opacity: 0.75}.text-opacity-100{--bs-text-opacity: 1}.bg-default{--bs-bg-opacity: 1;background-color:rgba(var(--bs-default-rgb), var(--bs-bg-opacity)) !important}.bg-primary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important}.bg-secondary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important}.bg-success{--bs-bg-opacity: 1;background-color:rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important}.bg-info{--bs-bg-opacity: 1;background-color:rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important}.bg-warning{--bs-bg-opacity: 1;background-color:rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important}.bg-danger{--bs-bg-opacity: 1;background-color:rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important}.bg-light{--bs-bg-opacity: 1;background-color:rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important}.bg-dark{--bs-bg-opacity: 1;background-color:rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important}.bg-black{--bs-bg-opacity: 1;background-color:rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important}.bg-white{--bs-bg-opacity: 1;background-color:rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important}.bg-body{--bs-bg-opacity: 1;background-color:rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important}.bg-transparent{--bs-bg-opacity: 1;background-color:transparent !important}.bg-opacity-10{--bs-bg-opacity: 0.1}.bg-opacity-25{--bs-bg-opacity: 0.25}.bg-opacity-50{--bs-bg-opacity: 0.5}.bg-opacity-75{--bs-bg-opacity: 0.75}.bg-opacity-100{--bs-bg-opacity: 1}.bg-gradient{background-image:var(--bs-gradient) !important}.user-select-all{user-select:all !important}.user-select-auto{user-select:auto !important}.user-select-none{user-select:none !important}.pe-none{pointer-events:none !important}.pe-auto{pointer-events:auto !important}.rounded{border-radius:.25rem !important}.rounded-0{border-radius:0 !important}.rounded-1{border-radius:.2em !important}.rounded-2{border-radius:.25rem !important}.rounded-3{border-radius:.3rem !important}.rounded-circle{border-radius:50% !important}.rounded-pill{border-radius:50rem !important}.rounded-top{border-top-left-radius:.25rem !important;border-top-right-radius:.25rem !important}.rounded-end{border-top-right-radius:.25rem !important;border-bottom-right-radius:.25rem !important}.rounded-bottom{border-bottom-right-radius:.25rem !important;border-bottom-left-radius:.25rem !important}.rounded-start{border-bottom-left-radius:.25rem !important;border-top-left-radius:.25rem !important}.visible{visibility:visible !important}.invisible{visibility:hidden !important}@media(min-width: 576px){.float-sm-start{float:left !important}.float-sm-end{float:right !important}.float-sm-none{float:none !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-block{display:block !important}.d-sm-grid{display:grid !important}.d-sm-table{display:table !important}.d-sm-table-row{display:table-row !important}.d-sm-table-cell{display:table-cell !important}.d-sm-flex{display:flex !important}.d-sm-inline-flex{display:inline-flex !important}.d-sm-none{display:none !important}.flex-sm-fill{flex:1 1 auto !important}.flex-sm-row{flex-direction:row !important}.flex-sm-column{flex-direction:column !important}.flex-sm-row-reverse{flex-direction:row-reverse !important}.flex-sm-column-reverse{flex-direction:column-reverse !important}.flex-sm-grow-0{flex-grow:0 !important}.flex-sm-grow-1{flex-grow:1 !important}.flex-sm-shrink-0{flex-shrink:0 !important}.flex-sm-shrink-1{flex-shrink:1 !important}.flex-sm-wrap{flex-wrap:wrap !important}.flex-sm-nowrap{flex-wrap:nowrap !important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse !important}.gap-sm-0{gap:0 !important}.gap-sm-1{gap:.25rem !important}.gap-sm-2{gap:.5rem !important}.gap-sm-3{gap:1rem !important}.gap-sm-4{gap:1.5rem !important}.gap-sm-5{gap:3rem !important}.justify-content-sm-start{justify-content:flex-start !important}.justify-content-sm-end{justify-content:flex-end !important}.justify-content-sm-center{justify-content:center !important}.justify-content-sm-between{justify-content:space-between !important}.justify-content-sm-around{justify-content:space-around !important}.justify-content-sm-evenly{justify-content:space-evenly !important}.align-items-sm-start{align-items:flex-start !important}.align-items-sm-end{align-items:flex-end !important}.align-items-sm-center{align-items:center !important}.align-items-sm-baseline{align-items:baseline !important}.align-items-sm-stretch{align-items:stretch !important}.align-content-sm-start{align-content:flex-start !important}.align-content-sm-end{align-content:flex-end !important}.align-content-sm-center{align-content:center !important}.align-content-sm-between{align-content:space-between !important}.align-content-sm-around{align-content:space-around !important}.align-content-sm-stretch{align-content:stretch !important}.align-self-sm-auto{align-self:auto !important}.align-self-sm-start{align-self:flex-start !important}.align-self-sm-end{align-self:flex-end !important}.align-self-sm-center{align-self:center !important}.align-self-sm-baseline{align-self:baseline !important}.align-self-sm-stretch{align-self:stretch !important}.order-sm-first{order:-1 !important}.order-sm-0{order:0 !important}.order-sm-1{order:1 !important}.order-sm-2{order:2 !important}.order-sm-3{order:3 !important}.order-sm-4{order:4 !important}.order-sm-5{order:5 !important}.order-sm-last{order:6 !important}.m-sm-0{margin:0 !important}.m-sm-1{margin:.25rem !important}.m-sm-2{margin:.5rem !important}.m-sm-3{margin:1rem !important}.m-sm-4{margin:1.5rem !important}.m-sm-5{margin:3rem !important}.m-sm-auto{margin:auto !important}.mx-sm-0{margin-right:0 !important;margin-left:0 !important}.mx-sm-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-sm-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-sm-3{margin-right:1rem !important;margin-left:1rem !important}.mx-sm-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-sm-5{margin-right:3rem !important;margin-left:3rem !important}.mx-sm-auto{margin-right:auto !important;margin-left:auto !important}.my-sm-0{margin-top:0 !important;margin-bottom:0 !important}.my-sm-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-sm-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-sm-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-sm-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-sm-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-sm-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-sm-0{margin-top:0 !important}.mt-sm-1{margin-top:.25rem !important}.mt-sm-2{margin-top:.5rem !important}.mt-sm-3{margin-top:1rem !important}.mt-sm-4{margin-top:1.5rem !important}.mt-sm-5{margin-top:3rem !important}.mt-sm-auto{margin-top:auto !important}.me-sm-0{margin-right:0 !important}.me-sm-1{margin-right:.25rem !important}.me-sm-2{margin-right:.5rem !important}.me-sm-3{margin-right:1rem !important}.me-sm-4{margin-right:1.5rem !important}.me-sm-5{margin-right:3rem !important}.me-sm-auto{margin-right:auto !important}.mb-sm-0{margin-bottom:0 !important}.mb-sm-1{margin-bottom:.25rem !important}.mb-sm-2{margin-bottom:.5rem !important}.mb-sm-3{margin-bottom:1rem !important}.mb-sm-4{margin-bottom:1.5rem !important}.mb-sm-5{margin-bottom:3rem !important}.mb-sm-auto{margin-bottom:auto !important}.ms-sm-0{margin-left:0 !important}.ms-sm-1{margin-left:.25rem !important}.ms-sm-2{margin-left:.5rem !important}.ms-sm-3{margin-left:1rem !important}.ms-sm-4{margin-left:1.5rem !important}.ms-sm-5{margin-left:3rem !important}.ms-sm-auto{margin-left:auto !important}.p-sm-0{padding:0 !important}.p-sm-1{padding:.25rem !important}.p-sm-2{padding:.5rem !important}.p-sm-3{padding:1rem !important}.p-sm-4{padding:1.5rem !important}.p-sm-5{padding:3rem !important}.px-sm-0{padding-right:0 !important;padding-left:0 !important}.px-sm-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-sm-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-sm-3{padding-right:1rem !important;padding-left:1rem !important}.px-sm-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-sm-5{padding-right:3rem !important;padding-left:3rem !important}.py-sm-0{padding-top:0 !important;padding-bottom:0 !important}.py-sm-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-sm-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-sm-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-sm-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-sm-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-sm-0{padding-top:0 !important}.pt-sm-1{padding-top:.25rem !important}.pt-sm-2{padding-top:.5rem !important}.pt-sm-3{padding-top:1rem !important}.pt-sm-4{padding-top:1.5rem !important}.pt-sm-5{padding-top:3rem !important}.pe-sm-0{padding-right:0 !important}.pe-sm-1{padding-right:.25rem !important}.pe-sm-2{padding-right:.5rem !important}.pe-sm-3{padding-right:1rem !important}.pe-sm-4{padding-right:1.5rem !important}.pe-sm-5{padding-right:3rem !important}.pb-sm-0{padding-bottom:0 !important}.pb-sm-1{padding-bottom:.25rem !important}.pb-sm-2{padding-bottom:.5rem !important}.pb-sm-3{padding-bottom:1rem !important}.pb-sm-4{padding-bottom:1.5rem !important}.pb-sm-5{padding-bottom:3rem !important}.ps-sm-0{padding-left:0 !important}.ps-sm-1{padding-left:.25rem !important}.ps-sm-2{padding-left:.5rem !important}.ps-sm-3{padding-left:1rem !important}.ps-sm-4{padding-left:1.5rem !important}.ps-sm-5{padding-left:3rem !important}.text-sm-start{text-align:left !important}.text-sm-end{text-align:right !important}.text-sm-center{text-align:center !important}}@media(min-width: 768px){.float-md-start{float:left !important}.float-md-end{float:right !important}.float-md-none{float:none !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-block{display:block !important}.d-md-grid{display:grid !important}.d-md-table{display:table !important}.d-md-table-row{display:table-row !important}.d-md-table-cell{display:table-cell !important}.d-md-flex{display:flex !important}.d-md-inline-flex{display:inline-flex !important}.d-md-none{display:none !important}.flex-md-fill{flex:1 1 auto !important}.flex-md-row{flex-direction:row !important}.flex-md-column{flex-direction:column !important}.flex-md-row-reverse{flex-direction:row-reverse !important}.flex-md-column-reverse{flex-direction:column-reverse !important}.flex-md-grow-0{flex-grow:0 !important}.flex-md-grow-1{flex-grow:1 !important}.flex-md-shrink-0{flex-shrink:0 !important}.flex-md-shrink-1{flex-shrink:1 !important}.flex-md-wrap{flex-wrap:wrap !important}.flex-md-nowrap{flex-wrap:nowrap !important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse !important}.gap-md-0{gap:0 !important}.gap-md-1{gap:.25rem !important}.gap-md-2{gap:.5rem !important}.gap-md-3{gap:1rem !important}.gap-md-4{gap:1.5rem !important}.gap-md-5{gap:3rem !important}.justify-content-md-start{justify-content:flex-start !important}.justify-content-md-end{justify-content:flex-end !important}.justify-content-md-center{justify-content:center !important}.justify-content-md-between{justify-content:space-between !important}.justify-content-md-around{justify-content:space-around !important}.justify-content-md-evenly{justify-content:space-evenly !important}.align-items-md-start{align-items:flex-start !important}.align-items-md-end{align-items:flex-end !important}.align-items-md-center{align-items:center !important}.align-items-md-baseline{align-items:baseline !important}.align-items-md-stretch{align-items:stretch !important}.align-content-md-start{align-content:flex-start !important}.align-content-md-end{align-content:flex-end !important}.align-content-md-center{align-content:center !important}.align-content-md-between{align-content:space-between !important}.align-content-md-around{align-content:space-around !important}.align-content-md-stretch{align-content:stretch !important}.align-self-md-auto{align-self:auto !important}.align-self-md-start{align-self:flex-start !important}.align-self-md-end{align-self:flex-end !important}.align-self-md-center{align-self:center !important}.align-self-md-baseline{align-self:baseline !important}.align-self-md-stretch{align-self:stretch !important}.order-md-first{order:-1 !important}.order-md-0{order:0 !important}.order-md-1{order:1 !important}.order-md-2{order:2 !important}.order-md-3{order:3 !important}.order-md-4{order:4 !important}.order-md-5{order:5 !important}.order-md-last{order:6 !important}.m-md-0{margin:0 !important}.m-md-1{margin:.25rem !important}.m-md-2{margin:.5rem !important}.m-md-3{margin:1rem !important}.m-md-4{margin:1.5rem !important}.m-md-5{margin:3rem !important}.m-md-auto{margin:auto !important}.mx-md-0{margin-right:0 !important;margin-left:0 !important}.mx-md-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-md-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-md-3{margin-right:1rem !important;margin-left:1rem !important}.mx-md-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-md-5{margin-right:3rem !important;margin-left:3rem !important}.mx-md-auto{margin-right:auto !important;margin-left:auto !important}.my-md-0{margin-top:0 !important;margin-bottom:0 !important}.my-md-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-md-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-md-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-md-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-md-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-md-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-md-0{margin-top:0 !important}.mt-md-1{margin-top:.25rem !important}.mt-md-2{margin-top:.5rem !important}.mt-md-3{margin-top:1rem !important}.mt-md-4{margin-top:1.5rem !important}.mt-md-5{margin-top:3rem !important}.mt-md-auto{margin-top:auto !important}.me-md-0{margin-right:0 !important}.me-md-1{margin-right:.25rem !important}.me-md-2{margin-right:.5rem !important}.me-md-3{margin-right:1rem !important}.me-md-4{margin-right:1.5rem !important}.me-md-5{margin-right:3rem !important}.me-md-auto{margin-right:auto !important}.mb-md-0{margin-bottom:0 !important}.mb-md-1{margin-bottom:.25rem !important}.mb-md-2{margin-bottom:.5rem !important}.mb-md-3{margin-bottom:1rem !important}.mb-md-4{margin-bottom:1.5rem !important}.mb-md-5{margin-bottom:3rem !important}.mb-md-auto{margin-bottom:auto !important}.ms-md-0{margin-left:0 !important}.ms-md-1{margin-left:.25rem !important}.ms-md-2{margin-left:.5rem !important}.ms-md-3{margin-left:1rem !important}.ms-md-4{margin-left:1.5rem !important}.ms-md-5{margin-left:3rem !important}.ms-md-auto{margin-left:auto !important}.p-md-0{padding:0 !important}.p-md-1{padding:.25rem !important}.p-md-2{padding:.5rem !important}.p-md-3{padding:1rem !important}.p-md-4{padding:1.5rem !important}.p-md-5{padding:3rem !important}.px-md-0{padding-right:0 !important;padding-left:0 !important}.px-md-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-md-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-md-3{padding-right:1rem !important;padding-left:1rem !important}.px-md-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-md-5{padding-right:3rem !important;padding-left:3rem !important}.py-md-0{padding-top:0 !important;padding-bottom:0 !important}.py-md-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-md-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-md-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-md-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-md-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-md-0{padding-top:0 !important}.pt-md-1{padding-top:.25rem !important}.pt-md-2{padding-top:.5rem !important}.pt-md-3{padding-top:1rem !important}.pt-md-4{padding-top:1.5rem !important}.pt-md-5{padding-top:3rem !important}.pe-md-0{padding-right:0 !important}.pe-md-1{padding-right:.25rem !important}.pe-md-2{padding-right:.5rem !important}.pe-md-3{padding-right:1rem !important}.pe-md-4{padding-right:1.5rem !important}.pe-md-5{padding-right:3rem !important}.pb-md-0{padding-bottom:0 !important}.pb-md-1{padding-bottom:.25rem !important}.pb-md-2{padding-bottom:.5rem !important}.pb-md-3{padding-bottom:1rem !important}.pb-md-4{padding-bottom:1.5rem !important}.pb-md-5{padding-bottom:3rem !important}.ps-md-0{padding-left:0 !important}.ps-md-1{padding-left:.25rem !important}.ps-md-2{padding-left:.5rem !important}.ps-md-3{padding-left:1rem !important}.ps-md-4{padding-left:1.5rem !important}.ps-md-5{padding-left:3rem !important}.text-md-start{text-align:left !important}.text-md-end{text-align:right !important}.text-md-center{text-align:center !important}}@media(min-width: 992px){.float-lg-start{float:left !important}.float-lg-end{float:right !important}.float-lg-none{float:none !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-block{display:block !important}.d-lg-grid{display:grid !important}.d-lg-table{display:table !important}.d-lg-table-row{display:table-row !important}.d-lg-table-cell{display:table-cell !important}.d-lg-flex{display:flex !important}.d-lg-inline-flex{display:inline-flex !important}.d-lg-none{display:none !important}.flex-lg-fill{flex:1 1 auto !important}.flex-lg-row{flex-direction:row !important}.flex-lg-column{flex-direction:column !important}.flex-lg-row-reverse{flex-direction:row-reverse !important}.flex-lg-column-reverse{flex-direction:column-reverse !important}.flex-lg-grow-0{flex-grow:0 !important}.flex-lg-grow-1{flex-grow:1 !important}.flex-lg-shrink-0{flex-shrink:0 !important}.flex-lg-shrink-1{flex-shrink:1 !important}.flex-lg-wrap{flex-wrap:wrap !important}.flex-lg-nowrap{flex-wrap:nowrap !important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse !important}.gap-lg-0{gap:0 !important}.gap-lg-1{gap:.25rem !important}.gap-lg-2{gap:.5rem !important}.gap-lg-3{gap:1rem !important}.gap-lg-4{gap:1.5rem !important}.gap-lg-5{gap:3rem !important}.justify-content-lg-start{justify-content:flex-start !important}.justify-content-lg-end{justify-content:flex-end !important}.justify-content-lg-center{justify-content:center !important}.justify-content-lg-between{justify-content:space-between !important}.justify-content-lg-around{justify-content:space-around !important}.justify-content-lg-evenly{justify-content:space-evenly !important}.align-items-lg-start{align-items:flex-start !important}.align-items-lg-end{align-items:flex-end !important}.align-items-lg-center{align-items:center !important}.align-items-lg-baseline{align-items:baseline !important}.align-items-lg-stretch{align-items:stretch !important}.align-content-lg-start{align-content:flex-start !important}.align-content-lg-end{align-content:flex-end !important}.align-content-lg-center{align-content:center !important}.align-content-lg-between{align-content:space-between !important}.align-content-lg-around{align-content:space-around !important}.align-content-lg-stretch{align-content:stretch !important}.align-self-lg-auto{align-self:auto !important}.align-self-lg-start{align-self:flex-start !important}.align-self-lg-end{align-self:flex-end !important}.align-self-lg-center{align-self:center !important}.align-self-lg-baseline{align-self:baseline !important}.align-self-lg-stretch{align-self:stretch !important}.order-lg-first{order:-1 !important}.order-lg-0{order:0 !important}.order-lg-1{order:1 !important}.order-lg-2{order:2 !important}.order-lg-3{order:3 !important}.order-lg-4{order:4 !important}.order-lg-5{order:5 !important}.order-lg-last{order:6 !important}.m-lg-0{margin:0 !important}.m-lg-1{margin:.25rem !important}.m-lg-2{margin:.5rem !important}.m-lg-3{margin:1rem !important}.m-lg-4{margin:1.5rem !important}.m-lg-5{margin:3rem !important}.m-lg-auto{margin:auto !important}.mx-lg-0{margin-right:0 !important;margin-left:0 !important}.mx-lg-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-lg-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-lg-3{margin-right:1rem !important;margin-left:1rem !important}.mx-lg-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-lg-5{margin-right:3rem !important;margin-left:3rem !important}.mx-lg-auto{margin-right:auto !important;margin-left:auto !important}.my-lg-0{margin-top:0 !important;margin-bottom:0 !important}.my-lg-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-lg-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-lg-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-lg-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-lg-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-lg-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-lg-0{margin-top:0 !important}.mt-lg-1{margin-top:.25rem !important}.mt-lg-2{margin-top:.5rem !important}.mt-lg-3{margin-top:1rem !important}.mt-lg-4{margin-top:1.5rem !important}.mt-lg-5{margin-top:3rem !important}.mt-lg-auto{margin-top:auto !important}.me-lg-0{margin-right:0 !important}.me-lg-1{margin-right:.25rem !important}.me-lg-2{margin-right:.5rem !important}.me-lg-3{margin-right:1rem !important}.me-lg-4{margin-right:1.5rem !important}.me-lg-5{margin-right:3rem !important}.me-lg-auto{margin-right:auto !important}.mb-lg-0{margin-bottom:0 !important}.mb-lg-1{margin-bottom:.25rem !important}.mb-lg-2{margin-bottom:.5rem !important}.mb-lg-3{margin-bottom:1rem !important}.mb-lg-4{margin-bottom:1.5rem !important}.mb-lg-5{margin-bottom:3rem !important}.mb-lg-auto{margin-bottom:auto !important}.ms-lg-0{margin-left:0 !important}.ms-lg-1{margin-left:.25rem !important}.ms-lg-2{margin-left:.5rem !important}.ms-lg-3{margin-left:1rem !important}.ms-lg-4{margin-left:1.5rem !important}.ms-lg-5{margin-left:3rem !important}.ms-lg-auto{margin-left:auto !important}.p-lg-0{padding:0 !important}.p-lg-1{padding:.25rem !important}.p-lg-2{padding:.5rem !important}.p-lg-3{padding:1rem !important}.p-lg-4{padding:1.5rem !important}.p-lg-5{padding:3rem !important}.px-lg-0{padding-right:0 !important;padding-left:0 !important}.px-lg-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-lg-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-lg-3{padding-right:1rem !important;padding-left:1rem !important}.px-lg-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-lg-5{padding-right:3rem !important;padding-left:3rem !important}.py-lg-0{padding-top:0 !important;padding-bottom:0 !important}.py-lg-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-lg-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-lg-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-lg-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-lg-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-lg-0{padding-top:0 !important}.pt-lg-1{padding-top:.25rem !important}.pt-lg-2{padding-top:.5rem !important}.pt-lg-3{padding-top:1rem !important}.pt-lg-4{padding-top:1.5rem !important}.pt-lg-5{padding-top:3rem !important}.pe-lg-0{padding-right:0 !important}.pe-lg-1{padding-right:.25rem !important}.pe-lg-2{padding-right:.5rem !important}.pe-lg-3{padding-right:1rem !important}.pe-lg-4{padding-right:1.5rem !important}.pe-lg-5{padding-right:3rem !important}.pb-lg-0{padding-bottom:0 !important}.pb-lg-1{padding-bottom:.25rem !important}.pb-lg-2{padding-bottom:.5rem !important}.pb-lg-3{padding-bottom:1rem !important}.pb-lg-4{padding-bottom:1.5rem !important}.pb-lg-5{padding-bottom:3rem !important}.ps-lg-0{padding-left:0 !important}.ps-lg-1{padding-left:.25rem !important}.ps-lg-2{padding-left:.5rem !important}.ps-lg-3{padding-left:1rem !important}.ps-lg-4{padding-left:1.5rem !important}.ps-lg-5{padding-left:3rem !important}.text-lg-start{text-align:left !important}.text-lg-end{text-align:right !important}.text-lg-center{text-align:center !important}}@media(min-width: 1200px){.float-xl-start{float:left !important}.float-xl-end{float:right !important}.float-xl-none{float:none !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-block{display:block !important}.d-xl-grid{display:grid !important}.d-xl-table{display:table !important}.d-xl-table-row{display:table-row !important}.d-xl-table-cell{display:table-cell !important}.d-xl-flex{display:flex !important}.d-xl-inline-flex{display:inline-flex !important}.d-xl-none{display:none !important}.flex-xl-fill{flex:1 1 auto !important}.flex-xl-row{flex-direction:row !important}.flex-xl-column{flex-direction:column !important}.flex-xl-row-reverse{flex-direction:row-reverse !important}.flex-xl-column-reverse{flex-direction:column-reverse !important}.flex-xl-grow-0{flex-grow:0 !important}.flex-xl-grow-1{flex-grow:1 !important}.flex-xl-shrink-0{flex-shrink:0 !important}.flex-xl-shrink-1{flex-shrink:1 !important}.flex-xl-wrap{flex-wrap:wrap !important}.flex-xl-nowrap{flex-wrap:nowrap !important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse !important}.gap-xl-0{gap:0 !important}.gap-xl-1{gap:.25rem !important}.gap-xl-2{gap:.5rem !important}.gap-xl-3{gap:1rem !important}.gap-xl-4{gap:1.5rem !important}.gap-xl-5{gap:3rem !important}.justify-content-xl-start{justify-content:flex-start !important}.justify-content-xl-end{justify-content:flex-end !important}.justify-content-xl-center{justify-content:center !important}.justify-content-xl-between{justify-content:space-between !important}.justify-content-xl-around{justify-content:space-around !important}.justify-content-xl-evenly{justify-content:space-evenly !important}.align-items-xl-start{align-items:flex-start !important}.align-items-xl-end{align-items:flex-end !important}.align-items-xl-center{align-items:center !important}.align-items-xl-baseline{align-items:baseline !important}.align-items-xl-stretch{align-items:stretch !important}.align-content-xl-start{align-content:flex-start !important}.align-content-xl-end{align-content:flex-end !important}.align-content-xl-center{align-content:center !important}.align-content-xl-between{align-content:space-between !important}.align-content-xl-around{align-content:space-around !important}.align-content-xl-stretch{align-content:stretch !important}.align-self-xl-auto{align-self:auto !important}.align-self-xl-start{align-self:flex-start !important}.align-self-xl-end{align-self:flex-end !important}.align-self-xl-center{align-self:center !important}.align-self-xl-baseline{align-self:baseline !important}.align-self-xl-stretch{align-self:stretch !important}.order-xl-first{order:-1 !important}.order-xl-0{order:0 !important}.order-xl-1{order:1 !important}.order-xl-2{order:2 !important}.order-xl-3{order:3 !important}.order-xl-4{order:4 !important}.order-xl-5{order:5 !important}.order-xl-last{order:6 !important}.m-xl-0{margin:0 !important}.m-xl-1{margin:.25rem !important}.m-xl-2{margin:.5rem !important}.m-xl-3{margin:1rem !important}.m-xl-4{margin:1.5rem !important}.m-xl-5{margin:3rem !important}.m-xl-auto{margin:auto !important}.mx-xl-0{margin-right:0 !important;margin-left:0 !important}.mx-xl-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-xl-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-xl-3{margin-right:1rem !important;margin-left:1rem !important}.mx-xl-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-xl-5{margin-right:3rem !important;margin-left:3rem !important}.mx-xl-auto{margin-right:auto !important;margin-left:auto !important}.my-xl-0{margin-top:0 !important;margin-bottom:0 !important}.my-xl-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-xl-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-xl-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-xl-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-xl-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-xl-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-xl-0{margin-top:0 !important}.mt-xl-1{margin-top:.25rem !important}.mt-xl-2{margin-top:.5rem !important}.mt-xl-3{margin-top:1rem !important}.mt-xl-4{margin-top:1.5rem !important}.mt-xl-5{margin-top:3rem !important}.mt-xl-auto{margin-top:auto !important}.me-xl-0{margin-right:0 !important}.me-xl-1{margin-right:.25rem !important}.me-xl-2{margin-right:.5rem !important}.me-xl-3{margin-right:1rem !important}.me-xl-4{margin-right:1.5rem !important}.me-xl-5{margin-right:3rem !important}.me-xl-auto{margin-right:auto !important}.mb-xl-0{margin-bottom:0 !important}.mb-xl-1{margin-bottom:.25rem !important}.mb-xl-2{margin-bottom:.5rem !important}.mb-xl-3{margin-bottom:1rem !important}.mb-xl-4{margin-bottom:1.5rem !important}.mb-xl-5{margin-bottom:3rem !important}.mb-xl-auto{margin-bottom:auto !important}.ms-xl-0{margin-left:0 !important}.ms-xl-1{margin-left:.25rem !important}.ms-xl-2{margin-left:.5rem !important}.ms-xl-3{margin-left:1rem !important}.ms-xl-4{margin-left:1.5rem !important}.ms-xl-5{margin-left:3rem !important}.ms-xl-auto{margin-left:auto !important}.p-xl-0{padding:0 !important}.p-xl-1{padding:.25rem !important}.p-xl-2{padding:.5rem !important}.p-xl-3{padding:1rem !important}.p-xl-4{padding:1.5rem !important}.p-xl-5{padding:3rem !important}.px-xl-0{padding-right:0 !important;padding-left:0 !important}.px-xl-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-xl-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-xl-3{padding-right:1rem !important;padding-left:1rem !important}.px-xl-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-xl-5{padding-right:3rem !important;padding-left:3rem !important}.py-xl-0{padding-top:0 !important;padding-bottom:0 !important}.py-xl-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-xl-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-xl-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-xl-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-xl-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-xl-0{padding-top:0 !important}.pt-xl-1{padding-top:.25rem !important}.pt-xl-2{padding-top:.5rem !important}.pt-xl-3{padding-top:1rem !important}.pt-xl-4{padding-top:1.5rem !important}.pt-xl-5{padding-top:3rem !important}.pe-xl-0{padding-right:0 !important}.pe-xl-1{padding-right:.25rem !important}.pe-xl-2{padding-right:.5rem !important}.pe-xl-3{padding-right:1rem !important}.pe-xl-4{padding-right:1.5rem !important}.pe-xl-5{padding-right:3rem !important}.pb-xl-0{padding-bottom:0 !important}.pb-xl-1{padding-bottom:.25rem !important}.pb-xl-2{padding-bottom:.5rem !important}.pb-xl-3{padding-bottom:1rem !important}.pb-xl-4{padding-bottom:1.5rem !important}.pb-xl-5{padding-bottom:3rem !important}.ps-xl-0{padding-left:0 !important}.ps-xl-1{padding-left:.25rem !important}.ps-xl-2{padding-left:.5rem !important}.ps-xl-3{padding-left:1rem !important}.ps-xl-4{padding-left:1.5rem !important}.ps-xl-5{padding-left:3rem !important}.text-xl-start{text-align:left !important}.text-xl-end{text-align:right !important}.text-xl-center{text-align:center !important}}@media(min-width: 1400px){.float-xxl-start{float:left !important}.float-xxl-end{float:right !important}.float-xxl-none{float:none !important}.d-xxl-inline{display:inline !important}.d-xxl-inline-block{display:inline-block !important}.d-xxl-block{display:block !important}.d-xxl-grid{display:grid !important}.d-xxl-table{display:table !important}.d-xxl-table-row{display:table-row !important}.d-xxl-table-cell{display:table-cell !important}.d-xxl-flex{display:flex !important}.d-xxl-inline-flex{display:inline-flex !important}.d-xxl-none{display:none !important}.flex-xxl-fill{flex:1 1 auto !important}.flex-xxl-row{flex-direction:row !important}.flex-xxl-column{flex-direction:column !important}.flex-xxl-row-reverse{flex-direction:row-reverse !important}.flex-xxl-column-reverse{flex-direction:column-reverse !important}.flex-xxl-grow-0{flex-grow:0 !important}.flex-xxl-grow-1{flex-grow:1 !important}.flex-xxl-shrink-0{flex-shrink:0 !important}.flex-xxl-shrink-1{flex-shrink:1 !important}.flex-xxl-wrap{flex-wrap:wrap !important}.flex-xxl-nowrap{flex-wrap:nowrap !important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse !important}.gap-xxl-0{gap:0 !important}.gap-xxl-1{gap:.25rem !important}.gap-xxl-2{gap:.5rem !important}.gap-xxl-3{gap:1rem !important}.gap-xxl-4{gap:1.5rem !important}.gap-xxl-5{gap:3rem !important}.justify-content-xxl-start{justify-content:flex-start !important}.justify-content-xxl-end{justify-content:flex-end !important}.justify-content-xxl-center{justify-content:center !important}.justify-content-xxl-between{justify-content:space-between !important}.justify-content-xxl-around{justify-content:space-around !important}.justify-content-xxl-evenly{justify-content:space-evenly !important}.align-items-xxl-start{align-items:flex-start !important}.align-items-xxl-end{align-items:flex-end !important}.align-items-xxl-center{align-items:center !important}.align-items-xxl-baseline{align-items:baseline !important}.align-items-xxl-stretch{align-items:stretch !important}.align-content-xxl-start{align-content:flex-start !important}.align-content-xxl-end{align-content:flex-end !important}.align-content-xxl-center{align-content:center !important}.align-content-xxl-between{align-content:space-between !important}.align-content-xxl-around{align-content:space-around !important}.align-content-xxl-stretch{align-content:stretch !important}.align-self-xxl-auto{align-self:auto !important}.align-self-xxl-start{align-self:flex-start !important}.align-self-xxl-end{align-self:flex-end !important}.align-self-xxl-center{align-self:center !important}.align-self-xxl-baseline{align-self:baseline !important}.align-self-xxl-stretch{align-self:stretch !important}.order-xxl-first{order:-1 !important}.order-xxl-0{order:0 !important}.order-xxl-1{order:1 !important}.order-xxl-2{order:2 !important}.order-xxl-3{order:3 !important}.order-xxl-4{order:4 !important}.order-xxl-5{order:5 !important}.order-xxl-last{order:6 !important}.m-xxl-0{margin:0 !important}.m-xxl-1{margin:.25rem !important}.m-xxl-2{margin:.5rem !important}.m-xxl-3{margin:1rem !important}.m-xxl-4{margin:1.5rem !important}.m-xxl-5{margin:3rem !important}.m-xxl-auto{margin:auto !important}.mx-xxl-0{margin-right:0 !important;margin-left:0 !important}.mx-xxl-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-xxl-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-xxl-3{margin-right:1rem !important;margin-left:1rem !important}.mx-xxl-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-xxl-5{margin-right:3rem !important;margin-left:3rem !important}.mx-xxl-auto{margin-right:auto !important;margin-left:auto !important}.my-xxl-0{margin-top:0 !important;margin-bottom:0 !important}.my-xxl-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-xxl-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-xxl-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-xxl-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-xxl-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-xxl-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-xxl-0{margin-top:0 !important}.mt-xxl-1{margin-top:.25rem !important}.mt-xxl-2{margin-top:.5rem !important}.mt-xxl-3{margin-top:1rem !important}.mt-xxl-4{margin-top:1.5rem !important}.mt-xxl-5{margin-top:3rem !important}.mt-xxl-auto{margin-top:auto !important}.me-xxl-0{margin-right:0 !important}.me-xxl-1{margin-right:.25rem !important}.me-xxl-2{margin-right:.5rem !important}.me-xxl-3{margin-right:1rem !important}.me-xxl-4{margin-right:1.5rem !important}.me-xxl-5{margin-right:3rem !important}.me-xxl-auto{margin-right:auto !important}.mb-xxl-0{margin-bottom:0 !important}.mb-xxl-1{margin-bottom:.25rem !important}.mb-xxl-2{margin-bottom:.5rem !important}.mb-xxl-3{margin-bottom:1rem !important}.mb-xxl-4{margin-bottom:1.5rem !important}.mb-xxl-5{margin-bottom:3rem !important}.mb-xxl-auto{margin-bottom:auto !important}.ms-xxl-0{margin-left:0 !important}.ms-xxl-1{margin-left:.25rem !important}.ms-xxl-2{margin-left:.5rem !important}.ms-xxl-3{margin-left:1rem !important}.ms-xxl-4{margin-left:1.5rem !important}.ms-xxl-5{margin-left:3rem !important}.ms-xxl-auto{margin-left:auto !important}.p-xxl-0{padding:0 !important}.p-xxl-1{padding:.25rem !important}.p-xxl-2{padding:.5rem !important}.p-xxl-3{padding:1rem !important}.p-xxl-4{padding:1.5rem !important}.p-xxl-5{padding:3rem !important}.px-xxl-0{padding-right:0 !important;padding-left:0 !important}.px-xxl-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-xxl-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-xxl-3{padding-right:1rem !important;padding-left:1rem !important}.px-xxl-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-xxl-5{padding-right:3rem !important;padding-left:3rem !important}.py-xxl-0{padding-top:0 !important;padding-bottom:0 !important}.py-xxl-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-xxl-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-xxl-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-xxl-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-xxl-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-xxl-0{padding-top:0 !important}.pt-xxl-1{padding-top:.25rem !important}.pt-xxl-2{padding-top:.5rem !important}.pt-xxl-3{padding-top:1rem !important}.pt-xxl-4{padding-top:1.5rem !important}.pt-xxl-5{padding-top:3rem !important}.pe-xxl-0{padding-right:0 !important}.pe-xxl-1{padding-right:.25rem !important}.pe-xxl-2{padding-right:.5rem !important}.pe-xxl-3{padding-right:1rem !important}.pe-xxl-4{padding-right:1.5rem !important}.pe-xxl-5{padding-right:3rem !important}.pb-xxl-0{padding-bottom:0 !important}.pb-xxl-1{padding-bottom:.25rem !important}.pb-xxl-2{padding-bottom:.5rem !important}.pb-xxl-3{padding-bottom:1rem !important}.pb-xxl-4{padding-bottom:1.5rem !important}.pb-xxl-5{padding-bottom:3rem !important}.ps-xxl-0{padding-left:0 !important}.ps-xxl-1{padding-left:.25rem !important}.ps-xxl-2{padding-left:.5rem !important}.ps-xxl-3{padding-left:1rem !important}.ps-xxl-4{padding-left:1.5rem !important}.ps-xxl-5{padding-left:3rem !important}.text-xxl-start{text-align:left !important}.text-xxl-end{text-align:right !important}.text-xxl-center{text-align:center !important}}.bg-default{color:#fff}.bg-primary{color:#fff}.bg-secondary{color:#fff}.bg-success{color:#fff}.bg-info{color:#fff}.bg-warning{color:#fff}.bg-danger{color:#fff}.bg-light{color:#000}.bg-dark{color:#fff}@media(min-width: 1200px){.fs-1{font-size:2.2rem !important}.fs-2{font-size:1.75rem !important}.fs-3{font-size:1.5rem !important}}@media print{.d-print-inline{display:inline !important}.d-print-inline-block{display:inline-block !important}.d-print-block{display:block !important}.d-print-grid{display:grid !important}.d-print-table{display:table !important}.d-print-table-row{display:table-row !important}.d-print-table-cell{display:table-cell !important}.d-print-flex{display:flex !important}.d-print-inline-flex{display:inline-flex !important}.d-print-none{display:none !important}}.quarto-container{min-height:calc(100vh - 132px)}footer.footer .nav-footer,#quarto-header nav{padding-left:1em;padding-right:1em}nav[role=doc-toc]{padding-left:.5em}#quarto-content>*{padding-top:14px}@media(max-width: 991.98px){#quarto-content>*{padding-top:0}#quarto-content .subtitle{padding-top:14px}#quarto-content section:first-of-type h2:first-of-type,#quarto-content section:first-of-type .h2:first-of-type{margin-top:1rem}}.headroom-target,header.headroom{will-change:transform;transition:transform 200ms linear;transition:position 200ms linear}header.headroom--pinned{transform:translateY(0%)}header.headroom--unpinned{transform:translateY(-100%)}.navbar-container{width:100%}.navbar-brand{text-overflow:ellipsis;overflow:hidden;max-width:calc(100% - 85px)}.navbar-toggler{flex-basis:content;flex-shrink:0}.navbar-brand>img{max-height:24px;width:auto;padding-right:6px}nav .nav-item:not(.compact){padding-top:1px}nav .nav-link i,nav .dropdown-item i{padding-right:1px}.navbar-expand-lg .navbar-nav .nav-link{padding-left:.6rem;padding-right:.6rem}nav .nav-item.compact .nav-link{padding-left:.5rem;padding-right:.5rem;font-size:1.1rem}.navbar-nav .dropdown-menu{min-width:220px;font-size:.9rem}.navbar .navbar-nav .nav-link.dropdown-toggle::after{opacity:.75;vertical-align:.175em}.navbar ul.dropdown-menu{padding-top:0;padding-bottom:0}.navbar .dropdown-header{text-transform:uppercase;font-size:.8rem;padding:0 .5rem}.navbar .dropdown-item{padding:.4rem .5rem}.navbar .dropdown-item>i.bi{margin-left:.1rem;margin-right:.25em}.sidebar #quarto-search{margin-top:-1px}.sidebar #quarto-search svg.aa-SubmitIcon{width:16px;height:16px}.sidebar-navigation a{color:inherit}.sidebar-title{margin-top:.25rem;padding-bottom:.5rem;font-size:1.3rem;line-height:1.6rem;visibility:visible}.sidebar-title>a{font-size:inherit;text-decoration:none}.sidebar-title .sidebar-tools-main{margin-top:-6px}.sidebar-header-stacked .sidebar-title{margin-top:.6rem}.sidebar-logo{max-width:90%;padding-bottom:.5rem}.sidebar-logo-link{text-decoration:none}.sidebar-navigation li a{text-decoration:none}.sidebar-navigation .sidebar-tool{opacity:.7;font-size:.875rem}#quarto-sidebar>nav>.sidebar-tools-main{margin-left:14px}.sidebar-tools-main{margin-left:0px}.sidebar-tools-main:not(.tools-wide){display:inline-block;vertical-align:middle}.sidebar-tools-main.tools-wide{padding-top:.3em}.sidebar-navigation .sidebar-tool.dropdown-toggle::after{display:none}.sidebar.sidebar-navigation>*{padding-top:1em}.sidebar-item{margin-bottom:.2em}.sidebar-section{margin-top:.2em;padding-left:.5em;padding-bottom:.2em}.sidebar-item .sidebar-item-container{display:flex;justify-content:space-between}.sidebar-item .sidebar-item-toggle .bi{font-size:.7rem;text-align:center}.sidebar-navigation .sidebar-divider{margin-left:0;margin-right:0;margin-top:.5rem;margin-bottom:.5rem}@media(max-width: 767.98px){.quarto-secondary-nav{display:block}}@media(min-width: 992px){.quarto-secondary-nav{display:none}}.quarto-secondary-nav .quarto-btn-toggle{color:#545555;padding-right:0}.quarto-secondary-nav[aria-expanded=false] .quarto-btn-toggle .bi-chevron-right::before{transform:none}.quarto-secondary-nav[aria-expanded=true] .quarto-btn-toggle .bi-chevron-right::before{transform:rotate(90deg)}.quarto-secondary-nav .quarto-btn-toggle .bi-chevron-right::before{transition:transform 200ms ease}.quarto-secondary-nav{cursor:pointer}.quarto-secondary-nav-title{margin-top:.3em;color:#545555;padding-top:4px}div.sidebar-item-container{color:#545555}div.sidebar-item-container:hover,div.sidebar-item-container:focus{color:rgba(26,86,152,.8)}div.sidebar-item-container.disabled{color:rgba(84,85,85,.75)}div.sidebar-item-container .active,div.sidebar-item-container .show>.nav-link,div.sidebar-item-container .sidebar-link>code{color:#1a5698}div.sidebar.sidebar-navigation.rollup.quarto-sidebar-toggle-contents,nav.sidebar.sidebar-navigation:not(.rollup){background-color:#f8f9fa}.sidebar.sidebar-navigation:not(.rollup){border-right:1px solid #dee2e6 !important}@media(max-width: 991.98px){.sidebar-navigation .sidebar-item a,.nav-page .nav-page-text,.sidebar-navigation{font-size:1rem}.sidebar-navigation ul.sidebar-section.depth1 .sidebar-section-item{font-size:1.1rem}.sidebar-logo{display:none}.sidebar.sidebar-navigation{position:static;border-bottom:1px solid #dee2e6}.sidebar.sidebar-navigation.collapsing{position:fixed;z-index:1000}.sidebar.sidebar-navigation.show{position:fixed;z-index:1000}.sidebar.sidebar-navigation{transition:height .15s linear;width:100%}nav.quarto-secondary-nav{background-color:#f8f9fa;border-bottom:1px solid #dee2e6}.sidebar .sidebar-footer{visibility:visible;padding-top:1rem;position:inherit}.sidebar-tools-collapse{display:block}}@media(min-width: 992px){#quarto-sidebar{display:flex;flex-direction:column}.nav-page .nav-page-text,.sidebar-navigation .sidebar-section .sidebar-item{font-size:.875rem}.sidebar-navigation .sidebar-item{font-size:.925rem}.sidebar.sidebar-navigation{display:block;position:sticky}.sidebar-search{width:100%}.sidebar .sidebar-footer{visibility:visible}}.sidebar .sidebar-footer{padding:.5rem 1rem;align-self:flex-end;color:#6c757d;width:100%}#quarto-sidebar{width:100%;padding-right:1em;color:#545555}.quarto-sidebar-footer{font-size:.875em}.sidebar-section .bi-chevron-right{vertical-align:middle}.sidebar-section a .bi-chevron-right::before{transform:rotate(90deg)}.sidebar-section a.collapsed .bi-chevron-right::before{transform:none}.sidebar-section .bi-chevron-right::before{font-size:.9em;transition:transform 200ms ease}.notransition{-webkit-transition:none !important;-moz-transition:none !important;-o-transition:none !important;transition:none !important}.btn:focus:not(:focus-visible){box-shadow:none}.page-navigation{display:flex;justify-content:space-between}.nav-page{padding-bottom:.75em}.nav-page .bi{font-size:1.8rem;vertical-align:middle}.nav-page .nav-page-text{padding-left:.25em;padding-right:.25em}.nav-page a{color:#6c757d;text-decoration:none;display:flex;align-items:center}.nav-page a:hover{color:#1f66b6}.toc-actions{display:flex}.toc-actions p{margin-block-start:0;margin-block-end:0}.toc-actions a{text-decoration:none;color:inherit;font-weight:400}.toc-actions a:hover{color:#1f66b6}.toc-actions .action-links{margin-left:4px}.sidebar nav[role=doc-toc] .toc-actions .bi{margin-left:-4px;font-size:.7rem;color:#6c757d}.sidebar nav[role=doc-toc] .toc-actions .bi:before{padding-top:3px}#quarto-margin-sidebar .toc-actions .bi:before{margin-top:.3rem;font-size:.7rem;color:#6c757d;vertical-align:top}.sidebar nav[role=doc-toc] .toc-actions>div:first-of-type{margin-top:-3px}#quarto-margin-sidebar .toc-actions p,.sidebar nav[role=doc-toc] .toc-actions p{font-size:.875rem}.sidebar nav[role=doc-toc] .header-section-number{display:none}.nav-footer{display:flex;justify-content:center;align-items:center;text-align:center;padding-top:.5rem;padding-bottom:.5rem;background-color:#fff}body.nav-fixed{padding-top:66px}.nav-footer-contents{color:#6c757d;margin-top:.25rem}.nav-footer{min-height:3.5em;color:#757575}.nav-footer a{color:#757575}.nav-footer .nav-footer-left{font-size:.825em}.nav-footer .nav-footer-center{font-size:.825em}.nav-footer .nav-footer-right{font-size:.825em}.nav-footer-left .footer-items,.nav-footer-center .footer-items,.nav-footer-right .footer-items{display:flex;padding-top:.3em;padding-bottom:.3em;margin-bottom:0em}.nav-footer-left .footer-items .nav-link,.nav-footer-center .footer-items .nav-link,.nav-footer-right .footer-items .nav-link{padding-left:.6em;padding-right:.6em}.nav-footer-left{margin-right:auto}.nav-footer-center{min-height:3em;position:absolute;text-align:center}.nav-footer-center .footer-items{justify-content:center}@media(max-width: 767.98px){.nav-footer-center{margin-top:3em}}.nav-footer-right{margin-left:auto}.navbar .quarto-reader-toggle{padding-left:.4em;padding-right:0}.navbar .quarto-reader-toggle.reader .quarto-reader-toggle-btn{background-color:#fdfeff;border-radius:3px}.quarto-reader-toggle.reader.sidebar-tool .quarto-reader-toggle-btn{background-color:#545555;border-radius:3px}.quarto-reader-toggle.sidebar-tool{padding-left:.3em}.quarto-reader-toggle .quarto-reader-toggle-btn{display:inline-flex;padding-left:.1em;padding-right:.3em;text-align:center}.navbar .quarto-reader-toggle:not(.reader) .bi::before{background-image:url('data:image/svg+xml,')}.navbar .quarto-reader-toggle.reader .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-reader-toggle:not(.reader) .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-reader-toggle.reader .bi::before{background-image:url('data:image/svg+xml,')}.aa-DetachedOverlay ul.aa-List,#quarto-search-results ul.aa-List{list-style:none;padding-left:0}.aa-DetachedOverlay .aa-Panel,#quarto-search-results .aa-Panel{background-color:#fff;position:absolute;z-index:2000}#quarto-search-results .aa-Panel{max-width:400px}#quarto-search input{font-size:.925rem}@media(min-width: 992px){.navbar #quarto-search{margin-left:1rem}}#quarto-sidebar .sidebar-search .aa-Autocomplete{width:100%}.navbar .aa-Autocomplete .aa-Form{width:180px}.navbar #quarto-search.type-overlay .aa-Autocomplete{width:40px}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form{background-color:inherit;border:none}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form:focus-within{box-shadow:none;outline:none}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form .aa-InputWrapper{display:none}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form .aa-InputWrapper:focus-within{display:inherit}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form .aa-Label svg,.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form .aa-LoadingIndicator svg{width:26px;height:26px;color:#fdfeff;opacity:1}.navbar #quarto-search.type-overlay .aa-Autocomplete svg.aa-SubmitIcon{width:26px;height:26px;color:#fdfeff;opacity:1}.aa-Autocomplete .aa-Form,.aa-DetachedFormContainer .aa-Form{align-items:center;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;color:#373a3c;display:flex;line-height:1em;margin:0;position:relative;width:100%}.aa-Autocomplete .aa-Form:focus-within,.aa-DetachedFormContainer .aa-Form:focus-within{box-shadow:rgba(39,128,227,.6) 0 0 0 1px;outline:currentColor none medium}.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix{align-items:center;display:flex;flex-shrink:0;order:1}.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-Label,.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-Label,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator{cursor:initial;flex-shrink:0;padding:0;text-align:left}.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-Label svg,.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator svg,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-Label svg,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator svg{color:#373a3c;opacity:.5}.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-SubmitButton,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-SubmitButton{appearance:none;background:none;border:0;margin:0}.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator{align-items:center;display:flex;justify-content:center}.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator[hidden],.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator[hidden]{display:none}.aa-Autocomplete .aa-Form .aa-InputWrapper,.aa-DetachedFormContainer .aa-Form .aa-InputWrapper{order:3;position:relative;width:100%}.aa-Autocomplete .aa-Form .aa-InputWrapper .aa-Input,.aa-DetachedFormContainer .aa-Form .aa-InputWrapper .aa-Input{appearance:none;background:none;border:0;color:#373a3c;font:inherit;height:calc(1.5em + (0.1rem + 2px));padding:0;width:100%}.aa-Autocomplete .aa-Form .aa-InputWrapper .aa-Input::placeholder,.aa-DetachedFormContainer .aa-Form .aa-InputWrapper .aa-Input::placeholder{color:#373a3c;opacity:.8}.aa-Autocomplete .aa-Form .aa-InputWrapper .aa-Input:focus,.aa-DetachedFormContainer .aa-Form .aa-InputWrapper .aa-Input:focus{border-color:none;box-shadow:none;outline:none}.aa-Autocomplete .aa-Form .aa-InputWrapper .aa-Input::-webkit-search-decoration,.aa-Autocomplete .aa-Form .aa-InputWrapper .aa-Input::-webkit-search-cancel-button,.aa-Autocomplete .aa-Form .aa-InputWrapper .aa-Input::-webkit-search-results-button,.aa-Autocomplete .aa-Form .aa-InputWrapper .aa-Input::-webkit-search-results-decoration,.aa-DetachedFormContainer .aa-Form .aa-InputWrapper .aa-Input::-webkit-search-decoration,.aa-DetachedFormContainer .aa-Form .aa-InputWrapper .aa-Input::-webkit-search-cancel-button,.aa-DetachedFormContainer .aa-Form .aa-InputWrapper .aa-Input::-webkit-search-results-button,.aa-DetachedFormContainer .aa-Form .aa-InputWrapper .aa-Input::-webkit-search-results-decoration{display:none}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix{align-items:center;display:flex;order:4}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-ClearButton,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-ClearButton{align-items:center;background:none;border:0;color:#373a3c;opacity:.8;cursor:pointer;display:flex;margin:0;width:calc(1.5em + (0.1rem + 2px))}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-ClearButton:hover,.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-ClearButton:focus,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-ClearButton:hover,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-ClearButton:focus{color:#373a3c;opacity:.8}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-ClearButton[hidden],.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-ClearButton[hidden]{display:none}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-ClearButton svg,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-ClearButton svg{width:calc(1.5em + 0.75rem + 2px)}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-CopyButton,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-CopyButton{border:none;align-items:center;background:none;color:#373a3c;opacity:.4;font-size:.7rem;cursor:pointer;display:none;margin:0;width:calc(1em + (0.1rem + 2px))}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-CopyButton:hover,.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-CopyButton:focus,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-CopyButton:hover,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-CopyButton:focus{color:#373a3c;opacity:.8}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-CopyButton[hidden],.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-CopyButton[hidden]{display:none}#quarto-search-results .aa-Panel{border:solid #ced4da 1px}#quarto-search-results .aa-SourceNoResults{width:398px}.aa-DetachedOverlay .aa-Panel,#quarto-search-results .aa-Panel{max-height:65vh;overflow-y:auto;font-size:.925rem}.aa-DetachedOverlay .aa-SourceNoResults,#quarto-search-results .aa-SourceNoResults{height:60px;display:flex;justify-content:center;align-items:center}.aa-DetachedOverlay .search-error,#quarto-search-results .search-error{padding-top:10px;padding-left:20px;padding-right:20px;cursor:default}.aa-DetachedOverlay .search-error .search-error-title,#quarto-search-results .search-error .search-error-title{font-size:1.1rem;margin-bottom:.5rem}.aa-DetachedOverlay .search-error .search-error-title .search-error-icon,#quarto-search-results .search-error .search-error-title .search-error-icon{margin-right:8px}.aa-DetachedOverlay .search-error .search-error-text,#quarto-search-results .search-error .search-error-text{font-weight:300}.aa-DetachedOverlay .search-result-text,#quarto-search-results .search-result-text{font-weight:300;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-height:1.2rem;max-height:2.4rem}.aa-DetachedOverlay .aa-SourceHeader .search-result-header,#quarto-search-results .aa-SourceHeader .search-result-header{font-size:.875rem;background-color:#f2f2f2;padding-left:14px;padding-bottom:4px;padding-top:4px}.aa-DetachedOverlay .aa-SourceHeader .search-result-header-no-results,#quarto-search-results .aa-SourceHeader .search-result-header-no-results{display:none}.aa-DetachedOverlay .aa-SourceFooter .algolia-search-logo,#quarto-search-results .aa-SourceFooter .algolia-search-logo{width:110px;opacity:.85;margin:8px;float:right}.aa-DetachedOverlay .search-result-section,#quarto-search-results .search-result-section{font-size:.925em}.aa-DetachedOverlay a.search-result-link,#quarto-search-results a.search-result-link{color:inherit;text-decoration:none}.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item,#quarto-search-results li.aa-Item[aria-selected=true] .search-item{background-color:#2780e3}.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item.search-result-more,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-result-section,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-result-text,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-result-title-container,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-result-text-container,#quarto-search-results li.aa-Item[aria-selected=true] .search-item.search-result-more,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-result-section,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-result-text,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-result-title-container,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-result-text-container{color:#fff;background-color:#2780e3}.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item mark.search-match,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-match.mark,#quarto-search-results li.aa-Item[aria-selected=true] .search-item mark.search-match,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-match.mark{color:#fff;background-color:#4b95e8}.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item,#quarto-search-results li.aa-Item[aria-selected=false] .search-item{background-color:#fff}.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item.search-result-more,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-result-section,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-result-text,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-result-title-container,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-result-text-container,#quarto-search-results li.aa-Item[aria-selected=false] .search-item.search-result-more,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-result-section,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-result-text,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-result-title-container,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-result-text-container{color:#373a3c}.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item mark.search-match,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-match.mark,#quarto-search-results li.aa-Item[aria-selected=false] .search-item mark.search-match,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-match.mark{color:inherit;background-color:#e5effc}.aa-DetachedOverlay .aa-Item .search-result-doc:not(.document-selectable) .search-result-title-container,#quarto-search-results .aa-Item .search-result-doc:not(.document-selectable) .search-result-title-container{background-color:#fff;color:#373a3c}.aa-DetachedOverlay .aa-Item .search-result-doc:not(.document-selectable) .search-result-text-container,#quarto-search-results .aa-Item .search-result-doc:not(.document-selectable) .search-result-text-container{padding-top:0px}.aa-DetachedOverlay li.aa-Item .search-result-doc.document-selectable .search-result-text-container,#quarto-search-results li.aa-Item .search-result-doc.document-selectable .search-result-text-container{margin-top:-4px}.aa-DetachedOverlay .aa-Item,#quarto-search-results .aa-Item{cursor:pointer}.aa-DetachedOverlay .aa-Item .search-item,#quarto-search-results .aa-Item .search-item{border-left:none;border-right:none;border-top:none;background-color:#fff;border-color:#ced4da;color:#373a3c}.aa-DetachedOverlay .aa-Item .search-item p,#quarto-search-results .aa-Item .search-item p{margin-top:0;margin-bottom:0}.aa-DetachedOverlay .aa-Item .search-item i.bi,#quarto-search-results .aa-Item .search-item i.bi{padding-left:8px;padding-right:8px;font-size:1.3em}.aa-DetachedOverlay .aa-Item .search-item .search-result-title,#quarto-search-results .aa-Item .search-item .search-result-title{margin-top:.3em;margin-bottom:.1rem}.aa-DetachedOverlay .aa-Item .search-result-title-container,#quarto-search-results .aa-Item .search-result-title-container{font-size:1em;display:flex;padding:6px 4px 6px 4px}.aa-DetachedOverlay .aa-Item .search-result-text-container,#quarto-search-results .aa-Item .search-result-text-container{padding-bottom:8px;padding-right:8px;margin-left:44px}.aa-DetachedOverlay .aa-Item .search-result-doc-section,.aa-DetachedOverlay .aa-Item .search-result-more,#quarto-search-results .aa-Item .search-result-doc-section,#quarto-search-results .aa-Item .search-result-more{padding-top:8px;padding-bottom:8px;padding-left:44px}.aa-DetachedOverlay .aa-Item .search-result-more,#quarto-search-results .aa-Item .search-result-more{font-size:.8em;font-weight:400}.aa-DetachedOverlay .aa-Item .search-result-doc,#quarto-search-results .aa-Item .search-result-doc{border-top:1px solid #ced4da}.aa-DetachedSearchButton{background:none;border:none}.aa-DetachedSearchButton .aa-DetachedSearchButtonPlaceholder{display:none}.navbar .aa-DetachedSearchButton .aa-DetachedSearchButtonIcon{color:#fdfeff}.sidebar-tools-collapse #quarto-search,.sidebar-tools-main #quarto-search{display:inline}.sidebar-tools-collapse #quarto-search .aa-Autocomplete,.sidebar-tools-main #quarto-search .aa-Autocomplete{display:inline}.sidebar-tools-collapse #quarto-search .aa-DetachedSearchButton,.sidebar-tools-main #quarto-search .aa-DetachedSearchButton{padding-left:4px;padding-right:4px}.sidebar-tools-collapse #quarto-search .aa-DetachedSearchButton .aa-DetachedSearchButtonIcon,.sidebar-tools-main #quarto-search .aa-DetachedSearchButton .aa-DetachedSearchButtonIcon{color:#545555}.sidebar-tools-collapse #quarto-search .aa-DetachedSearchButton .aa-DetachedSearchButtonIcon .aa-SubmitIcon,.sidebar-tools-main #quarto-search .aa-DetachedSearchButton .aa-DetachedSearchButtonIcon .aa-SubmitIcon{margin-top:-3px}.aa-DetachedContainer{background:rgba(255,255,255,.65);width:90%;bottom:0;box-shadow:rgba(206,212,218,.6) 0 0 0 1px;outline:currentColor none medium;display:flex;flex-direction:column;left:0;margin:0;overflow:hidden;padding:0;position:fixed;right:0;top:0;z-index:1101}.aa-DetachedContainer::after{height:32px}.aa-DetachedContainer .aa-SourceHeader{margin:var(--aa-spacing-half) 0 var(--aa-spacing-half) 2px}.aa-DetachedContainer .aa-Panel{background-color:#fff;border-radius:0;box-shadow:none;flex-grow:1;margin:0;padding:0;position:relative}.aa-DetachedContainer .aa-PanelLayout{bottom:0;box-shadow:none;left:0;margin:0;max-height:none;overflow-y:auto;position:absolute;right:0;top:0;width:100%}.aa-DetachedFormContainer{background-color:#fff;border-bottom:1px solid #ced4da;display:flex;flex-direction:row;justify-content:space-between;margin:0;padding:.5em}.aa-DetachedCancelButton{background:none;font-size:.8em;border:0;border-radius:3px;color:#373a3c;cursor:pointer;margin:0 0 0 .5em;padding:0 .5em}.aa-DetachedCancelButton:hover,.aa-DetachedCancelButton:focus{box-shadow:rgba(39,128,227,.6) 0 0 0 1px;outline:currentColor none medium}.aa-DetachedContainer--modal{border-radius:6px;bottom:inherit;height:auto;margin:0 auto;max-width:850px;position:absolute;top:100px}.aa-DetachedContainer--modal .aa-PanelLayout{max-height:var(--aa-detached-modal-max-height);padding-bottom:var(--aa-spacing-half);position:static}.aa-Detached{height:100vh;overflow:hidden}.aa-DetachedOverlay{background-color:rgba(55,58,60,.4);position:fixed;left:0;right:0;top:0;margin:0;padding:0;height:100vh;z-index:1100}.quarto-listing{padding-bottom:1em}.listing-pagination{padding-top:.5em}ul.pagination{float:right;padding-left:8px;padding-top:.5em}ul.pagination li{padding-right:.75em}ul.pagination li.disabled a,ul.pagination li.active a{color:#373a3c;text-decoration:none}ul.pagination li:last-of-type{padding-right:0}.listing-actions-group{display:flex}.quarto-listing-filter{margin-bottom:1em;width:200px;margin-left:auto}.quarto-listing-sort{margin-bottom:1em;margin-right:auto;width:auto}.quarto-listing-sort .input-group-text{font-size:.8em}.input-group-text{border-right:none}.quarto-listing-sort select.form-select{font-size:.8em}.listing-no-matching{text-align:center;padding-top:2em;padding-bottom:3em;font-size:1em}#quarto-margin-sidebar .quarto-listing-category{padding-top:0;font-size:1rem}#quarto-margin-sidebar .quarto-listing-category-title{cursor:pointer;font-weight:600;font-size:1rem}.quarto-listing-category .category{cursor:pointer}.quarto-listing-category .category.active{font-weight:600}.quarto-listing-category.category-cloud{display:flex;flex-wrap:wrap;align-items:baseline}.quarto-listing-category.category-cloud .category{padding-right:5px}.quarto-listing-category.category-cloud .category-cloud-1{font-size:.75em}.quarto-listing-category.category-cloud .category-cloud-2{font-size:.95em}.quarto-listing-category.category-cloud .category-cloud-3{font-size:1.15em}.quarto-listing-category.category-cloud .category-cloud-4{font-size:1.35em}.quarto-listing-category.category-cloud .category-cloud-5{font-size:1.55em}.quarto-listing-category.category-cloud .category-cloud-6{font-size:1.75em}.quarto-listing-category.category-cloud .category-cloud-7{font-size:1.95em}.quarto-listing-category.category-cloud .category-cloud-8{font-size:2.15em}.quarto-listing-category.category-cloud .category-cloud-9{font-size:2.35em}.quarto-listing-category.category-cloud .category-cloud-10{font-size:2.55em}.quarto-listing-cols-1{grid-template-columns:repeat(1, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-1{grid-template-columns:repeat(1, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-1{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-2{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-2{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-2{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-3{grid-template-columns:repeat(3, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-3{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-3{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-4{grid-template-columns:repeat(4, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-4{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-4{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-5{grid-template-columns:repeat(5, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-5{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-5{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-6{grid-template-columns:repeat(6, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-6{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-6{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-7{grid-template-columns:repeat(7, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-7{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-7{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-8{grid-template-columns:repeat(8, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-8{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-8{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-9{grid-template-columns:repeat(9, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-9{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-9{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-10{grid-template-columns:repeat(10, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-10{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-10{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-11{grid-template-columns:repeat(11, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-11{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-11{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-cols-12{grid-template-columns:repeat(12, minmax(0, 1fr));gap:1.5em}@media(max-width: 767.98px){.quarto-listing-cols-12{grid-template-columns:repeat(2, minmax(0, 1fr));gap:1.5em}}@media(max-width: 575.98px){.quarto-listing-cols-12{grid-template-columns:minmax(0, 1fr);gap:1.5em}}.quarto-listing-grid{gap:1.5em}.quarto-grid-item.borderless{border:none}.quarto-grid-item.borderless .listing-categories .listing-category:last-of-type,.quarto-grid-item.borderless .listing-categories .listing-category:first-of-type{padding-left:0}.quarto-grid-item.borderless .listing-categories .listing-category{border:0}.quarto-grid-link{text-decoration:none;color:inherit}.quarto-grid-link:hover{text-decoration:none;color:inherit}.quarto-grid-item h5.title,.quarto-grid-item .title.h5{margin-top:0;margin-bottom:0}.quarto-grid-item .card-footer{display:flex;justify-content:space-between;font-size:.8em}.quarto-grid-item .card-footer p{margin-bottom:0}.quarto-grid-item p.card-img-top{margin-bottom:0}.quarto-grid-item img.thumbnail-image{object-fit:cover}.quarto-grid-item .card-other-values{margin-top:.5em;font-size:.8em}.quarto-grid-item .card-other-values tr{margin-bottom:.5em}.quarto-grid-item .card-other-values tr>td:first-of-type{font-weight:600;padding-right:1em;padding-left:1em;vertical-align:top}.quarto-grid-item div.post-contents{display:flex;flex-direction:column;text-decoration:none;height:100%}.quarto-grid-item div.card-img-bg{background-color:#adb5bd;flex-shrink:0}.quarto-grid-item .card-attribution{padding-top:1em;display:flex;gap:1em;text-transform:uppercase;color:#6c757d;font-weight:500;flex-grow:10;align-items:flex-end}.quarto-grid-item .description{padding-bottom:1em}.quarto-grid-item .card-attribution .date{align-self:flex-end}.quarto-grid-item .card-attribution.justify{justify-content:space-between}.quarto-grid-item .card-attribution.start{justify-content:flex-start}.quarto-grid-item .card-attribution.end{justify-content:flex-end}.quarto-grid-item .card-title{margin-bottom:.1em}.quarto-grid-item .card-subtitle{padding-top:.25em}.quarto-grid-item .card-text{font-size:.9em}.quarto-grid-item .listing-reading-time{padding-bottom:.25em}.quarto-grid-item .card-text-small{font-size:.8em}.quarto-grid-item .card-subtitle.subtitle{font-size:.9em;font-weight:600;padding-bottom:.5em}.quarto-grid-item .listing-categories{display:flex;flex-wrap:wrap;padding-bottom:5px}.quarto-grid-item .listing-categories .listing-category{color:#6c757d;border:solid 1px #dee2e6;border-radius:.25rem;text-transform:uppercase;font-size:.65em;padding-left:.5em;padding-right:.5em;padding-top:.15em;padding-bottom:.15em;cursor:pointer;margin-right:4px;margin-bottom:4px}.quarto-grid-item.card-right{text-align:right}.quarto-grid-item.card-right .listing-categories{justify-content:flex-end}.quarto-grid-item.card-left{text-align:left}.quarto-grid-item.card-center{text-align:center}.quarto-grid-item.card-center .listing-description{text-align:justify}.quarto-grid-item.card-center .listing-categories{justify-content:center}table.quarto-listing-table td.image{padding:0px}table.quarto-listing-table td.image img{width:100%;max-width:50px;object-fit:contain}table.quarto-listing-table a{text-decoration:none}table.quarto-listing-table th a{color:inherit}table.quarto-listing-table th a.asc:after{margin-bottom:-2px;margin-left:5px;display:inline-block;height:1rem;width:1rem;background-repeat:no-repeat;background-size:1rem 1rem;background-image:url('data:image/svg+xml,');content:""}table.quarto-listing-table th a.desc:after{margin-bottom:-2px;margin-left:5px;display:inline-block;height:1rem;width:1rem;background-repeat:no-repeat;background-size:1rem 1rem;background-image:url('data:image/svg+xml,');content:""}table.quarto-listing-table.table-hover td{cursor:pointer}.quarto-post.image-left{flex-direction:row}.quarto-post.image-right{flex-direction:row-reverse}@media(max-width: 767.98px){.quarto-post.image-right,.quarto-post.image-left{gap:0em;flex-direction:column}.quarto-post .metadata{padding-bottom:1em;order:2}.quarto-post .body{order:1}.quarto-post .thumbnail{order:3}}.list.quarto-listing-default div:last-of-type{border-bottom:none}@media(min-width: 992px){.quarto-listing-container-default{margin-right:2em}}div.quarto-post{display:flex;gap:2em;margin-bottom:1.5em;border-bottom:1px solid #dee2e6}@media(max-width: 767.98px){div.quarto-post{padding-bottom:1em}}div.quarto-post .metadata{flex-basis:20%;flex-grow:0;margin-top:.2em;flex-shrink:10}div.quarto-post .thumbnail{flex-basis:30%;flex-grow:0;flex-shrink:0}div.quarto-post .thumbnail img{margin-top:.4em;width:100%;object-fit:cover}div.quarto-post .body{flex-basis:45%;flex-grow:1;flex-shrink:0}div.quarto-post .body h3.listing-title,div.quarto-post .body .listing-title.h3{margin-top:0px;margin-bottom:0px;border-bottom:none}div.quarto-post .body .listing-subtitle{font-size:.875em;margin-bottom:.5em;margin-top:.2em}div.quarto-post .body .description{font-size:.9em}div.quarto-post a{color:#373a3c;display:flex;flex-direction:column;text-decoration:none}div.quarto-post a div.description{flex-shrink:0}div.quarto-post .metadata{display:flex;flex-direction:column;font-size:.8em;font-family:var(--bs-font-sans-serif);flex-basis:33%}div.quarto-post .listing-categories{display:flex;flex-wrap:wrap;padding-bottom:5px}div.quarto-post .listing-categories .listing-category{color:#6c757d;border:solid 1px #dee2e6;border-radius:.25rem;text-transform:uppercase;font-size:.65em;padding-left:.5em;padding-right:.5em;padding-top:.15em;padding-bottom:.15em;cursor:pointer;margin-right:4px;margin-bottom:4px}div.quarto-post .listing-description{margin-bottom:.5em}div.quarto-about-jolla{display:flex !important;flex-direction:column;align-items:center;margin-top:10%;padding-bottom:1em}div.quarto-about-jolla .about-image{object-fit:cover;margin-left:auto;margin-right:auto;margin-bottom:1.5em}div.quarto-about-jolla img.round{border-radius:50%}div.quarto-about-jolla img.rounded{border-radius:10px}div.quarto-about-jolla .quarto-title h1.title,div.quarto-about-jolla .quarto-title .title.h1{text-align:center}div.quarto-about-jolla .quarto-title .description{text-align:center}div.quarto-about-jolla h2,div.quarto-about-jolla .h2{border-bottom:none}div.quarto-about-jolla .about-sep{width:60%}div.quarto-about-jolla main{text-align:center}div.quarto-about-jolla .about-links{display:flex}@media(min-width: 992px){div.quarto-about-jolla .about-links{flex-direction:row;column-gap:.8em;row-gap:15px;flex-wrap:wrap}}@media(max-width: 991.98px){div.quarto-about-jolla .about-links{flex-direction:column;row-gap:1em;width:100%;padding-bottom:1.5em}}div.quarto-about-jolla .about-link{color:#686d71;text-decoration:none;border:solid 1px}@media(min-width: 992px){div.quarto-about-jolla .about-link{font-size:.8em;padding:.25em .5em;border-radius:4px}}@media(max-width: 991.98px){div.quarto-about-jolla .about-link{font-size:1.1em;padding:.5em .5em;text-align:center;border-radius:6px}}div.quarto-about-jolla .about-link:hover{color:#2780e3}div.quarto-about-jolla .about-link i.bi{margin-right:.15em}div.quarto-about-solana{display:flex !important;flex-direction:column;padding-top:3em !important;padding-bottom:1em}div.quarto-about-solana .about-entity{display:flex !important;align-items:start;justify-content:space-between}@media(min-width: 992px){div.quarto-about-solana .about-entity{flex-direction:row}}@media(max-width: 991.98px){div.quarto-about-solana .about-entity{flex-direction:column-reverse;align-items:center;text-align:center}}div.quarto-about-solana .about-entity .entity-contents{display:flex;flex-direction:column}@media(max-width: 767.98px){div.quarto-about-solana .about-entity .entity-contents{width:100%}}div.quarto-about-solana .about-entity .about-image{object-fit:cover}@media(max-width: 991.98px){div.quarto-about-solana .about-entity .about-image{margin-bottom:1.5em}}div.quarto-about-solana .about-entity img.round{border-radius:50%}div.quarto-about-solana .about-entity img.rounded{border-radius:10px}div.quarto-about-solana .about-entity .about-links{display:flex;justify-content:left;padding-bottom:1.2em}@media(min-width: 992px){div.quarto-about-solana .about-entity .about-links{flex-direction:row;column-gap:.8em;row-gap:15px;flex-wrap:wrap}}@media(max-width: 991.98px){div.quarto-about-solana .about-entity .about-links{flex-direction:column;row-gap:1em;width:100%;padding-bottom:1.5em}}div.quarto-about-solana .about-entity .about-link{color:#686d71;text-decoration:none;border:solid 1px}@media(min-width: 992px){div.quarto-about-solana .about-entity .about-link{font-size:.8em;padding:.25em .5em;border-radius:4px}}@media(max-width: 991.98px){div.quarto-about-solana .about-entity .about-link{font-size:1.1em;padding:.5em .5em;text-align:center;border-radius:6px}}div.quarto-about-solana .about-entity .about-link:hover{color:#2780e3}div.quarto-about-solana .about-entity .about-link i.bi{margin-right:.15em}div.quarto-about-solana .about-contents{padding-right:1.5em;flex-basis:0;flex-grow:1}div.quarto-about-solana .about-contents main.content{margin-top:0}div.quarto-about-solana .about-contents h2,div.quarto-about-solana .about-contents .h2{border-bottom:none}div.quarto-about-trestles{display:flex !important;flex-direction:row;padding-top:3em !important;padding-bottom:1em}@media(max-width: 991.98px){div.quarto-about-trestles{flex-direction:column;padding-top:0em !important}}div.quarto-about-trestles .about-entity{display:flex !important;flex-direction:column;align-items:center;text-align:center;padding-right:1em}@media(min-width: 992px){div.quarto-about-trestles .about-entity{flex:0 0 42%}}div.quarto-about-trestles .about-entity .about-image{object-fit:cover;margin-bottom:1.5em}div.quarto-about-trestles .about-entity img.round{border-radius:50%}div.quarto-about-trestles .about-entity img.rounded{border-radius:10px}div.quarto-about-trestles .about-entity .about-links{display:flex;justify-content:center}@media(min-width: 992px){div.quarto-about-trestles .about-entity .about-links{flex-direction:row;column-gap:.8em;row-gap:15px;flex-wrap:wrap}}@media(max-width: 991.98px){div.quarto-about-trestles .about-entity .about-links{flex-direction:column;row-gap:1em;width:100%;padding-bottom:1.5em}}div.quarto-about-trestles .about-entity .about-link{color:#686d71;text-decoration:none;border:solid 1px}@media(min-width: 992px){div.quarto-about-trestles .about-entity .about-link{font-size:.8em;padding:.25em .5em;border-radius:4px}}@media(max-width: 991.98px){div.quarto-about-trestles .about-entity .about-link{font-size:1.1em;padding:.5em .5em;text-align:center;border-radius:6px}}div.quarto-about-trestles .about-entity .about-link:hover{color:#2780e3}div.quarto-about-trestles .about-entity .about-link i.bi{margin-right:.15em}div.quarto-about-trestles .about-contents{flex-basis:0;flex-grow:1}div.quarto-about-trestles .about-contents h2,div.quarto-about-trestles .about-contents .h2{border-bottom:none}@media(min-width: 992px){div.quarto-about-trestles .about-contents{border-left:solid 1px #dee2e6;padding-left:1.5em}}div.quarto-about-trestles .about-contents main.content{margin-top:0}div.quarto-about-marquee{padding-bottom:1em}div.quarto-about-marquee .about-contents{display:flex;flex-direction:column}div.quarto-about-marquee .about-image{max-height:550px;margin-bottom:1.5em;object-fit:cover}div.quarto-about-marquee img.round{border-radius:50%}div.quarto-about-marquee img.rounded{border-radius:10px}div.quarto-about-marquee h2,div.quarto-about-marquee .h2{border-bottom:none}div.quarto-about-marquee .about-links{display:flex;justify-content:center;padding-top:1.5em}@media(min-width: 992px){div.quarto-about-marquee .about-links{flex-direction:row;column-gap:.8em;row-gap:15px;flex-wrap:wrap}}@media(max-width: 991.98px){div.quarto-about-marquee .about-links{flex-direction:column;row-gap:1em;width:100%;padding-bottom:1.5em}}div.quarto-about-marquee .about-link{color:#686d71;text-decoration:none;border:solid 1px}@media(min-width: 992px){div.quarto-about-marquee .about-link{font-size:.8em;padding:.25em .5em;border-radius:4px}}@media(max-width: 991.98px){div.quarto-about-marquee .about-link{font-size:1.1em;padding:.5em .5em;text-align:center;border-radius:6px}}div.quarto-about-marquee .about-link:hover{color:#2780e3}div.quarto-about-marquee .about-link i.bi{margin-right:.15em}@media(min-width: 992px){div.quarto-about-marquee .about-link{border:none}}div.quarto-about-broadside{display:flex;flex-direction:column;padding-bottom:1em}div.quarto-about-broadside .about-main{display:flex !important;padding-top:0 !important}@media(min-width: 992px){div.quarto-about-broadside .about-main{flex-direction:row;align-items:flex-start}}@media(max-width: 991.98px){div.quarto-about-broadside .about-main{flex-direction:column}}@media(max-width: 991.98px){div.quarto-about-broadside .about-main .about-entity{flex-shrink:0;width:100%;height:450px;margin-bottom:1.5em;background-size:cover;background-repeat:no-repeat}}@media(min-width: 992px){div.quarto-about-broadside .about-main .about-entity{flex:0 10 50%;margin-right:1.5em;width:100%;height:100%;background-size:100%;background-repeat:no-repeat}}div.quarto-about-broadside .about-main .about-contents{padding-top:14px;flex:0 0 50%}div.quarto-about-broadside h2,div.quarto-about-broadside .h2{border-bottom:none}div.quarto-about-broadside .about-sep{margin-top:1.5em;width:60%;align-self:center}div.quarto-about-broadside .about-links{display:flex;justify-content:center;column-gap:20px;padding-top:1.5em}@media(min-width: 992px){div.quarto-about-broadside .about-links{flex-direction:row;column-gap:.8em;row-gap:15px;flex-wrap:wrap}}@media(max-width: 991.98px){div.quarto-about-broadside .about-links{flex-direction:column;row-gap:1em;width:100%;padding-bottom:1.5em}}div.quarto-about-broadside .about-link{color:#686d71;text-decoration:none;border:solid 1px}@media(min-width: 992px){div.quarto-about-broadside .about-link{font-size:.8em;padding:.25em .5em;border-radius:4px}}@media(max-width: 991.98px){div.quarto-about-broadside .about-link{font-size:1.1em;padding:.5em .5em;text-align:center;border-radius:6px}}div.quarto-about-broadside .about-link:hover{color:#2780e3}div.quarto-about-broadside .about-link i.bi{margin-right:.15em}@media(min-width: 992px){div.quarto-about-broadside .about-link{border:none}}.tippy-box[data-theme~=quarto]{background-color:#fff;color:#373a3c;border-radius:.25rem;border:solid 1px #dee2e6;font-size:.875rem}.tippy-box[data-theme~=quarto] .tippy-arrow{color:#dee2e6}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:-1px}.tippy-box[data-placement^=bottom]>.tippy-content{padding:.75em 1em;z-index:1}.top-right{position:absolute;top:1em;right:1em}.hidden{display:none !important}.quarto-layout-panel{margin-bottom:1em}.quarto-layout-panel>figure{width:100%}.quarto-layout-panel>figure>figcaption,.quarto-layout-panel>.panel-caption{margin-top:10pt}.quarto-layout-panel>.table-caption{margin-top:0px}.table-caption p{margin-bottom:.5em}.quarto-layout-row{display:flex;flex-direction:row;align-items:flex-start}.quarto-layout-valign-top{align-items:flex-start}.quarto-layout-valign-bottom{align-items:flex-end}.quarto-layout-valign-center{align-items:center}.quarto-layout-cell{position:relative;margin-right:20px}.quarto-layout-cell:last-child{margin-right:0}.quarto-layout-cell figure,.quarto-layout-cell>p{margin:.2em}.quarto-layout-cell img{max-width:100%}.quarto-layout-cell .html-widget{width:100% !important}.quarto-layout-cell div figure p{margin:0}.quarto-layout-cell figure{display:inline-block;margin-inline-start:0;margin-inline-end:0}.quarto-layout-cell table{display:inline-table}.quarto-layout-cell-subref figcaption,figure .quarto-layout-row figure figcaption{text-align:center;font-style:italic}.quarto-figure{position:relative;margin-bottom:1em}.quarto-figure>figure{width:100%;margin-bottom:0}.quarto-figure-left>figure>p{text-align:left}.quarto-figure-center>figure>p{text-align:center}.quarto-figure-right>figure>p{text-align:right}figure>p:empty{display:none}figure>p:first-child{margin-top:0;margin-bottom:0}figure>figcaption{margin-top:.5em}div[id^=tbl-]{position:relative}.quarto-figure>.anchorjs-link,div[id^=tbl-]>.anchorjs-link{position:absolute;top:0;right:0}.quarto-figure:hover>.anchorjs-link,div[id^=tbl-]:hover>.anchorjs-link,h2:hover>.anchorjs-link,.h2:hover>.anchorjs-link,h3:hover>.anchorjs-link,.h3:hover>.anchorjs-link,h4:hover>.anchorjs-link,.h4:hover>.anchorjs-link,h5:hover>.anchorjs-link,.h5:hover>.anchorjs-link,h6:hover>.anchorjs-link,.h6:hover>.anchorjs-link,.reveal-anchorjs-link>.anchorjs-link{opacity:1}#title-block-header{margin-block-end:1rem;position:relative;margin-top:-1px}#title-block-header .abstract{margin-block-start:1rem}#title-block-header .abstract .abstract-title{font-weight:600}#title-block-header a{text-decoration:none}#title-block-header .author,#title-block-header .date,#title-block-header .doi{margin-block-end:.2rem}#title-block-header .quarto-title-block>div{display:flex}#title-block-header .quarto-title-block>div>h1,#title-block-header .quarto-title-block>div>.h1{flex-grow:1}#title-block-header .quarto-title-block>div>button{flex-shrink:0;height:2.25rem;margin-top:0}@media(min-width: 992px){#title-block-header .quarto-title-block>div>button{margin-top:5px}}tr.header>th>p:last-of-type{margin-bottom:0px}table,.table{caption-side:top;margin-bottom:1.5rem}caption,.table-caption{text-align:center}.utterances{max-width:none;margin-left:-8px}iframe{margin-bottom:1em}details{margin-bottom:1em}details[show]{margin-bottom:0}details>summary{color:#6c757d}details>summary>p:only-child{display:inline}pre.sourceCode,code.sourceCode{position:relative}code{white-space:pre}@media print{code{white-space:pre-wrap}}pre>code{display:block}pre>code.sourceCode{white-space:pre}pre>code.sourceCode>span>a:first-child::before{text-decoration:none}pre.code-overflow-wrap>code.sourceCode{white-space:pre-wrap}pre.code-overflow-scroll>code.sourceCode{white-space:pre}code a:any-link{color:inherit;text-decoration:none}code a:hover{color:inherit;text-decoration:underline}ul.task-list{padding-left:1em}[data-tippy-root]{display:inline-block}.tippy-content .footnote-back{display:none}.quarto-embedded-source-code{display:none}.quarto-unresolved-ref{font-weight:600}.quarto-cover-image{max-width:35%;float:right;margin-left:30px}.cell-output-display .widget-subarea{margin-bottom:1em}.cell-output-display:not(.no-overflow-x){overflow-x:auto}.panel-input{margin-bottom:1em}.panel-input>div,.panel-input>div>div{display:inline-block;vertical-align:top;padding-right:12px}.panel-input>p:last-child{margin-bottom:0}.layout-sidebar{margin-bottom:1em}.layout-sidebar .tab-content{border:none}.tab-content>.page-columns.active{display:grid}div.sourceCode>iframe{width:100%;height:300px;margin-bottom:-0.5em}div.ansi-escaped-output{font-family:monospace;display:block}/*! -* -* ansi colors from IPython notebook's -* -*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-fg{color:#282c36}.ansi-black-intense-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-fg{color:#b22b31}.ansi-red-intense-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-fg{color:#007427}.ansi-green-intense-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-fg{color:#b27d12}.ansi-yellow-intense-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-fg{color:#0065ca}.ansi-blue-intense-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-fg{color:#a03196}.ansi-magenta-intense-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-fg{color:#258f8f}.ansi-cyan-intense-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-fg{color:#a1a6b2}.ansi-white-intense-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #fff;--quarto-body-color: #373a3c;--quarto-text-muted: #6c757d;--quarto-border-color: #dee2e6;--quarto-border-width: 1px;--quarto-border-radius: 0.25rem}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:transparent}.code-copy-button:focus{outline:none}pre.sourceCode:hover>.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}pre.sourceCode:hover>.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}pre.sourceCode:hover>.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}pre.sourceCode:hover>.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}main ol ol,main ul ul,main ol ul,main ul ol{margin-bottom:1em}body{margin:0}main.page-columns>header>h1.title,main.page-columns>header>.title.h1{margin-bottom:0}@media(min-width: 992px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 35px [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(500px, calc(850px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 35px [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(500px, calc(850px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] 35px [page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 35px [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc(1200px - 3em)) [body-content-end] 3em [body-end] 50px [body-end-outset] minmax(0px, 250px) [page-end-inset] 50px [page-end] 1fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 35px [page-start-inset] minmax(0px, 175px) [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(450px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 35px [page-start-inset] minmax(0px, 175px) [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(450px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(25px, 50px) [page-start-inset] minmax(50px, 150px) [body-start-outset] minmax(25px, 50px) [body-start] 1.5em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 100px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 50px [page-start-inset] minmax(50px, 150px) [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(450px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 100px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 50px [page-start-inset] minmax(50px, 150px) [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(450px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 150px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(25px, 50px) [page-start-inset] minmax(50px, 150px) [body-start-outset] minmax(25px, 50px) [body-start] 1.5em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 991.98px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc(1200px - 3em)) [body-content-end body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 35px [page-start-inset] minmax(0px, 145px) [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(450px, calc(750px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 35px [page-start-inset] minmax(0px, 145px) [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(450px, calc(750px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 767.98px){body .page-columns,body.fullcontent:not(.floating):not(.docked) .page-columns,body.slimcontent:not(.floating):not(.docked) .page-columns,body.docked .page-columns,body.docked.slimcontent .page-columns,body.docked.fullcontent .page-columns,body.floating .page-columns,body.floating.slimcontent .page-columns,body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}nav[role=doc-toc]{display:none}}body,.page-row-navigation{grid-template-rows:[page-top] max-content [contents-top] max-content [contents-bottom] max-content [page-bottom]}.page-rows-contents{grid-template-rows:[content-top] minmax(max-content, 1fr) [content-bottom] minmax(60px, max-content) [page-bottom]}.page-full{grid-column:screen-start/screen-end !important}.page-columns>*{grid-column:body-content-start/body-content-end}.page-columns.column-page>*{grid-column:page-start/page-end}.page-columns.column-page-left>*{grid-column:page-start/body-content-end}.page-columns.column-page-right>*{grid-column:body-content-start/page-end}.page-rows{grid-auto-rows:auto}.header{grid-column:screen-start/screen-end;grid-row:page-top/contents-top}#quarto-content{padding:0;grid-column:screen-start/screen-end;grid-row:contents-top/contents-bottom}body.floating .sidebar.sidebar-navigation{grid-column:page-start/body-start;grid-row:content-top/page-bottom}body.docked .sidebar.sidebar-navigation{grid-column:screen-start/body-start;grid-row:content-top/page-bottom}.sidebar.toc-left{grid-column:page-start/body-start;grid-row:content-top/page-bottom}.sidebar.margin-sidebar{grid-column:body-end/page-end;grid-row:content-top/page-bottom}.page-columns .content{grid-column:body-content-start/body-content-end;grid-row:content-top/content-bottom;align-content:flex-start}.page-columns .page-navigation{grid-column:body-content-start/body-content-end;grid-row:content-bottom/page-bottom}.page-columns .footer{grid-column:screen-start/screen-end;grid-row:contents-bottom/page-bottom}.page-columns .column-body{grid-column:body-content-start/body-content-end}.page-columns .column-body-fullbleed{grid-column:body-start/body-end}.page-columns .column-body-outset{grid-column:body-start-outset/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset table{background:#fff}.page-columns .column-body-outset-left{grid-column:body-start-outset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-left table{background:#fff}.page-columns .column-body-outset-right{grid-column:body-content-start/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-right table{background:#fff}.page-columns .column-page{grid-column:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page table{background:#fff}.page-columns .column-page-inset{grid-column:page-start-inset/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset table{background:#fff}.page-columns .column-page-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-left table{background:#fff}.page-columns .column-page-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-right figcaption table{background:#fff}.page-columns .column-page-left{grid-column:page-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-left table{background:#fff}.page-columns .column-page-right{grid-column:body-content-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-right figcaption table{background:#fff}#quarto-content.page-columns #quarto-margin-sidebar,#quarto-content.page-columns #quarto-sidebar{z-index:1}@media(max-width: 991.98px){#quarto-content.page-columns #quarto-margin-sidebar.collapse,#quarto-content.page-columns #quarto-sidebar.collapse{z-index:1055}}#quarto-content.page-columns main.column-page,#quarto-content.page-columns main.column-page-right,#quarto-content.page-columns main.column-page-left{z-index:0}.page-columns .column-screen-inset{grid-column:screen-start-inset/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset table{background:#fff}.page-columns .column-screen-inset-left{grid-column:screen-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-left table{background:#fff}.page-columns .column-screen-inset-right{grid-column:body-content-start/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:screen-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:screen-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-right table{background:#fff}.page-columns .column-screen-inset-shaded{grid-column:screen-start/screen-end;padding:1em;background:#f8f9fa;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}.zindex-content{z-index:998;transform:translate3d(0, 0, 0)}.zindex-modal{z-index:1055;transform:translate3d(0, 0, 0)}.zindex-over-content{z-index:999;transform:translate3d(0, 0, 0)}img.img-fluid.column-screen,img.img-fluid.column-screen-inset-shaded,img.img-fluid.column-screen-inset,img.img-fluid.column-screen-inset-left,img.img-fluid.column-screen-inset-right,img.img-fluid.column-screen-left,img.img-fluid.column-screen-right{width:100%}@media(min-width: 992px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.column-sidebar{grid-column:page-start/body-start !important;z-index:998}.column-leftmargin{grid-column:screen-start-inset/body-start !important;z-index:998}.no-row-height{height:1em;overflow:visible}}@media(max-width: 991.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.no-row-height{height:1em;overflow:visible}.page-columns.page-full{overflow:visible}.page-columns.toc-left .margin-caption,.page-columns.toc-left div.aside,.page-columns.toc-left aside,.page-columns.toc-left .column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.page-columns.toc-left .no-row-height{height:initial;overflow:initial}}@media(max-width: 767.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.no-row-height{height:initial;overflow:initial}#quarto-margin-sidebar{display:none}.hidden-sm{display:none}}.panel-grid{display:grid;grid-template-rows:repeat(1, 1fr);grid-template-columns:repeat(24, 1fr);gap:1em}.panel-grid .g-col-1{grid-column:auto/span 1}.panel-grid .g-col-2{grid-column:auto/span 2}.panel-grid .g-col-3{grid-column:auto/span 3}.panel-grid .g-col-4{grid-column:auto/span 4}.panel-grid .g-col-5{grid-column:auto/span 5}.panel-grid .g-col-6{grid-column:auto/span 6}.panel-grid .g-col-7{grid-column:auto/span 7}.panel-grid .g-col-8{grid-column:auto/span 8}.panel-grid .g-col-9{grid-column:auto/span 9}.panel-grid .g-col-10{grid-column:auto/span 10}.panel-grid .g-col-11{grid-column:auto/span 11}.panel-grid .g-col-12{grid-column:auto/span 12}.panel-grid .g-col-13{grid-column:auto/span 13}.panel-grid .g-col-14{grid-column:auto/span 14}.panel-grid .g-col-15{grid-column:auto/span 15}.panel-grid .g-col-16{grid-column:auto/span 16}.panel-grid .g-col-17{grid-column:auto/span 17}.panel-grid .g-col-18{grid-column:auto/span 18}.panel-grid .g-col-19{grid-column:auto/span 19}.panel-grid .g-col-20{grid-column:auto/span 20}.panel-grid .g-col-21{grid-column:auto/span 21}.panel-grid .g-col-22{grid-column:auto/span 22}.panel-grid .g-col-23{grid-column:auto/span 23}.panel-grid .g-col-24{grid-column:auto/span 24}.panel-grid .g-start-1{grid-column-start:1}.panel-grid .g-start-2{grid-column-start:2}.panel-grid .g-start-3{grid-column-start:3}.panel-grid .g-start-4{grid-column-start:4}.panel-grid .g-start-5{grid-column-start:5}.panel-grid .g-start-6{grid-column-start:6}.panel-grid .g-start-7{grid-column-start:7}.panel-grid .g-start-8{grid-column-start:8}.panel-grid .g-start-9{grid-column-start:9}.panel-grid .g-start-10{grid-column-start:10}.panel-grid .g-start-11{grid-column-start:11}.panel-grid .g-start-12{grid-column-start:12}.panel-grid .g-start-13{grid-column-start:13}.panel-grid .g-start-14{grid-column-start:14}.panel-grid .g-start-15{grid-column-start:15}.panel-grid .g-start-16{grid-column-start:16}.panel-grid .g-start-17{grid-column-start:17}.panel-grid .g-start-18{grid-column-start:18}.panel-grid .g-start-19{grid-column-start:19}.panel-grid .g-start-20{grid-column-start:20}.panel-grid .g-start-21{grid-column-start:21}.panel-grid .g-start-22{grid-column-start:22}.panel-grid .g-start-23{grid-column-start:23}@media(min-width: 576px){.panel-grid .g-col-sm-1{grid-column:auto/span 1}.panel-grid .g-col-sm-2{grid-column:auto/span 2}.panel-grid .g-col-sm-3{grid-column:auto/span 3}.panel-grid .g-col-sm-4{grid-column:auto/span 4}.panel-grid .g-col-sm-5{grid-column:auto/span 5}.panel-grid .g-col-sm-6{grid-column:auto/span 6}.panel-grid .g-col-sm-7{grid-column:auto/span 7}.panel-grid .g-col-sm-8{grid-column:auto/span 8}.panel-grid .g-col-sm-9{grid-column:auto/span 9}.panel-grid .g-col-sm-10{grid-column:auto/span 10}.panel-grid .g-col-sm-11{grid-column:auto/span 11}.panel-grid .g-col-sm-12{grid-column:auto/span 12}.panel-grid .g-col-sm-13{grid-column:auto/span 13}.panel-grid .g-col-sm-14{grid-column:auto/span 14}.panel-grid .g-col-sm-15{grid-column:auto/span 15}.panel-grid .g-col-sm-16{grid-column:auto/span 16}.panel-grid .g-col-sm-17{grid-column:auto/span 17}.panel-grid .g-col-sm-18{grid-column:auto/span 18}.panel-grid .g-col-sm-19{grid-column:auto/span 19}.panel-grid .g-col-sm-20{grid-column:auto/span 20}.panel-grid .g-col-sm-21{grid-column:auto/span 21}.panel-grid .g-col-sm-22{grid-column:auto/span 22}.panel-grid .g-col-sm-23{grid-column:auto/span 23}.panel-grid .g-col-sm-24{grid-column:auto/span 24}.panel-grid .g-start-sm-1{grid-column-start:1}.panel-grid .g-start-sm-2{grid-column-start:2}.panel-grid .g-start-sm-3{grid-column-start:3}.panel-grid .g-start-sm-4{grid-column-start:4}.panel-grid .g-start-sm-5{grid-column-start:5}.panel-grid .g-start-sm-6{grid-column-start:6}.panel-grid .g-start-sm-7{grid-column-start:7}.panel-grid .g-start-sm-8{grid-column-start:8}.panel-grid .g-start-sm-9{grid-column-start:9}.panel-grid .g-start-sm-10{grid-column-start:10}.panel-grid .g-start-sm-11{grid-column-start:11}.panel-grid .g-start-sm-12{grid-column-start:12}.panel-grid .g-start-sm-13{grid-column-start:13}.panel-grid .g-start-sm-14{grid-column-start:14}.panel-grid .g-start-sm-15{grid-column-start:15}.panel-grid .g-start-sm-16{grid-column-start:16}.panel-grid .g-start-sm-17{grid-column-start:17}.panel-grid .g-start-sm-18{grid-column-start:18}.panel-grid .g-start-sm-19{grid-column-start:19}.panel-grid .g-start-sm-20{grid-column-start:20}.panel-grid .g-start-sm-21{grid-column-start:21}.panel-grid .g-start-sm-22{grid-column-start:22}.panel-grid .g-start-sm-23{grid-column-start:23}}@media(min-width: 768px){.panel-grid .g-col-md-1{grid-column:auto/span 1}.panel-grid .g-col-md-2{grid-column:auto/span 2}.panel-grid .g-col-md-3{grid-column:auto/span 3}.panel-grid .g-col-md-4{grid-column:auto/span 4}.panel-grid .g-col-md-5{grid-column:auto/span 5}.panel-grid .g-col-md-6{grid-column:auto/span 6}.panel-grid .g-col-md-7{grid-column:auto/span 7}.panel-grid .g-col-md-8{grid-column:auto/span 8}.panel-grid .g-col-md-9{grid-column:auto/span 9}.panel-grid .g-col-md-10{grid-column:auto/span 10}.panel-grid .g-col-md-11{grid-column:auto/span 11}.panel-grid .g-col-md-12{grid-column:auto/span 12}.panel-grid .g-col-md-13{grid-column:auto/span 13}.panel-grid .g-col-md-14{grid-column:auto/span 14}.panel-grid .g-col-md-15{grid-column:auto/span 15}.panel-grid .g-col-md-16{grid-column:auto/span 16}.panel-grid .g-col-md-17{grid-column:auto/span 17}.panel-grid .g-col-md-18{grid-column:auto/span 18}.panel-grid .g-col-md-19{grid-column:auto/span 19}.panel-grid .g-col-md-20{grid-column:auto/span 20}.panel-grid .g-col-md-21{grid-column:auto/span 21}.panel-grid .g-col-md-22{grid-column:auto/span 22}.panel-grid .g-col-md-23{grid-column:auto/span 23}.panel-grid .g-col-md-24{grid-column:auto/span 24}.panel-grid .g-start-md-1{grid-column-start:1}.panel-grid .g-start-md-2{grid-column-start:2}.panel-grid .g-start-md-3{grid-column-start:3}.panel-grid .g-start-md-4{grid-column-start:4}.panel-grid .g-start-md-5{grid-column-start:5}.panel-grid .g-start-md-6{grid-column-start:6}.panel-grid .g-start-md-7{grid-column-start:7}.panel-grid .g-start-md-8{grid-column-start:8}.panel-grid .g-start-md-9{grid-column-start:9}.panel-grid .g-start-md-10{grid-column-start:10}.panel-grid .g-start-md-11{grid-column-start:11}.panel-grid .g-start-md-12{grid-column-start:12}.panel-grid .g-start-md-13{grid-column-start:13}.panel-grid .g-start-md-14{grid-column-start:14}.panel-grid .g-start-md-15{grid-column-start:15}.panel-grid .g-start-md-16{grid-column-start:16}.panel-grid .g-start-md-17{grid-column-start:17}.panel-grid .g-start-md-18{grid-column-start:18}.panel-grid .g-start-md-19{grid-column-start:19}.panel-grid .g-start-md-20{grid-column-start:20}.panel-grid .g-start-md-21{grid-column-start:21}.panel-grid .g-start-md-22{grid-column-start:22}.panel-grid .g-start-md-23{grid-column-start:23}}@media(min-width: 992px){.panel-grid .g-col-lg-1{grid-column:auto/span 1}.panel-grid .g-col-lg-2{grid-column:auto/span 2}.panel-grid .g-col-lg-3{grid-column:auto/span 3}.panel-grid .g-col-lg-4{grid-column:auto/span 4}.panel-grid .g-col-lg-5{grid-column:auto/span 5}.panel-grid .g-col-lg-6{grid-column:auto/span 6}.panel-grid .g-col-lg-7{grid-column:auto/span 7}.panel-grid .g-col-lg-8{grid-column:auto/span 8}.panel-grid .g-col-lg-9{grid-column:auto/span 9}.panel-grid .g-col-lg-10{grid-column:auto/span 10}.panel-grid .g-col-lg-11{grid-column:auto/span 11}.panel-grid .g-col-lg-12{grid-column:auto/span 12}.panel-grid .g-col-lg-13{grid-column:auto/span 13}.panel-grid .g-col-lg-14{grid-column:auto/span 14}.panel-grid .g-col-lg-15{grid-column:auto/span 15}.panel-grid .g-col-lg-16{grid-column:auto/span 16}.panel-grid .g-col-lg-17{grid-column:auto/span 17}.panel-grid .g-col-lg-18{grid-column:auto/span 18}.panel-grid .g-col-lg-19{grid-column:auto/span 19}.panel-grid .g-col-lg-20{grid-column:auto/span 20}.panel-grid .g-col-lg-21{grid-column:auto/span 21}.panel-grid .g-col-lg-22{grid-column:auto/span 22}.panel-grid .g-col-lg-23{grid-column:auto/span 23}.panel-grid .g-col-lg-24{grid-column:auto/span 24}.panel-grid .g-start-lg-1{grid-column-start:1}.panel-grid .g-start-lg-2{grid-column-start:2}.panel-grid .g-start-lg-3{grid-column-start:3}.panel-grid .g-start-lg-4{grid-column-start:4}.panel-grid .g-start-lg-5{grid-column-start:5}.panel-grid .g-start-lg-6{grid-column-start:6}.panel-grid .g-start-lg-7{grid-column-start:7}.panel-grid .g-start-lg-8{grid-column-start:8}.panel-grid .g-start-lg-9{grid-column-start:9}.panel-grid .g-start-lg-10{grid-column-start:10}.panel-grid .g-start-lg-11{grid-column-start:11}.panel-grid .g-start-lg-12{grid-column-start:12}.panel-grid .g-start-lg-13{grid-column-start:13}.panel-grid .g-start-lg-14{grid-column-start:14}.panel-grid .g-start-lg-15{grid-column-start:15}.panel-grid .g-start-lg-16{grid-column-start:16}.panel-grid .g-start-lg-17{grid-column-start:17}.panel-grid .g-start-lg-18{grid-column-start:18}.panel-grid .g-start-lg-19{grid-column-start:19}.panel-grid .g-start-lg-20{grid-column-start:20}.panel-grid .g-start-lg-21{grid-column-start:21}.panel-grid .g-start-lg-22{grid-column-start:22}.panel-grid .g-start-lg-23{grid-column-start:23}}@media(min-width: 1200px){.panel-grid .g-col-xl-1{grid-column:auto/span 1}.panel-grid .g-col-xl-2{grid-column:auto/span 2}.panel-grid .g-col-xl-3{grid-column:auto/span 3}.panel-grid .g-col-xl-4{grid-column:auto/span 4}.panel-grid .g-col-xl-5{grid-column:auto/span 5}.panel-grid .g-col-xl-6{grid-column:auto/span 6}.panel-grid .g-col-xl-7{grid-column:auto/span 7}.panel-grid .g-col-xl-8{grid-column:auto/span 8}.panel-grid .g-col-xl-9{grid-column:auto/span 9}.panel-grid .g-col-xl-10{grid-column:auto/span 10}.panel-grid .g-col-xl-11{grid-column:auto/span 11}.panel-grid .g-col-xl-12{grid-column:auto/span 12}.panel-grid .g-col-xl-13{grid-column:auto/span 13}.panel-grid .g-col-xl-14{grid-column:auto/span 14}.panel-grid .g-col-xl-15{grid-column:auto/span 15}.panel-grid .g-col-xl-16{grid-column:auto/span 16}.panel-grid .g-col-xl-17{grid-column:auto/span 17}.panel-grid .g-col-xl-18{grid-column:auto/span 18}.panel-grid .g-col-xl-19{grid-column:auto/span 19}.panel-grid .g-col-xl-20{grid-column:auto/span 20}.panel-grid .g-col-xl-21{grid-column:auto/span 21}.panel-grid .g-col-xl-22{grid-column:auto/span 22}.panel-grid .g-col-xl-23{grid-column:auto/span 23}.panel-grid .g-col-xl-24{grid-column:auto/span 24}.panel-grid .g-start-xl-1{grid-column-start:1}.panel-grid .g-start-xl-2{grid-column-start:2}.panel-grid .g-start-xl-3{grid-column-start:3}.panel-grid .g-start-xl-4{grid-column-start:4}.panel-grid .g-start-xl-5{grid-column-start:5}.panel-grid .g-start-xl-6{grid-column-start:6}.panel-grid .g-start-xl-7{grid-column-start:7}.panel-grid .g-start-xl-8{grid-column-start:8}.panel-grid .g-start-xl-9{grid-column-start:9}.panel-grid .g-start-xl-10{grid-column-start:10}.panel-grid .g-start-xl-11{grid-column-start:11}.panel-grid .g-start-xl-12{grid-column-start:12}.panel-grid .g-start-xl-13{grid-column-start:13}.panel-grid .g-start-xl-14{grid-column-start:14}.panel-grid .g-start-xl-15{grid-column-start:15}.panel-grid .g-start-xl-16{grid-column-start:16}.panel-grid .g-start-xl-17{grid-column-start:17}.panel-grid .g-start-xl-18{grid-column-start:18}.panel-grid .g-start-xl-19{grid-column-start:19}.panel-grid .g-start-xl-20{grid-column-start:20}.panel-grid .g-start-xl-21{grid-column-start:21}.panel-grid .g-start-xl-22{grid-column-start:22}.panel-grid .g-start-xl-23{grid-column-start:23}}@media(min-width: 1400px){.panel-grid .g-col-xxl-1{grid-column:auto/span 1}.panel-grid .g-col-xxl-2{grid-column:auto/span 2}.panel-grid .g-col-xxl-3{grid-column:auto/span 3}.panel-grid .g-col-xxl-4{grid-column:auto/span 4}.panel-grid .g-col-xxl-5{grid-column:auto/span 5}.panel-grid .g-col-xxl-6{grid-column:auto/span 6}.panel-grid .g-col-xxl-7{grid-column:auto/span 7}.panel-grid .g-col-xxl-8{grid-column:auto/span 8}.panel-grid .g-col-xxl-9{grid-column:auto/span 9}.panel-grid .g-col-xxl-10{grid-column:auto/span 10}.panel-grid .g-col-xxl-11{grid-column:auto/span 11}.panel-grid .g-col-xxl-12{grid-column:auto/span 12}.panel-grid .g-col-xxl-13{grid-column:auto/span 13}.panel-grid .g-col-xxl-14{grid-column:auto/span 14}.panel-grid .g-col-xxl-15{grid-column:auto/span 15}.panel-grid .g-col-xxl-16{grid-column:auto/span 16}.panel-grid .g-col-xxl-17{grid-column:auto/span 17}.panel-grid .g-col-xxl-18{grid-column:auto/span 18}.panel-grid .g-col-xxl-19{grid-column:auto/span 19}.panel-grid .g-col-xxl-20{grid-column:auto/span 20}.panel-grid .g-col-xxl-21{grid-column:auto/span 21}.panel-grid .g-col-xxl-22{grid-column:auto/span 22}.panel-grid .g-col-xxl-23{grid-column:auto/span 23}.panel-grid .g-col-xxl-24{grid-column:auto/span 24}.panel-grid .g-start-xxl-1{grid-column-start:1}.panel-grid .g-start-xxl-2{grid-column-start:2}.panel-grid .g-start-xxl-3{grid-column-start:3}.panel-grid .g-start-xxl-4{grid-column-start:4}.panel-grid .g-start-xxl-5{grid-column-start:5}.panel-grid .g-start-xxl-6{grid-column-start:6}.panel-grid .g-start-xxl-7{grid-column-start:7}.panel-grid .g-start-xxl-8{grid-column-start:8}.panel-grid .g-start-xxl-9{grid-column-start:9}.panel-grid .g-start-xxl-10{grid-column-start:10}.panel-grid .g-start-xxl-11{grid-column-start:11}.panel-grid .g-start-xxl-12{grid-column-start:12}.panel-grid .g-start-xxl-13{grid-column-start:13}.panel-grid .g-start-xxl-14{grid-column-start:14}.panel-grid .g-start-xxl-15{grid-column-start:15}.panel-grid .g-start-xxl-16{grid-column-start:16}.panel-grid .g-start-xxl-17{grid-column-start:17}.panel-grid .g-start-xxl-18{grid-column-start:18}.panel-grid .g-start-xxl-19{grid-column-start:19}.panel-grid .g-start-xxl-20{grid-column-start:20}.panel-grid .g-start-xxl-21{grid-column-start:21}.panel-grid .g-start-xxl-22{grid-column-start:22}.panel-grid .g-start-xxl-23{grid-column-start:23}}main{margin-top:1em;margin-bottom:1em}h1,.h1,h2,.h2{margin-top:2rem;margin-bottom:1rem}h1.title,.title.h1{margin-top:0}h2,.h2{border-bottom:1px solid #dee2e6;padding-bottom:.5rem}h3,.h3,h4,.h4{margin-top:1.5rem}.header-section-number{color:#747a7f}.nav-link.active .header-section-number{color:inherit}mark,.mark{padding:0em}.panel-caption,caption,.figure-caption{font-size:1rem}.panel-caption,.figure-caption,figcaption{color:#747a7f}.table-caption,caption{color:#373a3c}.quarto-layout-cell[data-ref-parent] caption{color:#747a7f}.column-margin figcaption,.margin-caption,div.aside,aside,.column-margin{color:#747a7f;font-size:.825rem}.panel-caption.margin-caption{text-align:inherit}.column-margin.column-container p{margin-bottom:0}.column-margin.column-container>*:not(.collapse){padding-top:.5em;padding-bottom:.5em;display:block}.column-margin.column-container>*.collapse:not(.show){display:none}@media(min-width: 768px){.column-margin.column-container .callout-margin-content:first-child{margin-top:4.5em}.column-margin.column-container .callout-margin-content-simple:first-child{margin-top:3.5em}}.margin-caption>*{padding-top:.5em;padding-bottom:.5em}@media(max-width: 767.98px){.quarto-layout-row{flex-direction:column}}.tab-content{margin-top:0px;border-left:#dee2e6 1px solid;border-right:#dee2e6 1px solid;border-bottom:#dee2e6 1px solid;margin-left:0;padding:1em;margin-bottom:1em}@media(max-width: 767.98px){.layout-sidebar{margin-left:0;margin-right:0}}.panel-sidebar,.panel-sidebar .form-control,.panel-input,.panel-input .form-control,.selectize-dropdown{font-size:.9rem}.panel-sidebar .form-control,.panel-input .form-control{padding-top:.1rem}.tab-pane div.sourceCode{margin-top:0px}.tab-pane>p{padding-top:1em}.tab-content>.tab-pane:not(.active){display:none !important}div.sourceCode{background-color:rgba(233,236,239,.65);border:1px solid rgba(233,236,239,.65);border-radius:.25rem}pre.sourceCode{background-color:transparent}pre.sourceCode{border:none;font-size:.875em;overflow:visible !important;padding:.4em}.callout pre.sourceCode{padding-left:0}div.sourceCode{overflow-y:hidden}.callout div.sourceCode{margin-left:initial}.blockquote{font-size:inherit;padding-left:1rem;padding-right:1.5rem;color:#747a7f}.blockquote h1:first-child,.blockquote .h1:first-child,.blockquote h2:first-child,.blockquote .h2:first-child,.blockquote h3:first-child,.blockquote .h3:first-child,.blockquote h4:first-child,.blockquote .h4:first-child,.blockquote h5:first-child,.blockquote .h5:first-child{margin-top:0}pre{background-color:initial;padding:initial;border:initial}p code:not(.sourceCode),li code:not(.sourceCode){background-color:#f7f7f7;padding:.2em}nav p code:not(.sourceCode),nav li code:not(.sourceCode){background-color:transparent;padding:0}#quarto-embedded-source-code-modal>.modal-dialog{max-width:1000px;padding-left:1.75rem;padding-right:1.75rem}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body{padding:0}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body div.sourceCode{margin:0;padding:.2rem .2rem;border-radius:0px;border:none}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-header{padding:.7rem}.code-tools-button{font-size:1rem;padding:.15rem .15rem;margin-left:5px;color:#6c757d;background-color:transparent;transition:initial;cursor:pointer}.code-tools-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.sidebar{will-change:top;transition:top 200ms linear;position:sticky;overflow-y:auto;padding-top:1.2em;max-height:100vh}.sidebar.toc-left,.sidebar.margin-sidebar{top:0px;padding-top:1em}.sidebar.toc-left>*,.sidebar.margin-sidebar>*{padding-top:.5em}.sidebar.quarto-banner-title-block-sidebar>*{padding-top:1.65em}.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-size:.875rem;font-weight:400;margin-bottom:.5rem;margin-top:.3rem;font-family:inherit;border-bottom:0;padding-bottom:0;padding-top:0px}.sidebar nav[role=doc-toc]>ul a{border-left:1px solid #e9ecef;padding-left:.6rem}.sidebar nav[role=doc-toc]>ul a:empty{display:none}.sidebar nav[role=doc-toc] ul{padding-left:0;list-style:none;font-size:.875rem;font-weight:300}.sidebar nav[role=doc-toc]>ul li a{line-height:1.1rem;padding-bottom:.2rem;padding-top:.2rem;color:inherit}.sidebar nav[role=doc-toc] ul>li>ul>li>a{padding-left:1.2em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>a{padding-left:2.4em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>a{padding-left:3.6em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:4.8em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:6em}.sidebar nav[role=doc-toc] ul>li>ul>li>a.active{border-left:1px solid #2780e3;color:#2780e3 !important}.sidebar nav[role=doc-toc] ul>li>a.active{border-left:1px solid #2780e3;color:#2780e3 !important}kbd,.kbd{color:#373a3c;background-color:#f8f9fa;border:1px solid;border-radius:5px;border-color:#dee2e6}div.hanging-indent{margin-left:1em;text-indent:-1em}.citation a,.footnote-ref{text-decoration:none}.footnotes ol{padding-left:1em}.tippy-content>*{margin-bottom:.7em}.tippy-content>*:last-child{margin-bottom:0}.table a{word-break:break-word}.table>:not(:first-child){border-top-width:1px;border-top-color:#dee2e6}.table>thead{border-bottom:1px solid currentColor}.table>tbody{border-top:1px solid #dee2e6}.callout{margin-top:1.25rem;margin-bottom:1.25rem;border-radius:.25rem}.callout.callout-style-simple{padding:.4em .7em;border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout.callout-style-default{border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout .callout-body-container{flex-grow:1}.callout.callout-style-simple .callout-body{font-size:.9rem;font-weight:400}.callout.callout-style-default .callout-body{font-size:.9rem;font-weight:400}.callout.callout-captioned .callout-body{margin-top:.2em}.callout:not(.no-icon).callout-captioned.callout-style-simple .callout-body{padding-left:1.6em}.callout.callout-captioned>.callout-header{padding-top:.2em;margin-bottom:-0.2em}.callout.callout-style-simple>div.callout-header{border-bottom:none;font-size:.9rem;font-weight:600;opacity:75%}.callout.callout-style-default>div.callout-header{border-bottom:none;font-weight:600;opacity:85%;font-size:.9rem;padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body{padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body>:first-child{margin-top:.5em}.callout>div.callout-header[data-bs-toggle=collapse]{cursor:pointer}.callout.callout-style-default .callout-header[aria-expanded=false],.callout.callout-style-default .callout-header[aria-expanded=true]{padding-top:0px;margin-bottom:0px;align-items:center}.callout.callout-captioned .callout-body>:last-child:not(.sourceCode),.callout.callout-captioned .callout-body>div>:last-child:not(.sourceCode){margin-bottom:.5rem}.callout:not(.callout-captioned) .callout-body>:first-child,.callout:not(.callout-captioned) .callout-body>div>:first-child{margin-top:.25rem}.callout:not(.callout-captioned) .callout-body>:last-child,.callout:not(.callout-captioned) .callout-body>div>:last-child{margin-bottom:.2rem}.callout.callout-style-simple .callout-icon::before,.callout.callout-style-simple .callout-toggle::before{height:1rem;width:1rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.callout.callout-style-default .callout-icon::before,.callout.callout-style-default .callout-toggle::before{height:.9rem;width:.9rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:.9rem .9rem}.callout.callout-style-default .callout-toggle::before{margin-top:5px}.callout .callout-btn-toggle .callout-toggle::before{transition:transform .2s linear}.callout .callout-header[aria-expanded=false] .callout-toggle::before{transform:rotate(-90deg)}.callout .callout-header[aria-expanded=true] .callout-toggle::before{transform:none}.callout.callout-style-simple:not(.no-icon) div.callout-icon-container{padding-top:.2em;padding-right:.55em}.callout.callout-style-default:not(.no-icon) div.callout-icon-container{padding-top:.1em;padding-right:.35em}.callout.callout-style-default:not(.no-icon) div.callout-caption-container{margin-top:-1px}.callout.callout-style-default.callout-caution:not(.no-icon) div.callout-icon-container{padding-top:.3em;padding-right:.35em}.callout>.callout-body>.callout-icon-container>.no-icon,.callout>.callout-header>.callout-icon-container>.no-icon{display:none}div.callout.callout{border-left-color:#6c757d}div.callout.callout-style-default>.callout-header{background-color:#6c757d}div.callout-note.callout{border-left-color:#2780e3}div.callout-note.callout-style-default>.callout-header{background-color:#e9f2fc}div.callout-note:not(.callout-captioned) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note.callout-captioned .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-tip.callout{border-left-color:#3fb618}div.callout-tip.callout-style-default>.callout-header{background-color:#ecf8e8}div.callout-tip:not(.callout-captioned) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip.callout-captioned .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-warning.callout{border-left-color:#ff7518}div.callout-warning.callout-style-default>.callout-header{background-color:#fff1e8}div.callout-warning:not(.callout-captioned) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning.callout-captioned .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-caution.callout{border-left-color:#f0ad4e}div.callout-caution.callout-style-default>.callout-header{background-color:#fef7ed}div.callout-caution:not(.callout-captioned) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution.callout-captioned .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-important.callout{border-left-color:#ff0039}div.callout-important.callout-style-default>.callout-header{background-color:#ffe6eb}div.callout-important:not(.callout-captioned) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important.callout-captioned .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important .callout-toggle::before{background-image:url('data:image/svg+xml,')}.quarto-toggle-container{display:flex}@media(min-width: 992px){.navbar .quarto-color-scheme-toggle{padding-left:.5rem;padding-right:.5rem}}@media(max-width: 767.98px){.navbar .quarto-color-scheme-toggle{padding-left:0;padding-right:0;padding-bottom:.5em}}.quarto-reader-toggle .bi::before,.quarto-color-scheme-toggle .bi::before{display:inline-block;height:1rem;width:1rem;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.navbar-collapse .quarto-color-scheme-toggle{padding-left:.6rem;padding-right:0;margin-top:-12px}.sidebar-navigation{padding-left:20px}.sidebar-navigation .quarto-color-scheme-toggle .bi::before{padding-top:.2rem;margin-bottom:-0.2rem}.sidebar-tools-main .quarto-color-scheme-toggle .bi::before{padding-top:.2rem;margin-bottom:-0.2rem}.navbar .quarto-color-scheme-toggle .bi::before{padding-top:7px;margin-bottom:-7px;padding-left:2px;margin-right:2px}.navbar .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.navbar .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.quarto-sidebar-toggle{border-color:#dee2e6;border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem;border-style:solid;border-width:1px;overflow:hidden;border-top-width:0px;padding-top:0px !important}.quarto-sidebar-toggle-title{cursor:pointer;padding-bottom:2px;margin-left:.25em;text-align:center;font-weight:400;font-size:.775em}#quarto-content .quarto-sidebar-toggle{background:#fafafa}#quarto-content .quarto-sidebar-toggle-title{color:#373a3c}.quarto-sidebar-toggle-icon{color:#dee2e6;margin-right:.5em;float:right;transition:transform .2s ease}.quarto-sidebar-toggle-icon::before{padding-top:5px}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-icon{transform:rotate(-180deg)}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-title{border-bottom:solid #dee2e6 1px}.quarto-sidebar-toggle-contents{background-color:#fff;padding-right:10px;padding-left:10px;margin-top:0px !important;transition:max-height .5s ease}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-contents{padding-top:1em;padding-bottom:10px}.quarto-sidebar-toggle:not(.expanded) .quarto-sidebar-toggle-contents{padding-top:0px !important;padding-bottom:0px}nav[role=doc-toc]{z-index:1020}#quarto-sidebar>*,nav[role=doc-toc]>*{transition:opacity .1s ease,border .1s ease}#quarto-sidebar.slow>*,nav[role=doc-toc].slow>*{transition:opacity .4s ease,border .4s ease}.quarto-color-scheme-toggle:not(.alternate).top-right .bi::before{background-image:url('data:image/svg+xml,')}.quarto-color-scheme-toggle.alternate.top-right .bi::before{background-image:url('data:image/svg+xml,')}#quarto-appendix.default{border-top:1px solid #dee2e6}#quarto-appendix.default{background-color:#fff;padding-top:1.5em;margin-top:2em;z-index:998}#quarto-appendix.default .quarto-appendix-heading{margin-top:0;line-height:1.4em;font-weight:600;opacity:.9;border-bottom:none;margin-bottom:0}#quarto-appendix.default .footnotes ol,#quarto-appendix.default .footnotes ol li>p:last-of-type,#quarto-appendix.default .quarto-appendix-contents>p:last-of-type{margin-bottom:0}#quarto-appendix.default .quarto-appendix-secondary-label{margin-bottom:.4em}#quarto-appendix.default .quarto-appendix-bibtex{font-size:.7em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-bibtex code.sourceCode{white-space:pre-wrap}#quarto-appendix.default .quarto-appendix-citeas{font-size:.9em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-heading{font-size:1em !important}#quarto-appendix.default *[role=doc-endnotes]>ol,#quarto-appendix.default .quarto-appendix-contents>*:not(h2):not(.h2){font-size:.9em}#quarto-appendix.default section{padding-bottom:1.5em}#quarto-appendix.default section *[role=doc-endnotes],#quarto-appendix.default section>*:not(a){opacity:.9;word-wrap:break-word}.btn.btn-quarto,div.cell-output-display .btn-quarto{color:#cbcccc;background-color:#373a3c;border-color:#373a3c}.btn.btn-quarto:hover,div.cell-output-display .btn-quarto:hover{color:#cbcccc;background-color:#555859;border-color:#4b4e50}.btn-check:focus+.btn.btn-quarto,.btn.btn-quarto:focus,.btn-check:focus+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:focus{color:#cbcccc;background-color:#555859;border-color:#4b4e50;box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn-check:checked+.btn.btn-quarto,.btn-check:active+.btn.btn-quarto,.btn.btn-quarto:active,.btn.btn-quarto.active,.show>.btn.btn-quarto.dropdown-toggle,.btn-check:checked+div.cell-output-display .btn-quarto,.btn-check:active+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:active,div.cell-output-display .btn-quarto.active,.show>div.cell-output-display .btn-quarto.dropdown-toggle{color:#fff;background-color:#5f6163;border-color:#4b4e50}.btn-check:checked+.btn.btn-quarto:focus,.btn-check:active+.btn.btn-quarto:focus,.btn.btn-quarto:active:focus,.btn.btn-quarto.active:focus,.show>.btn.btn-quarto.dropdown-toggle:focus,.btn-check:checked+div.cell-output-display .btn-quarto:focus,.btn-check:active+div.cell-output-display .btn-quarto:focus,div.cell-output-display .btn-quarto:active:focus,div.cell-output-display .btn-quarto.active:focus,.show>div.cell-output-display .btn-quarto.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn.btn-quarto:disabled,.btn.btn-quarto.disabled,div.cell-output-display .btn-quarto:disabled,div.cell-output-display .btn-quarto.disabled{color:#fff;background-color:#373a3c;border-color:#373a3c}nav.quarto-secondary-nav.color-navbar{background-color:#2780e3;color:#fdfeff}nav.quarto-secondary-nav.color-navbar h1,nav.quarto-secondary-nav.color-navbar .h1,nav.quarto-secondary-nav.color-navbar .quarto-btn-toggle{color:#fdfeff}@media(max-width: 991.98px){body.nav-sidebar .quarto-title-banner,body.nav-sidebar .quarto-title-banner{display:none}}p.subtitle{margin-top:.25em;margin-bottom:.5em}code a:any-link{color:inherit;text-decoration-color:#6c757d}/*! light */a.external:after{display:inline-block;height:.75rem;width:.75rem;margin-bottom:.15em;margin-left:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}a.external:after:hover{cursor:pointer}.quarto-ext-icon{display:inline-block;font-size:.75em;padding-left:.3em}.quarto-title-banner{margin-bottom:1em;color:#fdfeff;background:#2780e3}.quarto-title-banner .code-tools-button{color:#97cbff}.quarto-title-banner .code-tools-button:hover{color:#fdfeff}.quarto-title-banner .code-tools-button>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .quarto-title .title{font-weight:600}.quarto-title-banner .quarto-categories{margin-top:.75em}@media(min-width: 992px){.quarto-title-banner{padding-top:2.5em;padding-bottom:2.5em}}@media(max-width: 991.98px){.quarto-title-banner{padding-top:1em;padding-bottom:1em}}main.quarto-banner-title-block section:first-of-type h2:first-of-type,main.quarto-banner-title-block section:first-of-type .h2:first-of-type,main.quarto-banner-title-block section:first-of-type h3:first-of-type,main.quarto-banner-title-block section:first-of-type .h3:first-of-type,main.quarto-banner-title-block section:first-of-type h4:first-of-type,main.quarto-banner-title-block section:first-of-type .h4:first-of-type{margin-top:0}.quarto-title .quarto-categories{display:flex;column-gap:.4em;padding-bottom:.5em;margin-top:.25em}.quarto-title .quarto-categories .quarto-category{padding:.25em .75em;font-size:.65em;text-transform:uppercase;border:solid 1px;border-radius:.25rem;opacity:.6}.quarto-title .quarto-categories .quarto-category a{color:inherit}#title-block-header.quarto-title-block.default .quarto-title-meta{display:grid;grid-template-columns:repeat(2, 1fr)}#title-block-header.quarto-title-block.default .quarto-title .title{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-author-orcid img{margin-top:-5px}#title-block-header.quarto-title-block.default .quarto-description p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p,#title-block-header.quarto-title-block.default .quarto-title-authors p,#title-block-header.quarto-title-block.default .quarto-title-affiliations p{margin-bottom:.1em}#title-block-header.quarto-title-block.default .quarto-title-meta-heading{text-transform:uppercase;margin-top:1em;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-contents{font-size:.9em}#title-block-header.quarto-title-block.default .quarto-title-meta-contents a{color:#373a3c}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p.affiliation:last-of-type{margin-bottom:.7em}#title-block-header.quarto-title-block.default p.affiliation{margin-bottom:.1em}#title-block-header.quarto-title-block.default .description,#title-block-header.quarto-title-block.default .abstract{margin-top:0}#title-block-header.quarto-title-block.default .description>p,#title-block-header.quarto-title-block.default .abstract>p{font-size:.9em}#title-block-header.quarto-title-block.default .description>p:last-of-type,#title-block-header.quarto-title-block.default .abstract>p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .description .abstract-title,#title-block-header.quarto-title-block.default .abstract .abstract-title{margin-top:1em;text-transform:uppercase;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-author{display:grid;grid-template-columns:1fr 1fr}body{-webkit-font-smoothing:antialiased}.badge.bg-light{color:#373a3c}.progress .progress-bar{font-size:8px;line-height:8px}/*# sourceMappingURL=715b1393c7471b84a020498c36f87db5.css.map */ diff --git a/docs/site_libs/bootstrap/bootstrap.min.js b/docs/site_libs/bootstrap/bootstrap.min.js deleted file mode 100644 index cc0a255..0000000 --- a/docs/site_libs/bootstrap/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v5.1.3 (https://getbootstrap.com/) - * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t="transitionend",e=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e},i=t=>{const i=e(t);return i&&document.querySelector(i)?i:null},n=t=>{const i=e(t);return i?document.querySelector(i):null},s=e=>{e.dispatchEvent(new Event(t))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(t):null,a=(t,e,i)=>{Object.keys(i).forEach((n=>{const s=i[n],r=e[n],a=r&&o(r)?"element":null==(l=r)?`${l}`:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(s).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${s}".`)}))},l=t=>!(!o(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",(()=>{p.forEach((t=>t()))})),p.push(e)):e()},_=t=>{"function"==typeof t&&t()},b=(e,i,n=!0)=>{if(!n)return void _(e);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(i)+5;let r=!1;const a=({target:n})=>{n===i&&(r=!0,i.removeEventListener(t,a),_(e))};i.addEventListener(t,a),setTimeout((()=>{r||s(i)}),o)},v=(t,e,i,n)=>{let s=t.indexOf(e);if(-1===s)return t[!i&&n?t.length-1:0];const o=t.length;return s+=i?1:-1,n&&(s=(s+o)%o),t[Math.max(0,Math.min(s,o-1))]},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,E=/::\d+$/,A={};let T=1;const O={mouseenter:"mouseover",mouseleave:"mouseout"},C=/^(mouseenter|mouseleave)/i,k=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function L(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function x(t){const e=L(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function D(t,e,i=null){const n=Object.keys(t);for(let s=0,o=n.length;sfunction(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};n?n=t(n):i=t(i)}const[o,r,a]=S(e,i,n),l=x(t),c=l[a]||(l[a]={}),h=D(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=L(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(let a=o.length;a--;)if(o[a]===r)return s.delegateTarget=r,n.oneOff&&j.off(t,s.type,e,i),i.apply(r,[s]);return null}}(t,i,n):function(t,e){return function i(n){return n.delegateTarget=t,i.oneOff&&j.off(t,n.type,e),e.apply(t,[n])}}(t,i);u.delegationSelector=o?i:null,u.originalHandler=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function I(t,e,i,n,s){const o=D(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function P(t){return t=t.replace(w,""),O[t]||t}const j={on(t,e,i,n){N(t,e,i,n,!1)},one(t,e,i,n){N(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=x(t),c=e.startsWith(".");if(void 0!==o){if(!l||!l[r])return;return void I(t,l,r,o,s?i:null)}c&&Object.keys(l).forEach((i=>{!function(t,e,i,n){const s=e[i]||{};Object.keys(s).forEach((o=>{if(o.includes(n)){const n=s[o];I(t,e,i,n.originalHandler,n.delegationSelector)}}))}(t,l,i,e.slice(1))}));const h=l[r]||{};Object.keys(h).forEach((i=>{const n=i.replace(E,"");if(!a||e.includes(n)){const e=h[i];I(t,l,r,e.originalHandler,e.delegationSelector)}}))},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=f(),s=P(e),o=e!==s,r=k.has(s);let a,l=!0,c=!0,h=!1,d=null;return o&&n&&(a=n.Event(e,i),n(t).trigger(a),l=!a.isPropagationStopped(),c=!a.isImmediatePropagationStopped(),h=a.isDefaultPrevented()),r?(d=document.createEvent("HTMLEvents"),d.initEvent(s,l,!0)):d=new CustomEvent(e,{bubbles:l,cancelable:!0}),void 0!==i&&Object.keys(i).forEach((t=>{Object.defineProperty(d,t,{get:()=>i[t]})})),h&&d.preventDefault(),c&&t.dispatchEvent(d),d.defaultPrevented&&void 0!==a&&a.preventDefault(),d}},M=new Map,H={set(t,e,i){M.has(t)||M.set(t,new Map);const n=M.get(t);n.has(e)||0===n.size?n.set(e,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(n.keys())[0]}.`)},get:(t,e)=>M.has(t)&&M.get(t).get(e)||null,remove(t,e){if(!M.has(t))return;const i=M.get(t);i.delete(e),0===i.size&&M.delete(t)}};class B{constructor(t){(t=r(t))&&(this._element=t,H.set(this._element,this.constructor.DATA_KEY,this))}dispose(){H.remove(this._element,this.constructor.DATA_KEY),j.off(this._element,this.constructor.EVENT_KEY),Object.getOwnPropertyNames(this).forEach((t=>{this[t]=null}))}_queueCallback(t,e,i=!0){b(t,e,i)}static getInstance(t){return H.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.1.3"}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}}const R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,s=t.NAME;j.on(document,i,`[data-bs-dismiss="${s}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),c(this))return;const o=n(this)||this.closest(`.${s}`);t.getOrCreateInstance(o)[e]()}))};class W extends B{static get NAME(){return"alert"}close(){if(j.trigger(this._element,"close.bs.alert").defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),j.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=W.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(W,"close"),g(W);const $='[data-bs-toggle="button"]';class z extends B{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=z.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}function q(t){return"true"===t||"false"!==t&&(t===Number(t).toString()?Number(t):""===t||"null"===t?null:t)}function F(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}j.on(document,"click.bs.button.data-api",$,(t=>{t.preventDefault();const e=t.target.closest($);z.getOrCreateInstance(e).toggle()})),g(z);const U={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${F(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${F(e)}`)},getDataAttributes(t){if(!t)return{};const e={};return Object.keys(t.dataset).filter((t=>t.startsWith("bs"))).forEach((i=>{let n=i.replace(/^bs/,"");n=n.charAt(0).toLowerCase()+n.slice(1,n.length),e[n]=q(t.dataset[i])})),e},getDataAttribute:(t,e)=>q(t.getAttribute(`data-bs-${F(e)}`)),offset(t){const e=t.getBoundingClientRect();return{top:e.top+window.pageYOffset,left:e.left+window.pageXOffset}},position:t=>({top:t.offsetTop,left:t.offsetLeft})},V={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode;for(;n&&n.nodeType===Node.ELEMENT_NODE&&3!==n.nodeType;)n.matches(e)&&i.push(n),n=n.parentNode;return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(", ");return this.find(e,t).filter((t=>!c(t)&&l(t)))}},K="carousel",X={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},Y={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},Q="next",G="prev",Z="left",J="right",tt={ArrowLeft:J,ArrowRight:Z},et="slid.bs.carousel",it="active",nt=".active.carousel-item";class st extends B{constructor(t,e){super(t),this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._indicatorsElement=V.findOne(".carousel-indicators",this._element),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent),this._addEventListeners()}static get Default(){return X}static get NAME(){return K}next(){this._slide(Q)}nextWhenVisible(){!document.hidden&&l(this._element)&&this.next()}prev(){this._slide(G)}pause(t){t||(this._isPaused=!0),V.findOne(".carousel-item-next, .carousel-item-prev",this._element)&&(s(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null}cycle(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config&&this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))}to(t){this._activeElement=V.findOne(nt,this._element);const e=this._getItemIndex(this._activeElement);if(t>this._items.length-1||t<0)return;if(this._isSliding)return void j.one(this._element,et,(()=>this.to(t)));if(e===t)return this.pause(),void this.cycle();const i=t>e?Q:G;this._slide(i,this._items[t])}_getConfig(t){return t={...X,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(K,t,Y),t}_handleSwipe(){const t=Math.abs(this.touchDeltaX);if(t<=40)return;const e=t/this.touchDeltaX;this.touchDeltaX=0,e&&this._slide(e>0?J:Z)}_addEventListeners(){this._config.keyboard&&j.on(this._element,"keydown.bs.carousel",(t=>this._keydown(t))),"hover"===this._config.pause&&(j.on(this._element,"mouseenter.bs.carousel",(t=>this.pause(t))),j.on(this._element,"mouseleave.bs.carousel",(t=>this.cycle(t)))),this._config.touch&&this._touchSupported&&this._addTouchEventListeners()}_addTouchEventListeners(){const t=t=>this._pointerEvent&&("pen"===t.pointerType||"touch"===t.pointerType),e=e=>{t(e)?this.touchStartX=e.clientX:this._pointerEvent||(this.touchStartX=e.touches[0].clientX)},i=t=>{this.touchDeltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this.touchStartX},n=e=>{t(e)&&(this.touchDeltaX=e.clientX-this.touchStartX),this._handleSwipe(),"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((t=>this.cycle(t)),500+this._config.interval))};V.find(".carousel-item img",this._element).forEach((t=>{j.on(t,"dragstart.bs.carousel",(t=>t.preventDefault()))})),this._pointerEvent?(j.on(this._element,"pointerdown.bs.carousel",(t=>e(t))),j.on(this._element,"pointerup.bs.carousel",(t=>n(t))),this._element.classList.add("pointer-event")):(j.on(this._element,"touchstart.bs.carousel",(t=>e(t))),j.on(this._element,"touchmove.bs.carousel",(t=>i(t))),j.on(this._element,"touchend.bs.carousel",(t=>n(t))))}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=tt[t.key];e&&(t.preventDefault(),this._slide(e))}_getItemIndex(t){return this._items=t&&t.parentNode?V.find(".carousel-item",t.parentNode):[],this._items.indexOf(t)}_getItemByOrder(t,e){const i=t===Q;return v(this._items,e,i,this._config.wrap)}_triggerSlideEvent(t,e){const i=this._getItemIndex(t),n=this._getItemIndex(V.findOne(nt,this._element));return j.trigger(this._element,"slide.bs.carousel",{relatedTarget:t,direction:e,from:n,to:i})}_setActiveIndicatorElement(t){if(this._indicatorsElement){const e=V.findOne(".active",this._indicatorsElement);e.classList.remove(it),e.removeAttribute("aria-current");const i=V.find("[data-bs-target]",this._indicatorsElement);for(let e=0;e{j.trigger(this._element,et,{relatedTarget:o,direction:d,from:s,to:r})};if(this._element.classList.contains("slide")){o.classList.add(h),u(o),n.classList.add(c),o.classList.add(c);const t=()=>{o.classList.remove(c,h),o.classList.add(it),n.classList.remove(it,h,c),this._isSliding=!1,setTimeout(f,0)};this._queueCallback(t,n,!0)}else n.classList.remove(it),o.classList.add(it),this._isSliding=!1,f();a&&this.cycle()}_directionToOrder(t){return[J,Z].includes(t)?m()?t===Z?G:Q:t===Z?Q:G:t}_orderToDirection(t){return[Q,G].includes(t)?m()?t===G?Z:J:t===G?J:Z:t}static carouselInterface(t,e){const i=st.getOrCreateInstance(t,e);let{_config:n}=i;"object"==typeof e&&(n={...n,...e});const s="string"==typeof e?e:n.slide;if("number"==typeof e)i.to(e);else if("string"==typeof s){if(void 0===i[s])throw new TypeError(`No method named "${s}"`);i[s]()}else n.interval&&n.ride&&(i.pause(),i.cycle())}static jQueryInterface(t){return this.each((function(){st.carouselInterface(this,t)}))}static dataApiClickHandler(t){const e=n(this);if(!e||!e.classList.contains("carousel"))return;const i={...U.getDataAttributes(e),...U.getDataAttributes(this)},s=this.getAttribute("data-bs-slide-to");s&&(i.interval=!1),st.carouselInterface(e,i),s&&st.getInstance(e).to(s),t.preventDefault()}}j.on(document,"click.bs.carousel.data-api","[data-bs-slide], [data-bs-slide-to]",st.dataApiClickHandler),j.on(window,"load.bs.carousel.data-api",(()=>{const t=V.find('[data-bs-ride="carousel"]');for(let e=0,i=t.length;et===this._element));null!==s&&o.length&&(this._selector=s,this._triggerArray.push(e))}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return rt}static get NAME(){return ot}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t,e=[];if(this._config.parent){const t=V.find(ut,this._config.parent);e=V.find(".collapse.show, .collapse.collapsing",this._config.parent).filter((e=>!t.includes(e)))}const i=V.findOne(this._selector);if(e.length){const n=e.find((t=>i!==t));if(t=n?pt.getInstance(n):null,t&&t._isTransitioning)return}if(j.trigger(this._element,"show.bs.collapse").defaultPrevented)return;e.forEach((e=>{i!==e&&pt.getOrCreateInstance(e,{toggle:!1}).hide(),t||H.set(e,"bs.collapse",null)}));const n=this._getDimension();this._element.classList.remove(ct),this._element.classList.add(ht),this._element.style[n]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const s=`scroll${n[0].toUpperCase()+n.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(ht),this._element.classList.add(ct,lt),this._element.style[n]="",j.trigger(this._element,"shown.bs.collapse")}),this._element,!0),this._element.style[n]=`${this._element[s]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(j.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,u(this._element),this._element.classList.add(ht),this._element.classList.remove(ct,lt);const e=this._triggerArray.length;for(let t=0;t{this._isTransitioning=!1,this._element.classList.remove(ht),this._element.classList.add(ct),j.trigger(this._element,"hidden.bs.collapse")}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(lt)}_getConfig(t){return(t={...rt,...U.getDataAttributes(this._element),...t}).toggle=Boolean(t.toggle),t.parent=r(t.parent),a(ot,t,at),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=V.find(ut,this._config.parent);V.find(ft,this._config.parent).filter((e=>!t.includes(e))).forEach((t=>{const e=n(t);e&&this._addAriaAndCollapsedClass([t],this._isShown(e))}))}_addAriaAndCollapsedClass(t,e){t.length&&t.forEach((t=>{e?t.classList.remove(dt):t.classList.add(dt),t.setAttribute("aria-expanded",e)}))}static jQueryInterface(t){return this.each((function(){const e={};"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1);const i=pt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}j.on(document,"click.bs.collapse.data-api",ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();const e=i(this);V.find(e).forEach((t=>{pt.getOrCreateInstance(t,{toggle:!1}).toggle()}))})),g(pt);var mt="top",gt="bottom",_t="right",bt="left",vt="auto",yt=[mt,gt,_t,bt],wt="start",Et="end",At="clippingParents",Tt="viewport",Ot="popper",Ct="reference",kt=yt.reduce((function(t,e){return t.concat([e+"-"+wt,e+"-"+Et])}),[]),Lt=[].concat(yt,[vt]).reduce((function(t,e){return t.concat([e,e+"-"+wt,e+"-"+Et])}),[]),xt="beforeRead",Dt="read",St="afterRead",Nt="beforeMain",It="main",Pt="afterMain",jt="beforeWrite",Mt="write",Ht="afterWrite",Bt=[xt,Dt,St,Nt,It,Pt,jt,Mt,Ht];function Rt(t){return t?(t.nodeName||"").toLowerCase():null}function Wt(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function $t(t){return t instanceof Wt(t).Element||t instanceof Element}function zt(t){return t instanceof Wt(t).HTMLElement||t instanceof HTMLElement}function qt(t){return"undefined"!=typeof ShadowRoot&&(t instanceof Wt(t).ShadowRoot||t instanceof ShadowRoot)}const Ft={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];zt(s)&&Rt(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});zt(n)&&Rt(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function Ut(t){return t.split("-")[0]}function Vt(t,e){var i=t.getBoundingClientRect();return{width:i.width/1,height:i.height/1,top:i.top/1,right:i.right/1,bottom:i.bottom/1,left:i.left/1,x:i.left/1,y:i.top/1}}function Kt(t){var e=Vt(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Xt(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&qt(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Yt(t){return Wt(t).getComputedStyle(t)}function Qt(t){return["table","td","th"].indexOf(Rt(t))>=0}function Gt(t){return(($t(t)?t.ownerDocument:t.document)||window.document).documentElement}function Zt(t){return"html"===Rt(t)?t:t.assignedSlot||t.parentNode||(qt(t)?t.host:null)||Gt(t)}function Jt(t){return zt(t)&&"fixed"!==Yt(t).position?t.offsetParent:null}function te(t){for(var e=Wt(t),i=Jt(t);i&&Qt(i)&&"static"===Yt(i).position;)i=Jt(i);return i&&("html"===Rt(i)||"body"===Rt(i)&&"static"===Yt(i).position)?e:i||function(t){var e=-1!==navigator.userAgent.toLowerCase().indexOf("firefox");if(-1!==navigator.userAgent.indexOf("Trident")&&zt(t)&&"fixed"===Yt(t).position)return null;for(var i=Zt(t);zt(i)&&["html","body"].indexOf(Rt(i))<0;){var n=Yt(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function ee(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}var ie=Math.max,ne=Math.min,se=Math.round;function oe(t,e,i){return ie(t,ne(e,i))}function re(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function ae(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const le={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=Ut(i.placement),l=ee(a),c=[bt,_t].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return re("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:ae(t,yt))}(s.padding,i),d=Kt(o),u="y"===l?mt:bt,f="y"===l?gt:_t,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=te(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,E=oe(v,w,y),A=l;i.modifiersData[n]=((e={})[A]=E,e.centerOffset=E-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Xt(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function ce(t){return t.split("-")[1]}var he={top:"auto",right:"auto",bottom:"auto",left:"auto"};function de(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=!0===h?function(t){var e=t.x,i=t.y,n=window.devicePixelRatio||1;return{x:se(se(e*n)/n)||0,y:se(se(i*n)/n)||0}}(r):"function"==typeof h?h(r):r,u=d.x,f=void 0===u?0:u,p=d.y,m=void 0===p?0:p,g=r.hasOwnProperty("x"),_=r.hasOwnProperty("y"),b=bt,v=mt,y=window;if(c){var w=te(i),E="clientHeight",A="clientWidth";w===Wt(i)&&"static"!==Yt(w=Gt(i)).position&&"absolute"===a&&(E="scrollHeight",A="scrollWidth"),w=w,s!==mt&&(s!==bt&&s!==_t||o!==Et)||(v=gt,m-=w[E]-n.height,m*=l?1:-1),s!==bt&&(s!==mt&&s!==gt||o!==Et)||(b=_t,f-=w[A]-n.width,f*=l?1:-1)}var T,O=Object.assign({position:a},c&&he);return l?Object.assign({},O,((T={})[v]=_?"0":"",T[b]=g?"0":"",T.transform=(y.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",T)):Object.assign({},O,((e={})[v]=_?m+"px":"",e[b]=g?f+"px":"",e.transform="",e))}const ue={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:Ut(e.placement),variation:ce(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,de(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,de(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var fe={passive:!0};const pe={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=Wt(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,fe)})),a&&l.addEventListener("resize",i.update,fe),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,fe)})),a&&l.removeEventListener("resize",i.update,fe)}},data:{}};var me={left:"right",right:"left",bottom:"top",top:"bottom"};function ge(t){return t.replace(/left|right|bottom|top/g,(function(t){return me[t]}))}var _e={start:"end",end:"start"};function be(t){return t.replace(/start|end/g,(function(t){return _e[t]}))}function ve(t){var e=Wt(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ye(t){return Vt(Gt(t)).left+ve(t).scrollLeft}function we(t){var e=Yt(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ee(t){return["html","body","#document"].indexOf(Rt(t))>=0?t.ownerDocument.body:zt(t)&&we(t)?t:Ee(Zt(t))}function Ae(t,e){var i;void 0===e&&(e=[]);var n=Ee(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=Wt(n),r=s?[o].concat(o.visualViewport||[],we(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Ae(Zt(r)))}function Te(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Oe(t,e){return e===Tt?Te(function(t){var e=Wt(t),i=Gt(t),n=e.visualViewport,s=i.clientWidth,o=i.clientHeight,r=0,a=0;return n&&(s=n.width,o=n.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(r=n.offsetLeft,a=n.offsetTop)),{width:s,height:o,x:r+ye(t),y:a}}(t)):zt(e)?function(t){var e=Vt(t);return e.top=e.top+t.clientTop,e.left=e.left+t.clientLeft,e.bottom=e.top+t.clientHeight,e.right=e.left+t.clientWidth,e.width=t.clientWidth,e.height=t.clientHeight,e.x=e.left,e.y=e.top,e}(e):Te(function(t){var e,i=Gt(t),n=ve(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ie(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ie(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ye(t),l=-n.scrollTop;return"rtl"===Yt(s||i).direction&&(a+=ie(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Gt(t)))}function Ce(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?Ut(s):null,r=s?ce(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case mt:e={x:a,y:i.y-n.height};break;case gt:e={x:a,y:i.y+i.height};break;case _t:e={x:i.x+i.width,y:l};break;case bt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?ee(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case wt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Et:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ke(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.boundary,r=void 0===o?At:o,a=i.rootBoundary,l=void 0===a?Tt:a,c=i.elementContext,h=void 0===c?Ot:c,d=i.altBoundary,u=void 0!==d&&d,f=i.padding,p=void 0===f?0:f,m=re("number"!=typeof p?p:ae(p,yt)),g=h===Ot?Ct:Ot,_=t.rects.popper,b=t.elements[u?g:h],v=function(t,e,i){var n="clippingParents"===e?function(t){var e=Ae(Zt(t)),i=["absolute","fixed"].indexOf(Yt(t).position)>=0&&zt(t)?te(t):t;return $t(i)?e.filter((function(t){return $t(t)&&Xt(t,i)&&"body"!==Rt(t)})):[]}(t):[].concat(e),s=[].concat(n,[i]),o=s[0],r=s.reduce((function(e,i){var n=Oe(t,i);return e.top=ie(n.top,e.top),e.right=ne(n.right,e.right),e.bottom=ne(n.bottom,e.bottom),e.left=ie(n.left,e.left),e}),Oe(t,o));return r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}($t(b)?b:b.contextElement||Gt(t.elements.popper),r,l),y=Vt(t.elements.reference),w=Ce({reference:y,element:_,strategy:"absolute",placement:s}),E=Te(Object.assign({},_,w)),A=h===Ot?E:y,T={top:v.top-A.top+m.top,bottom:A.bottom-v.bottom+m.bottom,left:v.left-A.left+m.left,right:A.right-v.right+m.right},O=t.modifiersData.offset;if(h===Ot&&O){var C=O[s];Object.keys(T).forEach((function(t){var e=[_t,gt].indexOf(t)>=0?1:-1,i=[mt,gt].indexOf(t)>=0?"y":"x";T[t]+=C[i]*e}))}return T}function Le(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?Lt:l,h=ce(n),d=h?a?kt:kt.filter((function(t){return ce(t)===h})):yt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ke(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[Ut(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const xe={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=Ut(g),b=l||(_!==g&&p?function(t){if(Ut(t)===vt)return[];var e=ge(t);return[be(t),e,be(e)]}(g):[ge(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(Ut(i)===vt?Le(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,E=new Map,A=!0,T=v[0],O=0;O=0,D=x?"width":"height",S=ke(e,{placement:C,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),N=x?L?_t:bt:L?gt:mt;y[D]>w[D]&&(N=ge(N));var I=ge(N),P=[];if(o&&P.push(S[k]<=0),a&&P.push(S[N]<=0,S[I]<=0),P.every((function(t){return t}))){T=C,A=!1;break}E.set(C,P)}if(A)for(var j=function(t){var e=v.find((function(e){var i=E.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},M=p?3:1;M>0&&"break"!==j(M);M--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function De(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function Se(t){return[mt,_t,gt,bt].some((function(e){return t[e]>=0}))}const Ne={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ke(e,{elementContext:"reference"}),a=ke(e,{altBoundary:!0}),l=De(r,n),c=De(a,s,o),h=Se(l),d=Se(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Ie={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=Lt.reduce((function(t,i){return t[i]=function(t,e,i){var n=Ut(t),s=[bt,mt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[bt,_t].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},Pe={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=Ce({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},je={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ke(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=Ut(e.placement),b=ce(e.placement),v=!b,y=ee(_),w="x"===y?"y":"x",E=e.modifiersData.popperOffsets,A=e.rects.reference,T=e.rects.popper,O="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,C={x:0,y:0};if(E){if(o||a){var k="y"===y?mt:bt,L="y"===y?gt:_t,x="y"===y?"height":"width",D=E[y],S=E[y]+g[k],N=E[y]-g[L],I=f?-T[x]/2:0,P=b===wt?A[x]:T[x],j=b===wt?-T[x]:-A[x],M=e.elements.arrow,H=f&&M?Kt(M):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},R=B[k],W=B[L],$=oe(0,A[x],H[x]),z=v?A[x]/2-I-$-R-O:P-$-R-O,q=v?-A[x]/2+I+$+W+O:j+$+W+O,F=e.elements.arrow&&te(e.elements.arrow),U=F?"y"===y?F.clientTop||0:F.clientLeft||0:0,V=e.modifiersData.offset?e.modifiersData.offset[e.placement][y]:0,K=E[y]+z-V-U,X=E[y]+q-V;if(o){var Y=oe(f?ne(S,K):S,D,f?ie(N,X):N);E[y]=Y,C[y]=Y-D}if(a){var Q="x"===y?mt:bt,G="x"===y?gt:_t,Z=E[w],J=Z+g[Q],tt=Z-g[G],et=oe(f?ne(J,K):J,Z,f?ie(tt,X):tt);E[w]=et,C[w]=et-Z}}e.modifiersData[n]=C}},requiresIfExists:["offset"]};function Me(t,e,i){void 0===i&&(i=!1);var n=zt(e);zt(e)&&function(t){var e=t.getBoundingClientRect();e.width,t.offsetWidth,e.height,t.offsetHeight}(e);var s,o,r=Gt(e),a=Vt(t),l={scrollLeft:0,scrollTop:0},c={x:0,y:0};return(n||!n&&!i)&&(("body"!==Rt(e)||we(r))&&(l=(s=e)!==Wt(s)&&zt(s)?{scrollLeft:(o=s).scrollLeft,scrollTop:o.scrollTop}:ve(s)),zt(e)?((c=Vt(e)).x+=e.clientLeft,c.y+=e.clientTop):r&&(c.x=ye(r))),{x:a.left+l.scrollLeft-c.x,y:a.top+l.scrollTop-c.y,width:a.width,height:a.height}}function He(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var Be={placement:"bottom",modifiers:[],strategy:"absolute"};function Re(){for(var t=arguments.length,e=new Array(t),i=0;ij.on(t,"mouseover",d))),this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Je),this._element.classList.add(Je),j.trigger(this._element,"shown.bs.dropdown",t)}hide(){if(c(this._element)||!this._isShown(this._menu))return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){j.trigger(this._element,"hide.bs.dropdown",t).defaultPrevented||("ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>j.off(t,"mouseover",d))),this._popper&&this._popper.destroy(),this._menu.classList.remove(Je),this._element.classList.remove(Je),this._element.setAttribute("aria-expanded","false"),U.removeDataAttribute(this._menu,"popper"),j.trigger(this._element,"hidden.bs.dropdown",t))}_getConfig(t){if(t={...this.constructor.Default,...U.getDataAttributes(this._element),...t},a(Ue,t,this.constructor.DefaultType),"object"==typeof t.reference&&!o(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Ue.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(t){if(void 0===Fe)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let e=this._element;"parent"===this._config.reference?e=t:o(this._config.reference)?e=r(this._config.reference):"object"==typeof this._config.reference&&(e=this._config.reference);const i=this._getPopperConfig(),n=i.modifiers.find((t=>"applyStyles"===t.name&&!1===t.enabled));this._popper=qe(e,this._menu,i),n&&U.setDataAttribute(this._menu,"popper","static")}_isShown(t=this._element){return t.classList.contains(Je)}_getMenuElement(){return V.next(this._element,ei)[0]}_getPlacement(){const t=this._element.parentNode;if(t.classList.contains("dropend"))return ri;if(t.classList.contains("dropstart"))return ai;const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?ni:ii:e?oi:si}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return"static"===this._config.display&&(t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,..."function"==typeof this._config.popperConfig?this._config.popperConfig(t):this._config.popperConfig}}_selectMenuItem({key:t,target:e}){const i=V.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter(l);i.length&&v(i,e,t===Ye,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=hi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(t&&(2===t.button||"keyup"===t.type&&"Tab"!==t.key))return;const e=V.find(ti);for(let i=0,n=e.length;ie+t)),this._setElementAttributes(di,"paddingRight",(e=>e+t)),this._setElementAttributes(ui,"marginRight",(e=>e-t))}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t)[e];t.style[e]=`${i(Number.parseFloat(s))}px`}))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,"paddingRight"),this._resetElementAttributes(di,"paddingRight"),this._resetElementAttributes(ui,"marginRight")}_saveInitialAttribute(t,e){const i=t.style[e];i&&U.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=U.getDataAttribute(t,e);void 0===i?t.style.removeProperty(e):(U.removeDataAttribute(t,e),t.style[e]=i)}))}_applyManipulationCallback(t,e){o(t)?e(t):V.find(t,this._element).forEach(e)}isOverflowing(){return this.getWidth()>0}}const pi={className:"modal-backdrop",isVisible:!0,isAnimated:!1,rootElement:"body",clickCallback:null},mi={className:"string",isVisible:"boolean",isAnimated:"boolean",rootElement:"(element|string)",clickCallback:"(function|null)"},gi="show",_i="mousedown.bs.backdrop";class bi{constructor(t){this._config=this._getConfig(t),this._isAppended=!1,this._element=null}show(t){this._config.isVisible?(this._append(),this._config.isAnimated&&u(this._getElement()),this._getElement().classList.add(gi),this._emulateAnimation((()=>{_(t)}))):_(t)}hide(t){this._config.isVisible?(this._getElement().classList.remove(gi),this._emulateAnimation((()=>{this.dispose(),_(t)}))):_(t)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_getConfig(t){return(t={...pi,..."object"==typeof t?t:{}}).rootElement=r(t.rootElement),a("backdrop",t,mi),t}_append(){this._isAppended||(this._config.rootElement.append(this._getElement()),j.on(this._getElement(),_i,(()=>{_(this._config.clickCallback)})),this._isAppended=!0)}dispose(){this._isAppended&&(j.off(this._element,_i),this._element.remove(),this._isAppended=!1)}_emulateAnimation(t){b(t,this._getElement(),this._config.isAnimated)}}const vi={trapElement:null,autofocus:!0},yi={trapElement:"element",autofocus:"boolean"},wi=".bs.focustrap",Ei="backward";class Ai{constructor(t){this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}activate(){const{trapElement:t,autofocus:e}=this._config;this._isActive||(e&&t.focus(),j.off(document,wi),j.on(document,"focusin.bs.focustrap",(t=>this._handleFocusin(t))),j.on(document,"keydown.tab.bs.focustrap",(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,j.off(document,wi))}_handleFocusin(t){const{target:e}=t,{trapElement:i}=this._config;if(e===document||e===i||i.contains(e))return;const n=V.focusableChildren(i);0===n.length?i.focus():this._lastTabNavDirection===Ei?n[n.length-1].focus():n[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?Ei:"forward")}_getConfig(t){return t={...vi,..."object"==typeof t?t:{}},a("focustrap",t,yi),t}}const Ti="modal",Oi="Escape",Ci={backdrop:!0,keyboard:!0,focus:!0},ki={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean"},Li="hidden.bs.modal",xi="show.bs.modal",Di="resize.bs.modal",Si="click.dismiss.bs.modal",Ni="keydown.dismiss.bs.modal",Ii="mousedown.dismiss.bs.modal",Pi="modal-open",ji="show",Mi="modal-static";class Hi extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._dialog=V.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollBar=new fi}static get Default(){return Ci}static get NAME(){return Ti}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||j.trigger(this._element,xi,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isAnimated()&&(this._isTransitioning=!0),this._scrollBar.hide(),document.body.classList.add(Pi),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),j.on(this._dialog,Ii,(()=>{j.one(this._element,"mouseup.dismiss.bs.modal",(t=>{t.target===this._element&&(this._ignoreBackdropClick=!0)}))})),this._showBackdrop((()=>this._showElement(t))))}hide(){if(!this._isShown||this._isTransitioning)return;if(j.trigger(this._element,"hide.bs.modal").defaultPrevented)return;this._isShown=!1;const t=this._isAnimated();t&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),this._focustrap.deactivate(),this._element.classList.remove(ji),j.off(this._element,Si),j.off(this._dialog,Ii),this._queueCallback((()=>this._hideModal()),this._element,t)}dispose(){[window,this._dialog].forEach((t=>j.off(t,".bs.modal"))),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new bi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Ai({trapElement:this._element})}_getConfig(t){return t={...Ci,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(Ti,t,ki),t}_showElement(t){const e=this._isAnimated(),i=V.findOne(".modal-body",this._dialog);this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0,i&&(i.scrollTop=0),e&&u(this._element),this._element.classList.add(ji),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,j.trigger(this._element,"shown.bs.modal",{relatedTarget:t})}),this._dialog,e)}_setEscapeEvent(){this._isShown?j.on(this._element,Ni,(t=>{this._config.keyboard&&t.key===Oi?(t.preventDefault(),this.hide()):this._config.keyboard||t.key!==Oi||this._triggerBackdropTransition()})):j.off(this._element,Ni)}_setResizeEvent(){this._isShown?j.on(window,Di,(()=>this._adjustDialog())):j.off(window,Di)}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(Pi),this._resetAdjustments(),this._scrollBar.reset(),j.trigger(this._element,Li)}))}_showBackdrop(t){j.on(this._element,Si,(t=>{this._ignoreBackdropClick?this._ignoreBackdropClick=!1:t.target===t.currentTarget&&(!0===this._config.backdrop?this.hide():"static"===this._config.backdrop&&this._triggerBackdropTransition())})),this._backdrop.show(t)}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(j.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const{classList:t,scrollHeight:e,style:i}=this._element,n=e>document.documentElement.clientHeight;!n&&"hidden"===i.overflowY||t.contains(Mi)||(n||(i.overflowY="hidden"),t.add(Mi),this._queueCallback((()=>{t.remove(Mi),n||this._queueCallback((()=>{i.overflowY=""}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;(!i&&t&&!m()||i&&!t&&m())&&(this._element.style.paddingLeft=`${e}px`),(i&&!t&&!m()||!i&&t&&m())&&(this._element.style.paddingRight=`${e}px`)}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Hi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}j.on(document,"click.bs.modal.data-api",'[data-bs-toggle="modal"]',(function(t){const e=n(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),j.one(e,xi,(t=>{t.defaultPrevented||j.one(e,Li,(()=>{l(this)&&this.focus()}))}));const i=V.findOne(".modal.show");i&&Hi.getInstance(i).hide(),Hi.getOrCreateInstance(e).toggle(this)})),R(Hi),g(Hi);const Bi="offcanvas",Ri={backdrop:!0,keyboard:!0,scroll:!1},Wi={backdrop:"boolean",keyboard:"boolean",scroll:"boolean"},$i="show",zi=".offcanvas.show",qi="hidden.bs.offcanvas";class Fi extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get NAME(){return Bi}static get Default(){return Ri}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||j.trigger(this._element,"show.bs.offcanvas",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._element.style.visibility="visible",this._backdrop.show(),this._config.scroll||(new fi).hide(),this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add($i),this._queueCallback((()=>{this._config.scroll||this._focustrap.activate(),j.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(j.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.remove($i),this._backdrop.hide(),this._queueCallback((()=>{this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._element.style.visibility="hidden",this._config.scroll||(new fi).reset(),j.trigger(this._element,qi)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_getConfig(t){return t={...Ri,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(Bi,t,Wi),t}_initializeBackDrop(){return new bi({className:"offcanvas-backdrop",isVisible:this._config.backdrop,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:()=>this.hide()})}_initializeFocusTrap(){return new Ai({trapElement:this._element})}_addEventListeners(){j.on(this._element,"keydown.dismiss.bs.offcanvas",(t=>{this._config.keyboard&&"Escape"===t.key&&this.hide()}))}static jQueryInterface(t){return this.each((function(){const e=Fi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}j.on(document,"click.bs.offcanvas.data-api",'[data-bs-toggle="offcanvas"]',(function(t){const e=n(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this))return;j.one(e,qi,(()=>{l(this)&&this.focus()}));const i=V.findOne(zi);i&&i!==e&&Fi.getInstance(i).hide(),Fi.getOrCreateInstance(e).toggle(this)})),j.on(window,"load.bs.offcanvas.data-api",(()=>V.find(zi).forEach((t=>Fi.getOrCreateInstance(t).show())))),R(Fi),g(Fi);const Ui=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Vi=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,Ki=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i,Xi=(t,e)=>{const i=t.nodeName.toLowerCase();if(e.includes(i))return!Ui.has(i)||Boolean(Vi.test(t.nodeValue)||Ki.test(t.nodeValue));const n=e.filter((t=>t instanceof RegExp));for(let t=0,e=n.length;t{Xi(t,r)||i.removeAttribute(t.nodeName)}))}return n.body.innerHTML}const Qi="tooltip",Gi=new Set(["sanitize","allowList","sanitizeFn"]),Zi={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(array|string|function)",container:"(string|element|boolean)",fallbackPlacements:"array",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",allowList:"object",popperConfig:"(null|object|function)"},Ji={AUTO:"auto",TOP:"top",RIGHT:m()?"left":"right",BOTTOM:"bottom",LEFT:m()?"right":"left"},tn={animation:!0,template:'',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:[0,0],container:!1,fallbackPlacements:["top","right","bottom","left"],boundary:"clippingParents",customClass:"",sanitize:!0,sanitizeFn:null,allowList:{"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},popperConfig:null},en={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},nn="fade",sn="show",on="show",rn="out",an=".tooltip-inner",ln=".modal",cn="hide.bs.modal",hn="hover",dn="focus";class un extends B{constructor(t,e){if(void 0===Fe)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t),this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this._config=this._getConfig(e),this.tip=null,this._setListeners()}static get Default(){return tn}static get NAME(){return Qi}static get Event(){return en}static get DefaultType(){return Zi}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(t){if(this._isEnabled)if(t){const e=this._initializeOnDelegatedTarget(t);e._activeTrigger.click=!e._activeTrigger.click,e._isWithActiveTrigger()?e._enter(null,e):e._leave(null,e)}else{if(this.getTipElement().classList.contains(sn))return void this._leave(null,this);this._enter(null,this)}}dispose(){clearTimeout(this._timeout),j.off(this._element.closest(ln),cn,this._hideModalHandler),this.tip&&this.tip.remove(),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this.isWithContent()||!this._isEnabled)return;const t=j.trigger(this._element,this.constructor.Event.SHOW),e=h(this._element),i=null===e?this._element.ownerDocument.documentElement.contains(this._element):e.contains(this._element);if(t.defaultPrevented||!i)return;"tooltip"===this.constructor.NAME&&this.tip&&this.getTitle()!==this.tip.querySelector(an).innerHTML&&(this._disposePopper(),this.tip.remove(),this.tip=null);const n=this.getTipElement(),s=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME);n.setAttribute("id",s),this._element.setAttribute("aria-describedby",s),this._config.animation&&n.classList.add(nn);const o="function"==typeof this._config.placement?this._config.placement.call(this,n,this._element):this._config.placement,r=this._getAttachment(o);this._addAttachmentClass(r);const{container:a}=this._config;H.set(n,this.constructor.DATA_KEY,this),this._element.ownerDocument.documentElement.contains(this.tip)||(a.append(n),j.trigger(this._element,this.constructor.Event.INSERTED)),this._popper?this._popper.update():this._popper=qe(this._element,n,this._getPopperConfig(r)),n.classList.add(sn);const l=this._resolvePossibleFunction(this._config.customClass);l&&n.classList.add(...l.split(" ")),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>{j.on(t,"mouseover",d)}));const c=this.tip.classList.contains(nn);this._queueCallback((()=>{const t=this._hoverState;this._hoverState=null,j.trigger(this._element,this.constructor.Event.SHOWN),t===rn&&this._leave(null,this)}),this.tip,c)}hide(){if(!this._popper)return;const t=this.getTipElement();if(j.trigger(this._element,this.constructor.Event.HIDE).defaultPrevented)return;t.classList.remove(sn),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>j.off(t,"mouseover",d))),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1;const e=this.tip.classList.contains(nn);this._queueCallback((()=>{this._isWithActiveTrigger()||(this._hoverState!==on&&t.remove(),this._cleanTipClass(),this._element.removeAttribute("aria-describedby"),j.trigger(this._element,this.constructor.Event.HIDDEN),this._disposePopper())}),this.tip,e),this._hoverState=""}update(){null!==this._popper&&this._popper.update()}isWithContent(){return Boolean(this.getTitle())}getTipElement(){if(this.tip)return this.tip;const t=document.createElement("div");t.innerHTML=this._config.template;const e=t.children[0];return this.setContent(e),e.classList.remove(nn,sn),this.tip=e,this.tip}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),an)}_sanitizeAndSetContent(t,e,i){const n=V.findOne(i,t);e||!n?this.setElementContent(n,e):n.remove()}setElementContent(t,e){if(null!==t)return o(e)?(e=r(e),void(this._config.html?e.parentNode!==t&&(t.innerHTML="",t.append(e)):t.textContent=e.textContent)):void(this._config.html?(this._config.sanitize&&(e=Yi(e,this._config.allowList,this._config.sanitizeFn)),t.innerHTML=e):t.textContent=e)}getTitle(){const t=this._element.getAttribute("data-bs-original-title")||this._config.title;return this._resolvePossibleFunction(t)}updateAttachment(t){return"right"===t?"end":"left"===t?"start":t}_initializeOnDelegatedTarget(t,e){return e||this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return"function"==typeof t?t.call(this._element):t}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"onChange",enabled:!0,phase:"afterWrite",fn:t=>this._handlePopperPlacementChange(t)}],onFirstUpdate:t=>{t.options.placement!==t.placement&&this._handlePopperPlacementChange(t)}};return{...e,..."function"==typeof this._config.popperConfig?this._config.popperConfig(e):this._config.popperConfig}}_addAttachmentClass(t){this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(t)}`)}_getAttachment(t){return Ji[t.toUpperCase()]}_setListeners(){this._config.trigger.split(" ").forEach((t=>{if("click"===t)j.on(this._element,this.constructor.Event.CLICK,this._config.selector,(t=>this.toggle(t)));else if("manual"!==t){const e=t===hn?this.constructor.Event.MOUSEENTER:this.constructor.Event.FOCUSIN,i=t===hn?this.constructor.Event.MOUSELEAVE:this.constructor.Event.FOCUSOUT;j.on(this._element,e,this._config.selector,(t=>this._enter(t))),j.on(this._element,i,this._config.selector,(t=>this._leave(t)))}})),this._hideModalHandler=()=>{this._element&&this.hide()},j.on(this._element.closest(ln),cn,this._hideModalHandler),this._config.selector?this._config={...this._config,trigger:"manual",selector:""}:this._fixTitle()}_fixTitle(){const t=this._element.getAttribute("title"),e=typeof this._element.getAttribute("data-bs-original-title");(t||"string"!==e)&&(this._element.setAttribute("data-bs-original-title",t||""),!t||this._element.getAttribute("aria-label")||this._element.textContent||this._element.setAttribute("aria-label",t),this._element.setAttribute("title",""))}_enter(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusin"===t.type?dn:hn]=!0),e.getTipElement().classList.contains(sn)||e._hoverState===on?e._hoverState=on:(clearTimeout(e._timeout),e._hoverState=on,e._config.delay&&e._config.delay.show?e._timeout=setTimeout((()=>{e._hoverState===on&&e.show()}),e._config.delay.show):e.show())}_leave(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusout"===t.type?dn:hn]=e._element.contains(t.relatedTarget)),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=rn,e._config.delay&&e._config.delay.hide?e._timeout=setTimeout((()=>{e._hoverState===rn&&e.hide()}),e._config.delay.hide):e.hide())}_isWithActiveTrigger(){for(const t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1}_getConfig(t){const e=U.getDataAttributes(this._element);return Object.keys(e).forEach((t=>{Gi.has(t)&&delete e[t]})),(t={...this.constructor.Default,...e,..."object"==typeof t&&t?t:{}}).container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),a(Qi,t,this.constructor.DefaultType),t.sanitize&&(t.template=Yi(t.template,t.allowList,t.sanitizeFn)),t}_getDelegateConfig(){const t={};for(const e in this._config)this.constructor.Default[e]!==this._config[e]&&(t[e]=this._config[e]);return t}_cleanTipClass(){const t=this.getTipElement(),e=new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`,"g"),i=t.getAttribute("class").match(e);null!==i&&i.length>0&&i.map((t=>t.trim())).forEach((e=>t.classList.remove(e)))}_getBasicClassPrefix(){return"bs-tooltip"}_handlePopperPlacementChange(t){const{state:e}=t;e&&(this.tip=e.elements.popper,this._cleanTipClass(),this._addAttachmentClass(this._getAttachment(e.placement)))}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null)}static jQueryInterface(t){return this.each((function(){const e=un.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}g(un);const fn={...un.Default,placement:"right",offset:[0,8],trigger:"click",content:"",template:''},pn={...un.DefaultType,content:"(string|element|function)"},mn={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"};class gn extends un{static get Default(){return fn}static get NAME(){return"popover"}static get Event(){return mn}static get DefaultType(){return pn}isWithContent(){return this.getTitle()||this._getContent()}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),".popover-header"),this._sanitizeAndSetContent(t,this._getContent(),".popover-body")}_getContent(){return this._resolvePossibleFunction(this._config.content)}_getBasicClassPrefix(){return"bs-popover"}static jQueryInterface(t){return this.each((function(){const e=gn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}g(gn);const _n="scrollspy",bn={offset:10,method:"auto",target:""},vn={offset:"number",method:"string",target:"(string|element)"},yn="active",wn=".nav-link, .list-group-item, .dropdown-item",En="position";class An extends B{constructor(t,e){super(t),this._scrollElement="BODY"===this._element.tagName?window:this._element,this._config=this._getConfig(e),this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,j.on(this._scrollElement,"scroll.bs.scrollspy",(()=>this._process())),this.refresh(),this._process()}static get Default(){return bn}static get NAME(){return _n}refresh(){const t=this._scrollElement===this._scrollElement.window?"offset":En,e="auto"===this._config.method?t:this._config.method,n=e===En?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),V.find(wn,this._config.target).map((t=>{const s=i(t),o=s?V.findOne(s):null;if(o){const t=o.getBoundingClientRect();if(t.width||t.height)return[U[e](o).top+n,s]}return null})).filter((t=>t)).sort(((t,e)=>t[0]-e[0])).forEach((t=>{this._offsets.push(t[0]),this._targets.push(t[1])}))}dispose(){j.off(this._scrollElement,".bs.scrollspy"),super.dispose()}_getConfig(t){return(t={...bn,...U.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}}).target=r(t.target)||document.documentElement,a(_n,t,vn),t}_getScrollTop(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop}_getScrollHeight(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}_getOffsetHeight(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height}_process(){const t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),i=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=i){const t=this._targets[this._targets.length-1];this._activeTarget!==t&&this._activate(t)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(let e=this._offsets.length;e--;)this._activeTarget!==this._targets[e]&&t>=this._offsets[e]&&(void 0===this._offsets[e+1]||t`${e}[data-bs-target="${t}"],${e}[href="${t}"]`)),i=V.findOne(e.join(","),this._config.target);i.classList.add(yn),i.classList.contains("dropdown-item")?V.findOne(".dropdown-toggle",i.closest(".dropdown")).classList.add(yn):V.parents(i,".nav, .list-group").forEach((t=>{V.prev(t,".nav-link, .list-group-item").forEach((t=>t.classList.add(yn))),V.prev(t,".nav-item").forEach((t=>{V.children(t,".nav-link").forEach((t=>t.classList.add(yn)))}))})),j.trigger(this._scrollElement,"activate.bs.scrollspy",{relatedTarget:t})}_clear(){V.find(wn,this._config.target).filter((t=>t.classList.contains(yn))).forEach((t=>t.classList.remove(yn)))}static jQueryInterface(t){return this.each((function(){const e=An.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(window,"load.bs.scrollspy.data-api",(()=>{V.find('[data-bs-spy="scroll"]').forEach((t=>new An(t)))})),g(An);const Tn="active",On="fade",Cn="show",kn=".active",Ln=":scope > li > .active";class xn extends B{static get NAME(){return"tab"}show(){if(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&this._element.classList.contains(Tn))return;let t;const e=n(this._element),i=this._element.closest(".nav, .list-group");if(i){const e="UL"===i.nodeName||"OL"===i.nodeName?Ln:kn;t=V.find(e,i),t=t[t.length-1]}const s=t?j.trigger(t,"hide.bs.tab",{relatedTarget:this._element}):null;if(j.trigger(this._element,"show.bs.tab",{relatedTarget:t}).defaultPrevented||null!==s&&s.defaultPrevented)return;this._activate(this._element,i);const o=()=>{j.trigger(t,"hidden.bs.tab",{relatedTarget:this._element}),j.trigger(this._element,"shown.bs.tab",{relatedTarget:t})};e?this._activate(e,e.parentNode,o):o()}_activate(t,e,i){const n=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?V.children(e,kn):V.find(Ln,e))[0],s=i&&n&&n.classList.contains(On),o=()=>this._transitionComplete(t,n,i);n&&s?(n.classList.remove(Cn),this._queueCallback(o,t,!0)):o()}_transitionComplete(t,e,i){if(e){e.classList.remove(Tn);const t=V.findOne(":scope > .dropdown-menu .active",e.parentNode);t&&t.classList.remove(Tn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}t.classList.add(Tn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),u(t),t.classList.contains(On)&&t.classList.add(Cn);let n=t.parentNode;if(n&&"LI"===n.nodeName&&(n=n.parentNode),n&&n.classList.contains("dropdown-menu")){const e=t.closest(".dropdown");e&&V.find(".dropdown-toggle",e).forEach((t=>t.classList.add(Tn))),t.setAttribute("aria-expanded",!0)}i&&i()}static jQueryInterface(t){return this.each((function(){const e=xn.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(document,"click.bs.tab.data-api",'[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this)||xn.getOrCreateInstance(this).show()})),g(xn);const Dn="toast",Sn="hide",Nn="show",In="showing",Pn={animation:"boolean",autohide:"boolean",delay:"number"},jn={animation:!0,autohide:!0,delay:5e3};class Mn extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get DefaultType(){return Pn}static get Default(){return jn}static get NAME(){return Dn}show(){j.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(Sn),u(this._element),this._element.classList.add(Nn),this._element.classList.add(In),this._queueCallback((()=>{this._element.classList.remove(In),j.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this._element.classList.contains(Nn)&&(j.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add(In),this._queueCallback((()=>{this._element.classList.add(Sn),this._element.classList.remove(In),this._element.classList.remove(Nn),j.trigger(this._element,"hidden.bs.toast")}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this._element.classList.contains(Nn)&&this._element.classList.remove(Nn),super.dispose()}_getConfig(t){return t={...jn,...U.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}},a(Dn,t,this.constructor.DefaultType),t}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){j.on(this._element,"mouseover.bs.toast",(t=>this._onInteraction(t,!0))),j.on(this._element,"mouseout.bs.toast",(t=>this._onInteraction(t,!1))),j.on(this._element,"focusin.bs.toast",(t=>this._onInteraction(t,!0))),j.on(this._element,"focusout.bs.toast",(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=Mn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(Mn),g(Mn),{Alert:W,Button:z,Carousel:st,Collapse:pt,Dropdown:hi,Modal:Hi,Offcanvas:Fi,Popover:gn,ScrollSpy:An,Tab:xn,Toast:Mn,Tooltip:un}})); -//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/docs/site_libs/clipboard/clipboard.min.js b/docs/site_libs/clipboard/clipboard.min.js deleted file mode 100644 index 41c6a0f..0000000 --- a/docs/site_libs/clipboard/clipboard.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * clipboard.js v2.0.10 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1.anchorjs-link,.anchorjs-link:focus{opacity:1}",u.sheet.cssRules.length),u.sheet.insertRule("[data-anchorjs-icon]::after{content:attr(data-anchorjs-icon)}",u.sheet.cssRules.length),u.sheet.insertRule('@font-face{font-family:anchorjs-icons;src:url(data:n/a;base64,AAEAAAALAIAAAwAwT1MvMg8yG2cAAAE4AAAAYGNtYXDp3gC3AAABpAAAAExnYXNwAAAAEAAAA9wAAAAIZ2x5ZlQCcfwAAAH4AAABCGhlYWQHFvHyAAAAvAAAADZoaGVhBnACFwAAAPQAAAAkaG10eASAADEAAAGYAAAADGxvY2EACACEAAAB8AAAAAhtYXhwAAYAVwAAARgAAAAgbmFtZQGOH9cAAAMAAAAAunBvc3QAAwAAAAADvAAAACAAAQAAAAEAAHzE2p9fDzz1AAkEAAAAAADRecUWAAAAANQA6R8AAAAAAoACwAAAAAgAAgAAAAAAAAABAAADwP/AAAACgAAA/9MCrQABAAAAAAAAAAAAAAAAAAAAAwABAAAAAwBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMCQAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAg//0DwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAIAAAACgAAxAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADAAAAAIAAgAAgAAACDpy//9//8AAAAg6cv//f///+EWNwADAAEAAAAAAAAAAAAAAAAACACEAAEAAAAAAAAAAAAAAAAxAAACAAQARAKAAsAAKwBUAAABIiYnJjQ3NzY2MzIWFxYUBwcGIicmNDc3NjQnJiYjIgYHBwYUFxYUBwYGIwciJicmNDc3NjIXFhQHBwYUFxYWMzI2Nzc2NCcmNDc2MhcWFAcHBgYjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAAADACWAAEAAAAAAAEACAAAAAEAAAAAAAIAAwAIAAEAAAAAAAMACAAAAAEAAAAAAAQACAAAAAEAAAAAAAUAAQALAAEAAAAAAAYACAAAAAMAAQQJAAEAEAAMAAMAAQQJAAIABgAcAAMAAQQJAAMAEAAMAAMAAQQJAAQAEAAMAAMAAQQJAAUAAgAiAAMAAQQJAAYAEAAMYW5jaG9yanM0MDBAAGEAbgBjAGgAbwByAGoAcwA0ADAAMABAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAH//wAP) format("truetype")}',u.sheet.cssRules.length)),u=document.querySelectorAll("[id]"),t=[].map.call(u,function(A){return A.id}),i=0;i\]./()*\\\n\t\b\v\u00A0]/g,"-").replace(/-{2,}/g,"-").substring(0,this.options.truncate).replace(/^-+|-+$/gm,"").toLowerCase()},this.hasAnchorJSLink=function(A){var e=A.firstChild&&-1<(" "+A.firstChild.className+" ").indexOf(" anchorjs-link "),A=A.lastChild&&-1<(" "+A.lastChild.className+" ").indexOf(" anchorjs-link ");return e||A||!1}}}); -// @license-end \ No newline at end of file diff --git a/docs/site_libs/quarto-html/popper.min.js b/docs/site_libs/quarto-html/popper.min.js deleted file mode 100644 index 2269d66..0000000 --- a/docs/site_libs/quarto-html/popper.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @popperjs/core v2.11.4 - MIT License - */ - -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Popper={})}(this,(function(e){"use strict";function t(e){if(null==e)return window;if("[object Window]"!==e.toString()){var t=e.ownerDocument;return t&&t.defaultView||window}return e}function n(e){return e instanceof t(e).Element||e instanceof Element}function r(e){return e instanceof t(e).HTMLElement||e instanceof HTMLElement}function o(e){return"undefined"!=typeof ShadowRoot&&(e instanceof t(e).ShadowRoot||e instanceof ShadowRoot)}var i=Math.max,a=Math.min,s=Math.round;function f(e,t){void 0===t&&(t=!1);var n=e.getBoundingClientRect(),o=1,i=1;if(r(e)&&t){var a=e.offsetHeight,f=e.offsetWidth;f>0&&(o=s(n.width)/f||1),a>0&&(i=s(n.height)/a||1)}return{width:n.width/o,height:n.height/i,top:n.top/i,right:n.right/o,bottom:n.bottom/i,left:n.left/o,x:n.left/o,y:n.top/i}}function c(e){var n=t(e);return{scrollLeft:n.pageXOffset,scrollTop:n.pageYOffset}}function p(e){return e?(e.nodeName||"").toLowerCase():null}function u(e){return((n(e)?e.ownerDocument:e.document)||window.document).documentElement}function l(e){return f(u(e)).left+c(e).scrollLeft}function d(e){return t(e).getComputedStyle(e)}function h(e){var t=d(e),n=t.overflow,r=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+r)}function m(e,n,o){void 0===o&&(o=!1);var i,a,d=r(n),m=r(n)&&function(e){var t=e.getBoundingClientRect(),n=s(t.width)/e.offsetWidth||1,r=s(t.height)/e.offsetHeight||1;return 1!==n||1!==r}(n),v=u(n),g=f(e,m),y={scrollLeft:0,scrollTop:0},b={x:0,y:0};return(d||!d&&!o)&&(("body"!==p(n)||h(v))&&(y=(i=n)!==t(i)&&r(i)?{scrollLeft:(a=i).scrollLeft,scrollTop:a.scrollTop}:c(i)),r(n)?((b=f(n,!0)).x+=n.clientLeft,b.y+=n.clientTop):v&&(b.x=l(v))),{x:g.left+y.scrollLeft-b.x,y:g.top+y.scrollTop-b.y,width:g.width,height:g.height}}function v(e){var t=f(e),n=e.offsetWidth,r=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-r)<=1&&(r=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:r}}function g(e){return"html"===p(e)?e:e.assignedSlot||e.parentNode||(o(e)?e.host:null)||u(e)}function y(e){return["html","body","#document"].indexOf(p(e))>=0?e.ownerDocument.body:r(e)&&h(e)?e:y(g(e))}function b(e,n){var r;void 0===n&&(n=[]);var o=y(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),a=t(o),s=i?[a].concat(a.visualViewport||[],h(o)?o:[]):o,f=n.concat(s);return i?f:f.concat(b(g(s)))}function x(e){return["table","td","th"].indexOf(p(e))>=0}function w(e){return r(e)&&"fixed"!==d(e).position?e.offsetParent:null}function O(e){for(var n=t(e),i=w(e);i&&x(i)&&"static"===d(i).position;)i=w(i);return i&&("html"===p(i)||"body"===p(i)&&"static"===d(i).position)?n:i||function(e){var t=-1!==navigator.userAgent.toLowerCase().indexOf("firefox");if(-1!==navigator.userAgent.indexOf("Trident")&&r(e)&&"fixed"===d(e).position)return null;var n=g(e);for(o(n)&&(n=n.host);r(n)&&["html","body"].indexOf(p(n))<0;){var i=d(n);if("none"!==i.transform||"none"!==i.perspective||"paint"===i.contain||-1!==["transform","perspective"].indexOf(i.willChange)||t&&"filter"===i.willChange||t&&i.filter&&"none"!==i.filter)return n;n=n.parentNode}return null}(e)||n}var j="top",E="bottom",D="right",A="left",L="auto",P=[j,E,D,A],M="start",k="end",W="viewport",B="popper",H=P.reduce((function(e,t){return e.concat([t+"-"+M,t+"-"+k])}),[]),T=[].concat(P,[L]).reduce((function(e,t){return e.concat([t,t+"-"+M,t+"-"+k])}),[]),R=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function S(e){var t=new Map,n=new Set,r=[];function o(e){n.add(e.name),[].concat(e.requires||[],e.requiresIfExists||[]).forEach((function(e){if(!n.has(e)){var r=t.get(e);r&&o(r)}})),r.push(e)}return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||o(e)})),r}function C(e){return e.split("-")[0]}function q(e,t){var n=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(n&&o(n)){var r=t;do{if(r&&e.isSameNode(r))return!0;r=r.parentNode||r.host}while(r)}return!1}function V(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function N(e,r){return r===W?V(function(e){var n=t(e),r=u(e),o=n.visualViewport,i=r.clientWidth,a=r.clientHeight,s=0,f=0;return o&&(i=o.width,a=o.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(s=o.offsetLeft,f=o.offsetTop)),{width:i,height:a,x:s+l(e),y:f}}(e)):n(r)?function(e){var t=f(e);return t.top=t.top+e.clientTop,t.left=t.left+e.clientLeft,t.bottom=t.top+e.clientHeight,t.right=t.left+e.clientWidth,t.width=e.clientWidth,t.height=e.clientHeight,t.x=t.left,t.y=t.top,t}(r):V(function(e){var t,n=u(e),r=c(e),o=null==(t=e.ownerDocument)?void 0:t.body,a=i(n.scrollWidth,n.clientWidth,o?o.scrollWidth:0,o?o.clientWidth:0),s=i(n.scrollHeight,n.clientHeight,o?o.scrollHeight:0,o?o.clientHeight:0),f=-r.scrollLeft+l(e),p=-r.scrollTop;return"rtl"===d(o||n).direction&&(f+=i(n.clientWidth,o?o.clientWidth:0)-a),{width:a,height:s,x:f,y:p}}(u(e)))}function I(e,t,o){var s="clippingParents"===t?function(e){var t=b(g(e)),o=["absolute","fixed"].indexOf(d(e).position)>=0&&r(e)?O(e):e;return n(o)?t.filter((function(e){return n(e)&&q(e,o)&&"body"!==p(e)})):[]}(e):[].concat(t),f=[].concat(s,[o]),c=f[0],u=f.reduce((function(t,n){var r=N(e,n);return t.top=i(r.top,t.top),t.right=a(r.right,t.right),t.bottom=a(r.bottom,t.bottom),t.left=i(r.left,t.left),t}),N(e,c));return u.width=u.right-u.left,u.height=u.bottom-u.top,u.x=u.left,u.y=u.top,u}function _(e){return e.split("-")[1]}function F(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function U(e){var t,n=e.reference,r=e.element,o=e.placement,i=o?C(o):null,a=o?_(o):null,s=n.x+n.width/2-r.width/2,f=n.y+n.height/2-r.height/2;switch(i){case j:t={x:s,y:n.y-r.height};break;case E:t={x:s,y:n.y+n.height};break;case D:t={x:n.x+n.width,y:f};break;case A:t={x:n.x-r.width,y:f};break;default:t={x:n.x,y:n.y}}var c=i?F(i):null;if(null!=c){var p="y"===c?"height":"width";switch(a){case M:t[c]=t[c]-(n[p]/2-r[p]/2);break;case k:t[c]=t[c]+(n[p]/2-r[p]/2)}}return t}function z(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},e)}function X(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function Y(e,t){void 0===t&&(t={});var r=t,o=r.placement,i=void 0===o?e.placement:o,a=r.boundary,s=void 0===a?"clippingParents":a,c=r.rootBoundary,p=void 0===c?W:c,l=r.elementContext,d=void 0===l?B:l,h=r.altBoundary,m=void 0!==h&&h,v=r.padding,g=void 0===v?0:v,y=z("number"!=typeof g?g:X(g,P)),b=d===B?"reference":B,x=e.rects.popper,w=e.elements[m?b:d],O=I(n(w)?w:w.contextElement||u(e.elements.popper),s,p),A=f(e.elements.reference),L=U({reference:A,element:x,strategy:"absolute",placement:i}),M=V(Object.assign({},x,L)),k=d===B?M:A,H={top:O.top-k.top+y.top,bottom:k.bottom-O.bottom+y.bottom,left:O.left-k.left+y.left,right:k.right-O.right+y.right},T=e.modifiersData.offset;if(d===B&&T){var R=T[i];Object.keys(H).forEach((function(e){var t=[D,E].indexOf(e)>=0?1:-1,n=[j,E].indexOf(e)>=0?"y":"x";H[e]+=R[n]*t}))}return H}var G={placement:"bottom",modifiers:[],strategy:"absolute"};function J(){for(var e=arguments.length,t=new Array(e),n=0;n=0?-1:1,i="function"==typeof n?n(Object.assign({},t,{placement:e})):n,a=i[0],s=i[1];return a=a||0,s=(s||0)*o,[A,D].indexOf(r)>=0?{x:s,y:a}:{x:a,y:s}}(n,t.rects,i),e}),{}),s=a[t.placement],f=s.x,c=s.y;null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=f,t.modifiersData.popperOffsets.y+=c),t.modifiersData[r]=a}},ie={left:"right",right:"left",bottom:"top",top:"bottom"};function ae(e){return e.replace(/left|right|bottom|top/g,(function(e){return ie[e]}))}var se={start:"end",end:"start"};function fe(e){return e.replace(/start|end/g,(function(e){return se[e]}))}function ce(e,t){void 0===t&&(t={});var n=t,r=n.placement,o=n.boundary,i=n.rootBoundary,a=n.padding,s=n.flipVariations,f=n.allowedAutoPlacements,c=void 0===f?T:f,p=_(r),u=p?s?H:H.filter((function(e){return _(e)===p})):P,l=u.filter((function(e){return c.indexOf(e)>=0}));0===l.length&&(l=u);var d=l.reduce((function(t,n){return t[n]=Y(e,{placement:n,boundary:o,rootBoundary:i,padding:a})[C(n)],t}),{});return Object.keys(d).sort((function(e,t){return d[e]-d[t]}))}var pe={name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name;if(!t.modifiersData[r]._skip){for(var o=n.mainAxis,i=void 0===o||o,a=n.altAxis,s=void 0===a||a,f=n.fallbackPlacements,c=n.padding,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.flipVariations,h=void 0===d||d,m=n.allowedAutoPlacements,v=t.options.placement,g=C(v),y=f||(g===v||!h?[ae(v)]:function(e){if(C(e)===L)return[];var t=ae(e);return[fe(e),t,fe(t)]}(v)),b=[v].concat(y).reduce((function(e,n){return e.concat(C(n)===L?ce(t,{placement:n,boundary:p,rootBoundary:u,padding:c,flipVariations:h,allowedAutoPlacements:m}):n)}),[]),x=t.rects.reference,w=t.rects.popper,O=new Map,P=!0,k=b[0],W=0;W=0,S=R?"width":"height",q=Y(t,{placement:B,boundary:p,rootBoundary:u,altBoundary:l,padding:c}),V=R?T?D:A:T?E:j;x[S]>w[S]&&(V=ae(V));var N=ae(V),I=[];if(i&&I.push(q[H]<=0),s&&I.push(q[V]<=0,q[N]<=0),I.every((function(e){return e}))){k=B,P=!1;break}O.set(B,I)}if(P)for(var F=function(e){var t=b.find((function(t){var n=O.get(t);if(n)return n.slice(0,e).every((function(e){return e}))}));if(t)return k=t,"break"},U=h?3:1;U>0;U--){if("break"===F(U))break}t.placement!==k&&(t.modifiersData[r]._skip=!0,t.placement=k,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function ue(e,t,n){return i(e,a(t,n))}var le={name:"preventOverflow",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name,o=n.mainAxis,s=void 0===o||o,f=n.altAxis,c=void 0!==f&&f,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.padding,h=n.tether,m=void 0===h||h,g=n.tetherOffset,y=void 0===g?0:g,b=Y(t,{boundary:p,rootBoundary:u,padding:d,altBoundary:l}),x=C(t.placement),w=_(t.placement),L=!w,P=F(x),k="x"===P?"y":"x",W=t.modifiersData.popperOffsets,B=t.rects.reference,H=t.rects.popper,T="function"==typeof y?y(Object.assign({},t.rects,{placement:t.placement})):y,R="number"==typeof T?{mainAxis:T,altAxis:T}:Object.assign({mainAxis:0,altAxis:0},T),S=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,q={x:0,y:0};if(W){if(s){var V,N="y"===P?j:A,I="y"===P?E:D,U="y"===P?"height":"width",z=W[P],X=z+b[N],G=z-b[I],J=m?-H[U]/2:0,K=w===M?B[U]:H[U],Q=w===M?-H[U]:-B[U],Z=t.elements.arrow,$=m&&Z?v(Z):{width:0,height:0},ee=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},te=ee[N],ne=ee[I],re=ue(0,B[U],$[U]),oe=L?B[U]/2-J-re-te-R.mainAxis:K-re-te-R.mainAxis,ie=L?-B[U]/2+J+re+ne+R.mainAxis:Q+re+ne+R.mainAxis,ae=t.elements.arrow&&O(t.elements.arrow),se=ae?"y"===P?ae.clientTop||0:ae.clientLeft||0:0,fe=null!=(V=null==S?void 0:S[P])?V:0,ce=z+ie-fe,pe=ue(m?a(X,z+oe-fe-se):X,z,m?i(G,ce):G);W[P]=pe,q[P]=pe-z}if(c){var le,de="x"===P?j:A,he="x"===P?E:D,me=W[k],ve="y"===k?"height":"width",ge=me+b[de],ye=me-b[he],be=-1!==[j,A].indexOf(x),xe=null!=(le=null==S?void 0:S[k])?le:0,we=be?ge:me-B[ve]-H[ve]-xe+R.altAxis,Oe=be?me+B[ve]+H[ve]-xe-R.altAxis:ye,je=m&&be?function(e,t,n){var r=ue(e,t,n);return r>n?n:r}(we,me,Oe):ue(m?we:ge,me,m?Oe:ye);W[k]=je,q[k]=je-me}t.modifiersData[r]=q}},requiresIfExists:["offset"]};var de={name:"arrow",enabled:!0,phase:"main",fn:function(e){var t,n=e.state,r=e.name,o=e.options,i=n.elements.arrow,a=n.modifiersData.popperOffsets,s=C(n.placement),f=F(s),c=[A,D].indexOf(s)>=0?"height":"width";if(i&&a){var p=function(e,t){return z("number"!=typeof(e="function"==typeof e?e(Object.assign({},t.rects,{placement:t.placement})):e)?e:X(e,P))}(o.padding,n),u=v(i),l="y"===f?j:A,d="y"===f?E:D,h=n.rects.reference[c]+n.rects.reference[f]-a[f]-n.rects.popper[c],m=a[f]-n.rects.reference[f],g=O(i),y=g?"y"===f?g.clientHeight||0:g.clientWidth||0:0,b=h/2-m/2,x=p[l],w=y-u[c]-p[d],L=y/2-u[c]/2+b,M=ue(x,L,w),k=f;n.modifiersData[r]=((t={})[k]=M,t.centerOffset=M-L,t)}},effect:function(e){var t=e.state,n=e.options.element,r=void 0===n?"[data-popper-arrow]":n;null!=r&&("string"!=typeof r||(r=t.elements.popper.querySelector(r)))&&q(t.elements.popper,r)&&(t.elements.arrow=r)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function he(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function me(e){return[j,D,E,A].some((function(t){return e[t]>=0}))}var ve={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(e){var t=e.state,n=e.name,r=t.rects.reference,o=t.rects.popper,i=t.modifiersData.preventOverflow,a=Y(t,{elementContext:"reference"}),s=Y(t,{altBoundary:!0}),f=he(a,r),c=he(s,o,i),p=me(f),u=me(c);t.modifiersData[n]={referenceClippingOffsets:f,popperEscapeOffsets:c,isReferenceHidden:p,hasPopperEscaped:u},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":p,"data-popper-escaped":u})}},ge=K({defaultModifiers:[Z,$,ne,re]}),ye=[Z,$,ne,re,oe,pe,le,de,ve],be=K({defaultModifiers:ye});e.applyStyles=re,e.arrow=de,e.computeStyles=ne,e.createPopper=be,e.createPopperLite=ge,e.defaultModifiers=ye,e.detectOverflow=Y,e.eventListeners=Z,e.flip=pe,e.hide=ve,e.offset=oe,e.popperGenerator=K,e.popperOffsets=$,e.preventOverflow=le,Object.defineProperty(e,"__esModule",{value:!0})})); - diff --git a/docs/site_libs/quarto-html/quarto-syntax-highlighting.css b/docs/site_libs/quarto-html/quarto-syntax-highlighting.css deleted file mode 100644 index 36cb328..0000000 --- a/docs/site_libs/quarto-html/quarto-syntax-highlighting.css +++ /dev/null @@ -1,171 +0,0 @@ -/* quarto syntax highlight colors */ -:root { - --quarto-hl-ot-color: #003B4F; - --quarto-hl-at-color: #657422; - --quarto-hl-ss-color: #20794D; - --quarto-hl-an-color: #5E5E5E; - --quarto-hl-fu-color: #4758AB; - --quarto-hl-st-color: #20794D; - --quarto-hl-cf-color: #003B4F; - --quarto-hl-op-color: #5E5E5E; - --quarto-hl-er-color: #AD0000; - --quarto-hl-bn-color: #AD0000; - --quarto-hl-al-color: #AD0000; - --quarto-hl-va-color: #111111; - --quarto-hl-bu-color: inherit; - --quarto-hl-ex-color: inherit; - --quarto-hl-pp-color: #AD0000; - --quarto-hl-in-color: #5E5E5E; - --quarto-hl-vs-color: #20794D; - --quarto-hl-wa-color: #5E5E5E; - --quarto-hl-do-color: #5E5E5E; - --quarto-hl-im-color: #00769E; - --quarto-hl-ch-color: #20794D; - --quarto-hl-dt-color: #AD0000; - --quarto-hl-fl-color: #AD0000; - --quarto-hl-co-color: #5E5E5E; - --quarto-hl-cv-color: #5E5E5E; - --quarto-hl-cn-color: #8f5902; - --quarto-hl-sc-color: #5E5E5E; - --quarto-hl-dv-color: #AD0000; - --quarto-hl-kw-color: #003B4F; -} - -/* other quarto variables */ -:root { - --quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; -} - -pre > code.sourceCode > span { - color: #003B4F; -} - -code span { - color: #003B4F; -} - -code.sourceCode > span { - color: #003B4F; -} - -div.sourceCode, -div.sourceCode pre.sourceCode { - color: #003B4F; -} - -code span.ot { - color: #003B4F; -} - -code span.at { - color: #657422; -} - -code span.ss { - color: #20794D; -} - -code span.an { - color: #5E5E5E; -} - -code span.fu { - color: #4758AB; -} - -code span.st { - color: #20794D; -} - -code span.cf { - color: #003B4F; -} - -code span.op { - color: #5E5E5E; -} - -code span.er { - color: #AD0000; -} - -code span.bn { - color: #AD0000; -} - -code span.al { - color: #AD0000; -} - -code span.va { - color: #111111; -} - -code span.pp { - color: #AD0000; -} - -code span.in { - color: #5E5E5E; -} - -code span.vs { - color: #20794D; -} - -code span.wa { - color: #5E5E5E; - font-style: italic; -} - -code span.do { - color: #5E5E5E; - font-style: italic; -} - -code span.im { - color: #00769E; -} - -code span.ch { - color: #20794D; -} - -code span.dt { - color: #AD0000; -} - -code span.fl { - color: #AD0000; -} - -code span.co { - color: #5E5E5E; -} - -code span.cv { - color: #5E5E5E; - font-style: italic; -} - -code span.cn { - color: #8f5902; -} - -code span.sc { - color: #5E5E5E; -} - -code span.dv { - color: #AD0000; -} - -code span.kw { - color: #003B4F; -} - -.prevent-inlining { - content: " { - const sibling = el.previousElementSibling; - if (sibling && sibling.tagName === "A") { - return sibling.classList.contains("active"); - } else { - return false; - } - }; - - // fire slideEnter for bootstrap tab activations (for htmlwidget resize behavior) - function fireSlideEnter(e) { - const event = window.document.createEvent("Event"); - event.initEvent("slideenter", true, true); - window.document.dispatchEvent(event); - } - const tabs = window.document.querySelectorAll('a[data-bs-toggle="tab"]'); - tabs.forEach((tab) => { - tab.addEventListener("shown.bs.tab", fireSlideEnter); - }); - - // Track scrolling and mark TOC links as active - // get table of contents and sidebar (bail if we don't have at least one) - const tocLinks = tocEl - ? [...tocEl.querySelectorAll("a[data-scroll-target]")] - : []; - const makeActive = (link) => tocLinks[link].classList.add("active"); - const removeActive = (link) => tocLinks[link].classList.remove("active"); - const removeAllActive = () => - [...Array(tocLinks.length).keys()].forEach((link) => removeActive(link)); - - // activate the anchor for a section associated with this TOC entry - tocLinks.forEach((link) => { - link.addEventListener("click", () => { - if (link.href.indexOf("#") !== -1) { - const anchor = link.href.split("#")[1]; - const heading = window.document.querySelector( - `[data-anchor-id=${anchor}]` - ); - if (heading) { - // Add the class - heading.classList.add("reveal-anchorjs-link"); - - // function to show the anchor - const handleMouseout = () => { - heading.classList.remove("reveal-anchorjs-link"); - heading.removeEventListener("mouseout", handleMouseout); - }; - - // add a function to clear the anchor when the user mouses out of it - heading.addEventListener("mouseout", handleMouseout); - } - } - }); - }); - - const sections = tocLinks.map((link) => { - const target = link.getAttribute("data-scroll-target"); - if (target.startsWith("#")) { - return window.document.getElementById(decodeURI(`${target.slice(1)}`)); - } else { - return window.document.querySelector(decodeURI(`${target}`)); - } - }); - - const sectionMargin = 200; - let currentActive = 0; - // track whether we've initialized state the first time - let init = false; - - const updateActiveLink = () => { - // The index from bottom to top (e.g. reversed list) - let sectionIndex = -1; - if ( - window.innerHeight + window.pageYOffset >= - window.document.body.offsetHeight - ) { - sectionIndex = 0; - } else { - sectionIndex = [...sections].reverse().findIndex((section) => { - if (section) { - return window.pageYOffset >= section.offsetTop - sectionMargin; - } else { - return false; - } - }); - } - if (sectionIndex > -1) { - const current = sections.length - sectionIndex - 1; - if (current !== currentActive) { - removeAllActive(); - currentActive = current; - makeActive(current); - if (init) { - window.dispatchEvent(sectionChanged); - } - init = true; - } - } - }; - - const inHiddenRegion = (top, bottom, hiddenRegions) => { - for (const region of hiddenRegions) { - if (top <= region.bottom && bottom >= region.top) { - return true; - } - } - return false; - }; - - const categorySelector = "header.quarto-title-block .quarto-category"; - const activateCategories = (href) => { - // Find any categories - // Surround them with a link pointing back to: - // #category=Authoring - try { - const categoryEls = window.document.querySelectorAll(categorySelector); - for (const categoryEl of categoryEls) { - const categoryText = categoryEl.textContent; - if (categoryText) { - const link = `${href}#category=${encodeURIComponent(categoryText)}`; - const linkEl = window.document.createElement("a"); - linkEl.setAttribute("href", link); - for (const child of categoryEl.childNodes) { - linkEl.append(child); - } - categoryEl.appendChild(linkEl); - } - } - } catch { - // Ignore errors - } - }; - function hasTitleCategories() { - return window.document.querySelector(categorySelector) !== null; - } - - function offsetRelativeUrl(url) { - const offset = getMeta("quarto:offset"); - return offset ? offset + url : url; - } - - function offsetAbsoluteUrl(url) { - const offset = getMeta("quarto:offset"); - const baseUrl = new URL(offset, window.location); - - const projRelativeUrl = url.replace(baseUrl, ""); - if (projRelativeUrl.startsWith("/")) { - return projRelativeUrl; - } else { - return "/" + projRelativeUrl; - } - } - - // read a meta tag value - function getMeta(metaName) { - const metas = window.document.getElementsByTagName("meta"); - for (let i = 0; i < metas.length; i++) { - if (metas[i].getAttribute("name") === metaName) { - return metas[i].getAttribute("content"); - } - } - return ""; - } - - async function findAndActivateCategories() { - const currentPagePath = offsetAbsoluteUrl(window.location.href); - const response = await fetch(offsetRelativeUrl("listings.json")); - if (response.status == 200) { - return response.json().then(function (listingPaths) { - const listingHrefs = []; - for (const listingPath of listingPaths) { - const pathWithoutLeadingSlash = listingPath.listing.substring(1); - for (const item of listingPath.items) { - if ( - item === currentPagePath || - item === currentPagePath + "index.html" - ) { - // Resolve this path against the offset to be sure - // we already are using the correct path to the listing - // (this adjusts the listing urls to be rooted against - // whatever root the page is actually running against) - const relative = offsetRelativeUrl(pathWithoutLeadingSlash); - const baseUrl = window.location; - const resolvedPath = new URL(relative, baseUrl); - listingHrefs.push(resolvedPath.pathname); - break; - } - } - } - - // Look up the tree for a nearby linting and use that if we find one - const nearestListing = findNearestParentListing( - offsetAbsoluteUrl(window.location.pathname), - listingHrefs - ); - if (nearestListing) { - activateCategories(nearestListing); - } else { - // See if the referrer is a listing page for this item - const referredRelativePath = offsetAbsoluteUrl(document.referrer); - const referrerListing = listingHrefs.find((listingHref) => { - const isListingReferrer = - listingHref === referredRelativePath || - listingHref === referredRelativePath + "index.html"; - return isListingReferrer; - }); - - if (referrerListing) { - // Try to use the referrer if possible - activateCategories(referrerListing); - } else if (listingHrefs.length > 0) { - // Otherwise, just fall back to the first listing - activateCategories(listingHrefs[0]); - } - } - }); - } - } - if (hasTitleCategories()) { - findAndActivateCategories(); - } - - const findNearestParentListing = (href, listingHrefs) => { - if (!href || !listingHrefs) { - return undefined; - } - // Look up the tree for a nearby linting and use that if we find one - const relativeParts = href.substring(1).split("/"); - while (relativeParts.length > 0) { - const path = relativeParts.join("/"); - for (const listingHref of listingHrefs) { - if (listingHref.startsWith(path)) { - return listingHref; - } - } - relativeParts.pop(); - } - - return undefined; - }; - - const manageSidebarVisiblity = (el, placeholderDescriptor) => { - let isVisible = true; - - return (hiddenRegions) => { - if (el === null) { - return; - } - - // Find the last element of the TOC - const lastChildEl = el.lastElementChild; - - if (lastChildEl) { - // Find the top and bottom o the element that is being managed - const elTop = el.offsetTop; - const elBottom = - elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight; - - // Converts the sidebar to a menu - const convertToMenu = () => { - for (const child of el.children) { - child.style.opacity = 0; - child.style.display = "none"; - } - - const toggleContainer = window.document.createElement("div"); - toggleContainer.style.width = "100%"; - toggleContainer.classList.add("zindex-over-content"); - toggleContainer.classList.add("quarto-sidebar-toggle"); - toggleContainer.classList.add("headroom-target"); // Marks this to be managed by headeroom - toggleContainer.id = placeholderDescriptor.id; - toggleContainer.style.position = "fixed"; - - const toggleIcon = window.document.createElement("i"); - toggleIcon.classList.add("quarto-sidebar-toggle-icon"); - toggleIcon.classList.add("bi"); - toggleIcon.classList.add("bi-caret-down-fill"); - - const toggleTitle = window.document.createElement("div"); - const titleEl = window.document.body.querySelector( - placeholderDescriptor.titleSelector - ); - if (titleEl) { - toggleTitle.append(titleEl.innerText, toggleIcon); - } - toggleTitle.classList.add("zindex-over-content"); - toggleTitle.classList.add("quarto-sidebar-toggle-title"); - toggleContainer.append(toggleTitle); - - const toggleContents = window.document.createElement("div"); - toggleContents.classList = el.classList; - toggleContents.classList.add("zindex-over-content"); - toggleContents.classList.add("quarto-sidebar-toggle-contents"); - for (const child of el.children) { - if (child.id === "toc-title") { - continue; - } - - const clone = child.cloneNode(true); - clone.style.opacity = 1; - clone.style.display = null; - toggleContents.append(clone); - } - toggleContents.style.height = "0px"; - toggleContainer.append(toggleContents); - el.parentElement.prepend(toggleContainer); - - // Process clicks - let tocShowing = false; - // Allow the caller to control whether this is dismissed - // when it is clicked (e.g. sidebar navigation supports - // opening and closing the nav tree, so don't dismiss on click) - const clickEl = placeholderDescriptor.dismissOnClick - ? toggleContainer - : toggleTitle; - - const closeToggle = () => { - if (tocShowing) { - toggleContainer.classList.remove("expanded"); - toggleContents.style.height = "0px"; - tocShowing = false; - } - }; - - const positionToggle = () => { - // position the element (top left of parent, same width as parent) - const elRect = el.getBoundingClientRect(); - toggleContainer.style.left = `${elRect.left}px`; - toggleContainer.style.top = `${elRect.top}px`; - toggleContainer.style.width = `${elRect.width}px`; - }; - - // Get rid of any expanded toggle if the user scrolls - window.document.addEventListener( - "scroll", - throttle(() => { - closeToggle(); - }, 50) - ); - - // Handle positioning of the toggle - window.addEventListener( - "resize", - throttle(() => { - positionToggle(); - }, 50) - ); - positionToggle(); - - // Process the click - clickEl.onclick = () => { - if (!tocShowing) { - toggleContainer.classList.add("expanded"); - toggleContents.style.height = null; - tocShowing = true; - } else { - closeToggle(); - } - }; - }; - - // Converts a sidebar from a menu back to a sidebar - const convertToSidebar = () => { - for (const child of el.children) { - child.style.opacity = 1; - clone.style.display = null; - } - - const placeholderEl = window.document.getElementById( - placeholderDescriptor.id - ); - if (placeholderEl) { - placeholderEl.remove(); - } - - el.classList.remove("rollup"); - }; - - if (isReaderMode()) { - convertToMenu(); - isVisible = false; - } else { - if (!isVisible) { - // If the element is current not visible reveal if there are - // no conflicts with overlay regions - if (!inHiddenRegion(elTop, elBottom, hiddenRegions)) { - convertToSidebar(); - isVisible = true; - } - } else { - // If the element is visible, hide it if it conflicts with overlay regions - // and insert a placeholder toggle (or if we're in reader mode) - if (inHiddenRegion(elTop, elBottom, hiddenRegions)) { - convertToMenu(); - isVisible = false; - } - } - } - } - }; - }; - - // Find any conflicting margin elements and add margins to the - // top to prevent overlap - const marginChildren = window.document.querySelectorAll( - ".column-margin.column-container > * " - ); - let lastBottom = 0; - for (const marginChild of marginChildren) { - const top = marginChild.getBoundingClientRect().top; - if (top < lastBottom) { - const margin = lastBottom - top; - marginChild.style.marginTop = `${margin}px`; - } - const styles = window.getComputedStyle(marginChild); - const marginTop = parseFloat(styles["marginTop"]); - - lastBottom = top + marginChild.getBoundingClientRect().height + marginTop; - } - - // Manage the visibility of the toc and the sidebar - const marginScrollVisibility = manageSidebarVisiblity(marginSidebarEl, { - id: "quarto-toc-toggle", - titleSelector: "#toc-title", - dismissOnClick: true, - }); - const sidebarScrollVisiblity = manageSidebarVisiblity(sidebarEl, { - id: "quarto-sidebarnav-toggle", - titleSelector: ".title", - dismissOnClick: false, - }); - let tocLeftScrollVisibility; - if (leftTocEl) { - tocLeftScrollVisibility = manageSidebarVisiblity(leftTocEl, { - id: "quarto-lefttoc-toggle", - titleSelector: "#toc-title", - dismissOnClick: true, - }); - } - - // Find the first element that uses formatting in special columns - const conflictingEls = window.document.body.querySelectorAll( - '[class^="column-"], [class*=" column-"], aside, [class*="margin-caption"], [class*=" margin-caption"], [class*="margin-ref"], [class*=" margin-ref"]' - ); - - // Filter all the possibly conflicting elements into ones - // the do conflict on the left or ride side - const arrConflictingEls = Array.from(conflictingEls); - const leftSideConflictEls = arrConflictingEls.filter((el) => { - if (el.tagName === "ASIDE") { - return false; - } - return Array.from(el.classList).find((className) => { - return ( - className !== "column-body" && - className.startsWith("column-") && - !className.endsWith("right") && - !className.endsWith("container") && - className !== "column-margin" - ); - }); - }); - const rightSideConflictEls = arrConflictingEls.filter((el) => { - if (el.tagName === "ASIDE") { - return true; - } - - const hasMarginCaption = Array.from(el.classList).find((className) => { - return className == "margin-caption"; - }); - if (hasMarginCaption) { - return true; - } - - return Array.from(el.classList).find((className) => { - return ( - className !== "column-body" && - !className.endsWith("container") && - className.startsWith("column-") && - !className.endsWith("left") - ); - }); - }); - - const kOverlapPaddingSize = 10; - function toRegions(els) { - return els.map((el) => { - const top = - el.getBoundingClientRect().top + - document.documentElement.scrollTop - - kOverlapPaddingSize; - return { - top, - bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize, - }; - }); - } - - const hideOverlappedSidebars = () => { - marginScrollVisibility(toRegions(rightSideConflictEls)); - sidebarScrollVisiblity(toRegions(leftSideConflictEls)); - if (tocLeftScrollVisibility) { - tocLeftScrollVisibility(toRegions(leftSideConflictEls)); - } - }; - - window.quartoToggleReader = () => { - // Applies a slow class (or removes it) - // to update the transition speed - const slowTransition = (slow) => { - const manageTransition = (id, slow) => { - const el = document.getElementById(id); - if (el) { - if (slow) { - el.classList.add("slow"); - } else { - el.classList.remove("slow"); - } - } - }; - - manageTransition("TOC", slow); - manageTransition("quarto-sidebar", slow); - }; - - const readerMode = !isReaderMode(); - setReaderModeValue(readerMode); - - // If we're entering reader mode, slow the transition - if (readerMode) { - slowTransition(readerMode); - } - highlightReaderToggle(readerMode); - hideOverlappedSidebars(); - - // If we're exiting reader mode, restore the non-slow transition - if (!readerMode) { - slowTransition(!readerMode); - } - }; - - const highlightReaderToggle = (readerMode) => { - const els = document.querySelectorAll(".quarto-reader-toggle"); - if (els) { - els.forEach((el) => { - if (readerMode) { - el.classList.add("reader"); - } else { - el.classList.remove("reader"); - } - }); - } - }; - - const setReaderModeValue = (val) => { - if (window.location.protocol !== "file:") { - window.localStorage.setItem("quarto-reader-mode", val); - } else { - localReaderMode = val; - } - }; - - const isReaderMode = () => { - if (window.location.protocol !== "file:") { - return window.localStorage.getItem("quarto-reader-mode") === "true"; - } else { - return localReaderMode; - } - }; - let localReaderMode = null; - - // Walk the TOC and collapse/expand nodes - // Nodes are expanded if: - // - they are top level - // - they have children that are 'active' links - // - they are directly below an link that is 'active' - const walk = (el, depth) => { - // Tick depth when we enter a UL - if (el.tagName === "UL") { - depth = depth + 1; - } - - // It this is active link - let isActiveNode = false; - if (el.tagName === "A" && el.classList.contains("active")) { - isActiveNode = true; - } - - // See if there is an active child to this element - let hasActiveChild = false; - for (child of el.children) { - hasActiveChild = walk(child, depth) || hasActiveChild; - } - - // Process the collapse state if this is an UL - if (el.tagName === "UL") { - if (depth === 1 || hasActiveChild || prevSiblingIsActiveLink(el)) { - el.classList.remove("collapse"); - } else { - el.classList.add("collapse"); - } - - // untick depth when we leave a UL - depth = depth - 1; - } - return hasActiveChild || isActiveNode; - }; - - // walk the TOC and expand / collapse any items that should be shown - - if (tocEl) { - walk(tocEl, 0); - updateActiveLink(); - } - - // Throttle the scroll event and walk peridiocally - window.document.addEventListener( - "scroll", - throttle(() => { - if (tocEl) { - updateActiveLink(); - walk(tocEl, 0); - } - if (!isReaderMode()) { - hideOverlappedSidebars(); - } - }, 5) - ); - window.addEventListener( - "resize", - throttle(() => { - if (!isReaderMode()) { - hideOverlappedSidebars(); - } - }, 10) - ); - hideOverlappedSidebars(); - highlightReaderToggle(isReaderMode()); -}); - -function throttle(func, wait) { - let waiting = false; - return function () { - if (!waiting) { - func.apply(this, arguments); - waiting = true; - setTimeout(function () { - waiting = false; - }, wait); - } - }; -} diff --git a/docs/site_libs/quarto-html/tippy.css b/docs/site_libs/quarto-html/tippy.css deleted file mode 100644 index e6ae635..0000000 --- a/docs/site_libs/quarto-html/tippy.css +++ /dev/null @@ -1 +0,0 @@ -.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1} \ No newline at end of file diff --git a/docs/site_libs/quarto-html/tippy.umd.min.js b/docs/site_libs/quarto-html/tippy.umd.min.js deleted file mode 100644 index ca292be..0000000 --- a/docs/site_libs/quarto-html/tippy.umd.min.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@popperjs/core")):"function"==typeof define&&define.amd?define(["@popperjs/core"],t):(e=e||self).tippy=t(e.Popper)}(this,(function(e){"use strict";var t={passive:!0,capture:!0},n=function(){return document.body};function r(e,t,n){if(Array.isArray(e)){var r=e[t];return null==r?Array.isArray(n)?n[t]:n:r}return e}function o(e,t){var n={}.toString.call(e);return 0===n.indexOf("[object")&&n.indexOf(t+"]")>-1}function i(e,t){return"function"==typeof e?e.apply(void 0,t):e}function a(e,t){return 0===t?e:function(r){clearTimeout(n),n=setTimeout((function(){e(r)}),t)};var n}function s(e,t){var n=Object.assign({},e);return t.forEach((function(e){delete n[e]})),n}function u(e){return[].concat(e)}function c(e,t){-1===e.indexOf(t)&&e.push(t)}function p(e){return e.split("-")[0]}function f(e){return[].slice.call(e)}function l(e){return Object.keys(e).reduce((function(t,n){return void 0!==e[n]&&(t[n]=e[n]),t}),{})}function d(){return document.createElement("div")}function v(e){return["Element","Fragment"].some((function(t){return o(e,t)}))}function m(e){return o(e,"MouseEvent")}function g(e){return!(!e||!e._tippy||e._tippy.reference!==e)}function h(e){return v(e)?[e]:function(e){return o(e,"NodeList")}(e)?f(e):Array.isArray(e)?e:f(document.querySelectorAll(e))}function b(e,t){e.forEach((function(e){e&&(e.style.transitionDuration=t+"ms")}))}function y(e,t){e.forEach((function(e){e&&e.setAttribute("data-state",t)}))}function w(e){var t,n=u(e)[0];return null!=n&&null!=(t=n.ownerDocument)&&t.body?n.ownerDocument:document}function E(e,t,n){var r=t+"EventListener";["transitionend","webkitTransitionEnd"].forEach((function(t){e[r](t,n)}))}function O(e,t){for(var n=t;n;){var r;if(e.contains(n))return!0;n=null==n.getRootNode||null==(r=n.getRootNode())?void 0:r.host}return!1}var x={isTouch:!1},C=0;function T(){x.isTouch||(x.isTouch=!0,window.performance&&document.addEventListener("mousemove",A))}function A(){var e=performance.now();e-C<20&&(x.isTouch=!1,document.removeEventListener("mousemove",A)),C=e}function L(){var e=document.activeElement;if(g(e)){var t=e._tippy;e.blur&&!t.state.isVisible&&e.blur()}}var D=!!("undefined"!=typeof window&&"undefined"!=typeof document)&&!!window.msCrypto,R=Object.assign({appendTo:n,aria:{content:"auto",expanded:"auto"},delay:0,duration:[300,250],getReferenceClientRect:null,hideOnClick:!0,ignoreAttributes:!1,interactive:!1,interactiveBorder:2,interactiveDebounce:0,moveTransition:"",offset:[0,10],onAfterUpdate:function(){},onBeforeUpdate:function(){},onCreate:function(){},onDestroy:function(){},onHidden:function(){},onHide:function(){},onMount:function(){},onShow:function(){},onShown:function(){},onTrigger:function(){},onUntrigger:function(){},onClickOutside:function(){},placement:"top",plugins:[],popperOptions:{},render:null,showOnCreate:!1,touch:!0,trigger:"mouseenter focus",triggerTarget:null},{animateFill:!1,followCursor:!1,inlinePositioning:!1,sticky:!1},{allowHTML:!1,animation:"fade",arrow:!0,content:"",inertia:!1,maxWidth:350,role:"tooltip",theme:"",zIndex:9999}),k=Object.keys(R);function P(e){var t=(e.plugins||[]).reduce((function(t,n){var r,o=n.name,i=n.defaultValue;o&&(t[o]=void 0!==e[o]?e[o]:null!=(r=R[o])?r:i);return t}),{});return Object.assign({},e,t)}function j(e,t){var n=Object.assign({},t,{content:i(t.content,[e])},t.ignoreAttributes?{}:function(e,t){return(t?Object.keys(P(Object.assign({},R,{plugins:t}))):k).reduce((function(t,n){var r=(e.getAttribute("data-tippy-"+n)||"").trim();if(!r)return t;if("content"===n)t[n]=r;else try{t[n]=JSON.parse(r)}catch(e){t[n]=r}return t}),{})}(e,t.plugins));return n.aria=Object.assign({},R.aria,n.aria),n.aria={expanded:"auto"===n.aria.expanded?t.interactive:n.aria.expanded,content:"auto"===n.aria.content?t.interactive?null:"describedby":n.aria.content},n}function M(e,t){e.innerHTML=t}function V(e){var t=d();return!0===e?t.className="tippy-arrow":(t.className="tippy-svg-arrow",v(e)?t.appendChild(e):M(t,e)),t}function I(e,t){v(t.content)?(M(e,""),e.appendChild(t.content)):"function"!=typeof t.content&&(t.allowHTML?M(e,t.content):e.textContent=t.content)}function S(e){var t=e.firstElementChild,n=f(t.children);return{box:t,content:n.find((function(e){return e.classList.contains("tippy-content")})),arrow:n.find((function(e){return e.classList.contains("tippy-arrow")||e.classList.contains("tippy-svg-arrow")})),backdrop:n.find((function(e){return e.classList.contains("tippy-backdrop")}))}}function N(e){var t=d(),n=d();n.className="tippy-box",n.setAttribute("data-state","hidden"),n.setAttribute("tabindex","-1");var r=d();function o(n,r){var o=S(t),i=o.box,a=o.content,s=o.arrow;r.theme?i.setAttribute("data-theme",r.theme):i.removeAttribute("data-theme"),"string"==typeof r.animation?i.setAttribute("data-animation",r.animation):i.removeAttribute("data-animation"),r.inertia?i.setAttribute("data-inertia",""):i.removeAttribute("data-inertia"),i.style.maxWidth="number"==typeof r.maxWidth?r.maxWidth+"px":r.maxWidth,r.role?i.setAttribute("role",r.role):i.removeAttribute("role"),n.content===r.content&&n.allowHTML===r.allowHTML||I(a,e.props),r.arrow?s?n.arrow!==r.arrow&&(i.removeChild(s),i.appendChild(V(r.arrow))):i.appendChild(V(r.arrow)):s&&i.removeChild(s)}return r.className="tippy-content",r.setAttribute("data-state","hidden"),I(r,e.props),t.appendChild(n),n.appendChild(r),o(e.props,e.props),{popper:t,onUpdate:o}}N.$$tippy=!0;var B=1,H=[],U=[];function _(o,s){var v,g,h,C,T,A,L,k,M=j(o,Object.assign({},R,P(l(s)))),V=!1,I=!1,N=!1,_=!1,F=[],W=a(we,M.interactiveDebounce),X=B++,Y=(k=M.plugins).filter((function(e,t){return k.indexOf(e)===t})),$={id:X,reference:o,popper:d(),popperInstance:null,props:M,state:{isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},plugins:Y,clearDelayTimeouts:function(){clearTimeout(v),clearTimeout(g),cancelAnimationFrame(h)},setProps:function(e){if($.state.isDestroyed)return;ae("onBeforeUpdate",[$,e]),be();var t=$.props,n=j(o,Object.assign({},t,l(e),{ignoreAttributes:!0}));$.props=n,he(),t.interactiveDebounce!==n.interactiveDebounce&&(ce(),W=a(we,n.interactiveDebounce));t.triggerTarget&&!n.triggerTarget?u(t.triggerTarget).forEach((function(e){e.removeAttribute("aria-expanded")})):n.triggerTarget&&o.removeAttribute("aria-expanded");ue(),ie(),J&&J(t,n);$.popperInstance&&(Ce(),Ae().forEach((function(e){requestAnimationFrame(e._tippy.popperInstance.forceUpdate)})));ae("onAfterUpdate",[$,e])},setContent:function(e){$.setProps({content:e})},show:function(){var e=$.state.isVisible,t=$.state.isDestroyed,o=!$.state.isEnabled,a=x.isTouch&&!$.props.touch,s=r($.props.duration,0,R.duration);if(e||t||o||a)return;if(te().hasAttribute("disabled"))return;if(ae("onShow",[$],!1),!1===$.props.onShow($))return;$.state.isVisible=!0,ee()&&(z.style.visibility="visible");ie(),de(),$.state.isMounted||(z.style.transition="none");if(ee()){var u=re(),p=u.box,f=u.content;b([p,f],0)}A=function(){var e;if($.state.isVisible&&!_){if(_=!0,z.offsetHeight,z.style.transition=$.props.moveTransition,ee()&&$.props.animation){var t=re(),n=t.box,r=t.content;b([n,r],s),y([n,r],"visible")}se(),ue(),c(U,$),null==(e=$.popperInstance)||e.forceUpdate(),ae("onMount",[$]),$.props.animation&&ee()&&function(e,t){me(e,t)}(s,(function(){$.state.isShown=!0,ae("onShown",[$])}))}},function(){var e,t=$.props.appendTo,r=te();e=$.props.interactive&&t===n||"parent"===t?r.parentNode:i(t,[r]);e.contains(z)||e.appendChild(z);$.state.isMounted=!0,Ce()}()},hide:function(){var e=!$.state.isVisible,t=$.state.isDestroyed,n=!$.state.isEnabled,o=r($.props.duration,1,R.duration);if(e||t||n)return;if(ae("onHide",[$],!1),!1===$.props.onHide($))return;$.state.isVisible=!1,$.state.isShown=!1,_=!1,V=!1,ee()&&(z.style.visibility="hidden");if(ce(),ve(),ie(!0),ee()){var i=re(),a=i.box,s=i.content;$.props.animation&&(b([a,s],o),y([a,s],"hidden"))}se(),ue(),$.props.animation?ee()&&function(e,t){me(e,(function(){!$.state.isVisible&&z.parentNode&&z.parentNode.contains(z)&&t()}))}(o,$.unmount):$.unmount()},hideWithInteractivity:function(e){ne().addEventListener("mousemove",W),c(H,W),W(e)},enable:function(){$.state.isEnabled=!0},disable:function(){$.hide(),$.state.isEnabled=!1},unmount:function(){$.state.isVisible&&$.hide();if(!$.state.isMounted)return;Te(),Ae().forEach((function(e){e._tippy.unmount()})),z.parentNode&&z.parentNode.removeChild(z);U=U.filter((function(e){return e!==$})),$.state.isMounted=!1,ae("onHidden",[$])},destroy:function(){if($.state.isDestroyed)return;$.clearDelayTimeouts(),$.unmount(),be(),delete o._tippy,$.state.isDestroyed=!0,ae("onDestroy",[$])}};if(!M.render)return $;var q=M.render($),z=q.popper,J=q.onUpdate;z.setAttribute("data-tippy-root",""),z.id="tippy-"+$.id,$.popper=z,o._tippy=$,z._tippy=$;var G=Y.map((function(e){return e.fn($)})),K=o.hasAttribute("aria-expanded");return he(),ue(),ie(),ae("onCreate",[$]),M.showOnCreate&&Le(),z.addEventListener("mouseenter",(function(){$.props.interactive&&$.state.isVisible&&$.clearDelayTimeouts()})),z.addEventListener("mouseleave",(function(){$.props.interactive&&$.props.trigger.indexOf("mouseenter")>=0&&ne().addEventListener("mousemove",W)})),$;function Q(){var e=$.props.touch;return Array.isArray(e)?e:[e,0]}function Z(){return"hold"===Q()[0]}function ee(){var e;return!(null==(e=$.props.render)||!e.$$tippy)}function te(){return L||o}function ne(){var e=te().parentNode;return e?w(e):document}function re(){return S(z)}function oe(e){return $.state.isMounted&&!$.state.isVisible||x.isTouch||C&&"focus"===C.type?0:r($.props.delay,e?0:1,R.delay)}function ie(e){void 0===e&&(e=!1),z.style.pointerEvents=$.props.interactive&&!e?"":"none",z.style.zIndex=""+$.props.zIndex}function ae(e,t,n){var r;(void 0===n&&(n=!0),G.forEach((function(n){n[e]&&n[e].apply(n,t)})),n)&&(r=$.props)[e].apply(r,t)}function se(){var e=$.props.aria;if(e.content){var t="aria-"+e.content,n=z.id;u($.props.triggerTarget||o).forEach((function(e){var r=e.getAttribute(t);if($.state.isVisible)e.setAttribute(t,r?r+" "+n:n);else{var o=r&&r.replace(n,"").trim();o?e.setAttribute(t,o):e.removeAttribute(t)}}))}}function ue(){!K&&$.props.aria.expanded&&u($.props.triggerTarget||o).forEach((function(e){$.props.interactive?e.setAttribute("aria-expanded",$.state.isVisible&&e===te()?"true":"false"):e.removeAttribute("aria-expanded")}))}function ce(){ne().removeEventListener("mousemove",W),H=H.filter((function(e){return e!==W}))}function pe(e){if(!x.isTouch||!N&&"mousedown"!==e.type){var t=e.composedPath&&e.composedPath()[0]||e.target;if(!$.props.interactive||!O(z,t)){if(u($.props.triggerTarget||o).some((function(e){return O(e,t)}))){if(x.isTouch)return;if($.state.isVisible&&$.props.trigger.indexOf("click")>=0)return}else ae("onClickOutside",[$,e]);!0===$.props.hideOnClick&&($.clearDelayTimeouts(),$.hide(),I=!0,setTimeout((function(){I=!1})),$.state.isMounted||ve())}}}function fe(){N=!0}function le(){N=!1}function de(){var e=ne();e.addEventListener("mousedown",pe,!0),e.addEventListener("touchend",pe,t),e.addEventListener("touchstart",le,t),e.addEventListener("touchmove",fe,t)}function ve(){var e=ne();e.removeEventListener("mousedown",pe,!0),e.removeEventListener("touchend",pe,t),e.removeEventListener("touchstart",le,t),e.removeEventListener("touchmove",fe,t)}function me(e,t){var n=re().box;function r(e){e.target===n&&(E(n,"remove",r),t())}if(0===e)return t();E(n,"remove",T),E(n,"add",r),T=r}function ge(e,t,n){void 0===n&&(n=!1),u($.props.triggerTarget||o).forEach((function(r){r.addEventListener(e,t,n),F.push({node:r,eventType:e,handler:t,options:n})}))}function he(){var e;Z()&&(ge("touchstart",ye,{passive:!0}),ge("touchend",Ee,{passive:!0})),(e=$.props.trigger,e.split(/\s+/).filter(Boolean)).forEach((function(e){if("manual"!==e)switch(ge(e,ye),e){case"mouseenter":ge("mouseleave",Ee);break;case"focus":ge(D?"focusout":"blur",Oe);break;case"focusin":ge("focusout",Oe)}}))}function be(){F.forEach((function(e){var t=e.node,n=e.eventType,r=e.handler,o=e.options;t.removeEventListener(n,r,o)})),F=[]}function ye(e){var t,n=!1;if($.state.isEnabled&&!xe(e)&&!I){var r="focus"===(null==(t=C)?void 0:t.type);C=e,L=e.currentTarget,ue(),!$.state.isVisible&&m(e)&&H.forEach((function(t){return t(e)})),"click"===e.type&&($.props.trigger.indexOf("mouseenter")<0||V)&&!1!==$.props.hideOnClick&&$.state.isVisible?n=!0:Le(e),"click"===e.type&&(V=!n),n&&!r&&De(e)}}function we(e){var t=e.target,n=te().contains(t)||z.contains(t);"mousemove"===e.type&&n||function(e,t){var n=t.clientX,r=t.clientY;return e.every((function(e){var t=e.popperRect,o=e.popperState,i=e.props.interactiveBorder,a=p(o.placement),s=o.modifiersData.offset;if(!s)return!0;var u="bottom"===a?s.top.y:0,c="top"===a?s.bottom.y:0,f="right"===a?s.left.x:0,l="left"===a?s.right.x:0,d=t.top-r+u>i,v=r-t.bottom-c>i,m=t.left-n+f>i,g=n-t.right-l>i;return d||v||m||g}))}(Ae().concat(z).map((function(e){var t,n=null==(t=e._tippy.popperInstance)?void 0:t.state;return n?{popperRect:e.getBoundingClientRect(),popperState:n,props:M}:null})).filter(Boolean),e)&&(ce(),De(e))}function Ee(e){xe(e)||$.props.trigger.indexOf("click")>=0&&V||($.props.interactive?$.hideWithInteractivity(e):De(e))}function Oe(e){$.props.trigger.indexOf("focusin")<0&&e.target!==te()||$.props.interactive&&e.relatedTarget&&z.contains(e.relatedTarget)||De(e)}function xe(e){return!!x.isTouch&&Z()!==e.type.indexOf("touch")>=0}function Ce(){Te();var t=$.props,n=t.popperOptions,r=t.placement,i=t.offset,a=t.getReferenceClientRect,s=t.moveTransition,u=ee()?S(z).arrow:null,c=a?{getBoundingClientRect:a,contextElement:a.contextElement||te()}:o,p=[{name:"offset",options:{offset:i}},{name:"preventOverflow",options:{padding:{top:2,bottom:2,left:5,right:5}}},{name:"flip",options:{padding:5}},{name:"computeStyles",options:{adaptive:!s}},{name:"$$tippy",enabled:!0,phase:"beforeWrite",requires:["computeStyles"],fn:function(e){var t=e.state;if(ee()){var n=re().box;["placement","reference-hidden","escaped"].forEach((function(e){"placement"===e?n.setAttribute("data-placement",t.placement):t.attributes.popper["data-popper-"+e]?n.setAttribute("data-"+e,""):n.removeAttribute("data-"+e)})),t.attributes.popper={}}}}];ee()&&u&&p.push({name:"arrow",options:{element:u,padding:3}}),p.push.apply(p,(null==n?void 0:n.modifiers)||[]),$.popperInstance=e.createPopper(c,z,Object.assign({},n,{placement:r,onFirstUpdate:A,modifiers:p}))}function Te(){$.popperInstance&&($.popperInstance.destroy(),$.popperInstance=null)}function Ae(){return f(z.querySelectorAll("[data-tippy-root]"))}function Le(e){$.clearDelayTimeouts(),e&&ae("onTrigger",[$,e]),de();var t=oe(!0),n=Q(),r=n[0],o=n[1];x.isTouch&&"hold"===r&&o&&(t=o),t?v=setTimeout((function(){$.show()}),t):$.show()}function De(e){if($.clearDelayTimeouts(),ae("onUntrigger",[$,e]),$.state.isVisible){if(!($.props.trigger.indexOf("mouseenter")>=0&&$.props.trigger.indexOf("click")>=0&&["mouseleave","mousemove"].indexOf(e.type)>=0&&V)){var t=oe(!1);t?g=setTimeout((function(){$.state.isVisible&&$.hide()}),t):h=requestAnimationFrame((function(){$.hide()}))}}else ve()}}function F(e,n){void 0===n&&(n={});var r=R.plugins.concat(n.plugins||[]);document.addEventListener("touchstart",T,t),window.addEventListener("blur",L);var o=Object.assign({},n,{plugins:r}),i=h(e).reduce((function(e,t){var n=t&&_(t,o);return n&&e.push(n),e}),[]);return v(e)?i[0]:i}F.defaultProps=R,F.setDefaultProps=function(e){Object.keys(e).forEach((function(t){R[t]=e[t]}))},F.currentInput=x;var W=Object.assign({},e.applyStyles,{effect:function(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow)}}),X={mouseover:"mouseenter",focusin:"focus",click:"click"};var Y={name:"animateFill",defaultValue:!1,fn:function(e){var t;if(null==(t=e.props.render)||!t.$$tippy)return{};var n=S(e.popper),r=n.box,o=n.content,i=e.props.animateFill?function(){var e=d();return e.className="tippy-backdrop",y([e],"hidden"),e}():null;return{onCreate:function(){i&&(r.insertBefore(i,r.firstElementChild),r.setAttribute("data-animatefill",""),r.style.overflow="hidden",e.setProps({arrow:!1,animation:"shift-away"}))},onMount:function(){if(i){var e=r.style.transitionDuration,t=Number(e.replace("ms",""));o.style.transitionDelay=Math.round(t/10)+"ms",i.style.transitionDuration=e,y([i],"visible")}},onShow:function(){i&&(i.style.transitionDuration="0ms")},onHide:function(){i&&y([i],"hidden")}}}};var $={clientX:0,clientY:0},q=[];function z(e){var t=e.clientX,n=e.clientY;$={clientX:t,clientY:n}}var J={name:"followCursor",defaultValue:!1,fn:function(e){var t=e.reference,n=w(e.props.triggerTarget||t),r=!1,o=!1,i=!0,a=e.props;function s(){return"initial"===e.props.followCursor&&e.state.isVisible}function u(){n.addEventListener("mousemove",f)}function c(){n.removeEventListener("mousemove",f)}function p(){r=!0,e.setProps({getReferenceClientRect:null}),r=!1}function f(n){var r=!n.target||t.contains(n.target),o=e.props.followCursor,i=n.clientX,a=n.clientY,s=t.getBoundingClientRect(),u=i-s.left,c=a-s.top;!r&&e.props.interactive||e.setProps({getReferenceClientRect:function(){var e=t.getBoundingClientRect(),n=i,r=a;"initial"===o&&(n=e.left+u,r=e.top+c);var s="horizontal"===o?e.top:r,p="vertical"===o?e.right:n,f="horizontal"===o?e.bottom:r,l="vertical"===o?e.left:n;return{width:p-l,height:f-s,top:s,right:p,bottom:f,left:l}}})}function l(){e.props.followCursor&&(q.push({instance:e,doc:n}),function(e){e.addEventListener("mousemove",z)}(n))}function d(){0===(q=q.filter((function(t){return t.instance!==e}))).filter((function(e){return e.doc===n})).length&&function(e){e.removeEventListener("mousemove",z)}(n)}return{onCreate:l,onDestroy:d,onBeforeUpdate:function(){a=e.props},onAfterUpdate:function(t,n){var i=n.followCursor;r||void 0!==i&&a.followCursor!==i&&(d(),i?(l(),!e.state.isMounted||o||s()||u()):(c(),p()))},onMount:function(){e.props.followCursor&&!o&&(i&&(f($),i=!1),s()||u())},onTrigger:function(e,t){m(t)&&($={clientX:t.clientX,clientY:t.clientY}),o="focus"===t.type},onHidden:function(){e.props.followCursor&&(p(),c(),i=!0)}}}};var G={name:"inlinePositioning",defaultValue:!1,fn:function(e){var t,n=e.reference;var r=-1,o=!1,i=[],a={name:"tippyInlinePositioning",enabled:!0,phase:"afterWrite",fn:function(o){var a=o.state;e.props.inlinePositioning&&(-1!==i.indexOf(a.placement)&&(i=[]),t!==a.placement&&-1===i.indexOf(a.placement)&&(i.push(a.placement),e.setProps({getReferenceClientRect:function(){return function(e){return function(e,t,n,r){if(n.length<2||null===e)return t;if(2===n.length&&r>=0&&n[0].left>n[1].right)return n[r]||t;switch(e){case"top":case"bottom":var o=n[0],i=n[n.length-1],a="top"===e,s=o.top,u=i.bottom,c=a?o.left:i.left,p=a?o.right:i.right;return{top:s,bottom:u,left:c,right:p,width:p-c,height:u-s};case"left":case"right":var f=Math.min.apply(Math,n.map((function(e){return e.left}))),l=Math.max.apply(Math,n.map((function(e){return e.right}))),d=n.filter((function(t){return"left"===e?t.left===f:t.right===l})),v=d[0].top,m=d[d.length-1].bottom;return{top:v,bottom:m,left:f,right:l,width:l-f,height:m-v};default:return t}}(p(e),n.getBoundingClientRect(),f(n.getClientRects()),r)}(a.placement)}})),t=a.placement)}};function s(){var t;o||(t=function(e,t){var n;return{popperOptions:Object.assign({},e.popperOptions,{modifiers:[].concat(((null==(n=e.popperOptions)?void 0:n.modifiers)||[]).filter((function(e){return e.name!==t.name})),[t])})}}(e.props,a),o=!0,e.setProps(t),o=!1)}return{onCreate:s,onAfterUpdate:s,onTrigger:function(t,n){if(m(n)){var o=f(e.reference.getClientRects()),i=o.find((function(e){return e.left-2<=n.clientX&&e.right+2>=n.clientX&&e.top-2<=n.clientY&&e.bottom+2>=n.clientY})),a=o.indexOf(i);r=a>-1?a:r}},onHidden:function(){r=-1}}}};var K={name:"sticky",defaultValue:!1,fn:function(e){var t=e.reference,n=e.popper;function r(t){return!0===e.props.sticky||e.props.sticky===t}var o=null,i=null;function a(){var s=r("reference")?(e.popperInstance?e.popperInstance.state.elements.reference:t).getBoundingClientRect():null,u=r("popper")?n.getBoundingClientRect():null;(s&&Q(o,s)||u&&Q(i,u))&&e.popperInstance&&e.popperInstance.update(),o=s,i=u,e.state.isMounted&&requestAnimationFrame(a)}return{onMount:function(){e.props.sticky&&a()}}}};function Q(e,t){return!e||!t||(e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left)}return F.setDefaultProps({plugins:[Y,J,G,K],render:N}),F.createSingleton=function(e,t){var n;void 0===t&&(t={});var r,o=e,i=[],a=[],c=t.overrides,p=[],f=!1;function l(){a=o.map((function(e){return u(e.props.triggerTarget||e.reference)})).reduce((function(e,t){return e.concat(t)}),[])}function v(){i=o.map((function(e){return e.reference}))}function m(e){o.forEach((function(t){e?t.enable():t.disable()}))}function g(e){return o.map((function(t){var n=t.setProps;return t.setProps=function(o){n(o),t.reference===r&&e.setProps(o)},function(){t.setProps=n}}))}function h(e,t){var n=a.indexOf(t);if(t!==r){r=t;var s=(c||[]).concat("content").reduce((function(e,t){return e[t]=o[n].props[t],e}),{});e.setProps(Object.assign({},s,{getReferenceClientRect:"function"==typeof s.getReferenceClientRect?s.getReferenceClientRect:function(){var e;return null==(e=i[n])?void 0:e.getBoundingClientRect()}}))}}m(!1),v(),l();var b={fn:function(){return{onDestroy:function(){m(!0)},onHidden:function(){r=null},onClickOutside:function(e){e.props.showOnCreate&&!f&&(f=!0,r=null)},onShow:function(e){e.props.showOnCreate&&!f&&(f=!0,h(e,i[0]))},onTrigger:function(e,t){h(e,t.currentTarget)}}}},y=F(d(),Object.assign({},s(t,["overrides"]),{plugins:[b].concat(t.plugins||[]),triggerTarget:a,popperOptions:Object.assign({},t.popperOptions,{modifiers:[].concat((null==(n=t.popperOptions)?void 0:n.modifiers)||[],[W])})})),w=y.show;y.show=function(e){if(w(),!r&&null==e)return h(y,i[0]);if(!r||null!=e){if("number"==typeof e)return i[e]&&h(y,i[e]);if(o.indexOf(e)>=0){var t=e.reference;return h(y,t)}return i.indexOf(e)>=0?h(y,e):void 0}},y.showNext=function(){var e=i[0];if(!r)return y.show(0);var t=i.indexOf(r);y.show(i[t+1]||e)},y.showPrevious=function(){var e=i[i.length-1];if(!r)return y.show(e);var t=i.indexOf(r),n=i[t-1]||e;y.show(n)};var E=y.setProps;return y.setProps=function(e){c=e.overrides||c,E(e)},y.setInstances=function(e){m(!0),p.forEach((function(e){return e()})),o=e,m(!1),v(),l(),p=g(y),y.setProps({triggerTarget:a})},p=g(y),y},F.delegate=function(e,n){var r=[],o=[],i=!1,a=n.target,c=s(n,["target"]),p=Object.assign({},c,{trigger:"manual",touch:!1}),f=Object.assign({touch:R.touch},c,{showOnCreate:!0}),l=F(e,p);function d(e){if(e.target&&!i){var t=e.target.closest(a);if(t){var r=t.getAttribute("data-tippy-trigger")||n.trigger||R.trigger;if(!t._tippy&&!("touchstart"===e.type&&"boolean"==typeof f.touch||"touchstart"!==e.type&&r.indexOf(X[e.type])<0)){var s=F(t,f);s&&(o=o.concat(s))}}}}function v(e,t,n,o){void 0===o&&(o=!1),e.addEventListener(t,n,o),r.push({node:e,eventType:t,handler:n,options:o})}return u(l).forEach((function(e){var n=e.destroy,a=e.enable,s=e.disable;e.destroy=function(e){void 0===e&&(e=!0),e&&o.forEach((function(e){e.destroy()})),o=[],r.forEach((function(e){var t=e.node,n=e.eventType,r=e.handler,o=e.options;t.removeEventListener(n,r,o)})),r=[],n()},e.enable=function(){a(),o.forEach((function(e){return e.enable()})),i=!1},e.disable=function(){s(),o.forEach((function(e){return e.disable()})),i=!0},function(e){var n=e.reference;v(n,"touchstart",d,t),v(n,"mouseover",d),v(n,"focusin",d),v(n,"click",d)}(e)})),l},F.hideAll=function(e){var t=void 0===e?{}:e,n=t.exclude,r=t.duration;U.forEach((function(e){var t=!1;if(n&&(t=g(n)?e.reference===n:e.popper===n.popper),!t){var o=e.props.duration;e.setProps({duration:r}),e.hide(),e.state.isDestroyed||e.setProps({duration:o})}}))},F.roundArrow='',F})); - diff --git a/docs/site_libs/quarto-nav/headroom.min.js b/docs/site_libs/quarto-nav/headroom.min.js deleted file mode 100644 index b08f1df..0000000 --- a/docs/site_libs/quarto-nav/headroom.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * headroom.js v0.12.0 - Give your page some headroom. Hide your header until you need it - * Copyright (c) 2020 Nick Williams - http://wicky.nillia.ms/headroom.js - * License: MIT - */ - -!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(t=t||self).Headroom=n()}(this,function(){"use strict";function t(){return"undefined"!=typeof window}function d(t){return function(t){return t&&t.document&&function(t){return 9===t.nodeType}(t.document)}(t)?function(t){var n=t.document,o=n.body,s=n.documentElement;return{scrollHeight:function(){return Math.max(o.scrollHeight,s.scrollHeight,o.offsetHeight,s.offsetHeight,o.clientHeight,s.clientHeight)},height:function(){return t.innerHeight||s.clientHeight||o.clientHeight},scrollY:function(){return void 0!==t.pageYOffset?t.pageYOffset:(s||o.parentNode||o).scrollTop}}}(t):function(t){return{scrollHeight:function(){return Math.max(t.scrollHeight,t.offsetHeight,t.clientHeight)},height:function(){return Math.max(t.offsetHeight,t.clientHeight)},scrollY:function(){return t.scrollTop}}}(t)}function n(t,s,e){var n,o=function(){var n=!1;try{var t={get passive(){n=!0}};window.addEventListener("test",t,t),window.removeEventListener("test",t,t)}catch(t){n=!1}return n}(),i=!1,r=d(t),l=r.scrollY(),a={};function c(){var t=Math.round(r.scrollY()),n=r.height(),o=r.scrollHeight();a.scrollY=t,a.lastScrollY=l,a.direction=ls.tolerance[a.direction],e(a),l=t,i=!1}function h(){i||(i=!0,n=requestAnimationFrame(c))}var u=!!o&&{passive:!0,capture:!1};return t.addEventListener("scroll",h,u),c(),{destroy:function(){cancelAnimationFrame(n),t.removeEventListener("scroll",h,u)}}}function o(t){return t===Object(t)?t:{down:t,up:t}}function s(t,n){n=n||{},Object.assign(this,s.options,n),this.classes=Object.assign({},s.options.classes,n.classes),this.elem=t,this.tolerance=o(this.tolerance),this.offset=o(this.offset),this.initialised=!1,this.frozen=!1}return s.prototype={constructor:s,init:function(){return s.cutsTheMustard&&!this.initialised&&(this.addClass("initial"),this.initialised=!0,setTimeout(function(t){t.scrollTracker=n(t.scroller,{offset:t.offset,tolerance:t.tolerance},t.update.bind(t))},100,this)),this},destroy:function(){this.initialised=!1,Object.keys(this.classes).forEach(this.removeClass,this),this.scrollTracker.destroy()},unpin:function(){!this.hasClass("pinned")&&this.hasClass("unpinned")||(this.addClass("unpinned"),this.removeClass("pinned"),this.onUnpin&&this.onUnpin.call(this))},pin:function(){this.hasClass("unpinned")&&(this.addClass("pinned"),this.removeClass("unpinned"),this.onPin&&this.onPin.call(this))},freeze:function(){this.frozen=!0,this.addClass("frozen")},unfreeze:function(){this.frozen=!1,this.removeClass("frozen")},top:function(){this.hasClass("top")||(this.addClass("top"),this.removeClass("notTop"),this.onTop&&this.onTop.call(this))},notTop:function(){this.hasClass("notTop")||(this.addClass("notTop"),this.removeClass("top"),this.onNotTop&&this.onNotTop.call(this))},bottom:function(){this.hasClass("bottom")||(this.addClass("bottom"),this.removeClass("notBottom"),this.onBottom&&this.onBottom.call(this))},notBottom:function(){this.hasClass("notBottom")||(this.addClass("notBottom"),this.removeClass("bottom"),this.onNotBottom&&this.onNotBottom.call(this))},shouldUnpin:function(t){return"down"===t.direction&&!t.top&&t.toleranceExceeded},shouldPin:function(t){return"up"===t.direction&&t.toleranceExceeded||t.top},addClass:function(t){this.elem.classList.add.apply(this.elem.classList,this.classes[t].split(" "))},removeClass:function(t){this.elem.classList.remove.apply(this.elem.classList,this.classes[t].split(" "))},hasClass:function(t){return this.classes[t].split(" ").every(function(t){return this.classList.contains(t)},this.elem)},update:function(t){t.isOutOfBounds||!0!==this.frozen&&(t.top?this.top():this.notTop(),t.bottom?this.bottom():this.notBottom(),this.shouldUnpin(t)?this.unpin():this.shouldPin(t)&&this.pin())}},s.options={tolerance:{up:0,down:0},offset:0,scroller:t()?window:null,classes:{frozen:"headroom--frozen",pinned:"headroom--pinned",unpinned:"headroom--unpinned",top:"headroom--top",notTop:"headroom--not-top",bottom:"headroom--bottom",notBottom:"headroom--not-bottom",initial:"headroom"}},s.cutsTheMustard=!!(t()&&function(){}.bind&&"classList"in document.documentElement&&Object.assign&&Object.keys&&requestAnimationFrame),s}); diff --git a/docs/site_libs/quarto-nav/quarto-nav.js b/docs/site_libs/quarto-nav/quarto-nav.js deleted file mode 100644 index 024fb1f..0000000 --- a/docs/site_libs/quarto-nav/quarto-nav.js +++ /dev/null @@ -1,221 +0,0 @@ -const headroomChanged = new CustomEvent("quarto-hrChanged", { - detail: {}, - bubbles: true, - cancelable: false, - composed: false, -}); - -window.document.addEventListener("DOMContentLoaded", function () { - let init = false; - - function throttle(func, wait) { - var timeout; - return function () { - const context = this; - const args = arguments; - const later = function () { - clearTimeout(timeout); - timeout = null; - func.apply(context, args); - }; - - if (!timeout) { - timeout = setTimeout(later, wait); - } - }; - } - - function headerOffset() { - // Set an offset if there is are fixed top navbar - const headerEl = window.document.querySelector("header.fixed-top"); - if (headerEl) { - return headerEl.clientHeight; - } else { - return 0; - } - } - - function footerOffset() { - const footerEl = window.document.querySelector("footer.footer"); - if (footerEl) { - return footerEl.clientHeight; - } else { - return 0; - } - } - - function updateDocumentOffsetWithoutAnimation() { - updateDocumentOffset(false); - } - - function updateDocumentOffset(animated) { - // set body offset - const topOffset = headerOffset(); - const bodyOffset = topOffset + footerOffset(); - const bodyEl = window.document.body; - bodyEl.setAttribute("data-bs-offset", topOffset); - bodyEl.style.paddingTop = topOffset + "px"; - - // deal with sidebar offsets - const sidebars = window.document.querySelectorAll( - ".sidebar, .headroom-target" - ); - sidebars.forEach((sidebar) => { - if (!animated) { - sidebar.classList.add("notransition"); - // Remove the no transition class after the animation has time to complete - setTimeout(function () { - sidebar.classList.remove("notransition"); - }, 201); - } - - if (window.Headroom && sidebar.classList.contains("sidebar-unpinned")) { - sidebar.style.top = "0"; - sidebar.style.maxHeight = "100vh"; - } else { - sidebar.style.top = topOffset + "px"; - sidebar.style.maxHeight = "calc(100vh - " + topOffset + "px)"; - } - }); - - // allow space for footer - const mainContainer = window.document.querySelector(".quarto-container"); - if (mainContainer) { - mainContainer.style.minHeight = "calc(100vh - " + bodyOffset + "px)"; - } - - // link offset - let linkStyle = window.document.querySelector("#quarto-target-style"); - if (!linkStyle) { - linkStyle = window.document.createElement("style"); - window.document.head.appendChild(linkStyle); - } - while (linkStyle.firstChild) { - linkStyle.removeChild(linkStyle.firstChild); - } - if (topOffset > 0) { - linkStyle.appendChild( - window.document.createTextNode(` - section:target::before { - content: ""; - display: block; - height: ${topOffset}px; - margin: -${topOffset}px 0 0; - }`) - ); - } - if (init) { - window.dispatchEvent(headroomChanged); - } - init = true; - } - - // initialize headroom - var header = window.document.querySelector("#quarto-header"); - if (header && window.Headroom) { - const headroom = new window.Headroom(header, { - tolerance: 5, - onPin: function () { - const sidebars = window.document.querySelectorAll( - ".sidebar, .headroom-target" - ); - sidebars.forEach((sidebar) => { - sidebar.classList.remove("sidebar-unpinned"); - }); - updateDocumentOffset(); - }, - onUnpin: function () { - const sidebars = window.document.querySelectorAll( - ".sidebar, .headroom-target" - ); - sidebars.forEach((sidebar) => { - sidebar.classList.add("sidebar-unpinned"); - }); - updateDocumentOffset(); - }, - }); - headroom.init(); - - let frozen = false; - window.quartoToggleHeadroom = function () { - if (frozen) { - headroom.unfreeze(); - frozen = false; - } else { - headroom.freeze(); - frozen = true; - } - }; - } - - // Observe size changed for the header - const headerEl = window.document.querySelector("header.fixed-top"); - if (headerEl && window.ResizeObserver) { - const observer = new window.ResizeObserver( - throttle(updateDocumentOffsetWithoutAnimation, 50) - ); - observer.observe(headerEl, { - attributes: true, - childList: true, - characterData: true, - }); - } else { - window.addEventListener( - "resize", - throttle(updateDocumentOffsetWithoutAnimation, 50) - ); - setTimeout(updateDocumentOffsetWithoutAnimation, 500); - } - - // fixup index.html links if we aren't on the filesystem - if (window.location.protocol !== "file:") { - const links = window.document.querySelectorAll("a"); - for (let i = 0; i < links.length; i++) { - links[i].href = links[i].href.replace(/\/index\.html/, "/"); - } - - // Fixup any sharing links that require urls - // Append url to any sharing urls - const sharingLinks = window.document.querySelectorAll( - "a.sidebar-tools-main-item" - ); - for (let i = 0; i < sharingLinks.length; i++) { - const sharingLink = sharingLinks[i]; - const href = sharingLink.getAttribute("href"); - if (href) { - sharingLink.setAttribute( - "href", - href.replace("|url|", window.location.href) - ); - } - } - - // Scroll the active navigation item into view, if necessary - const navSidebar = window.document.querySelector("nav#quarto-sidebar"); - if (navSidebar) { - // Find the active item - const activeItem = navSidebar.querySelector("li.sidebar-item a.active"); - if (activeItem) { - // Wait for the scroll height and height to resolve by observing size changes on the - // nav element that is scrollable - const resizeObserver = new ResizeObserver((_entries) => { - // The bottom of the element - const elBottom = activeItem.offsetTop; - const viewBottom = navSidebar.scrollTop + navSidebar.clientHeight; - - // The element height and scroll height are the same, then we are still loading - if (viewBottom !== navSidebar.scrollHeight) { - // Determine if the item isn't visible and scroll to it - if (elBottom >= viewBottom) { - navSidebar.scrollTop = elBottom; - } - - // stop observing now since we've completed the scroll - resizeObserver.unobserve(navSidebar); - } - }); - resizeObserver.observe(navSidebar); - } - } - } -}); diff --git a/docs/site_libs/quarto-search/autocomplete.umd.js b/docs/site_libs/quarto-search/autocomplete.umd.js deleted file mode 100644 index 3f2dcf0..0000000 --- a/docs/site_libs/quarto-search/autocomplete.umd.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! @algolia/autocomplete-js 1.5.3 | MIT License | © Algolia, Inc. and contributors | https://github.com/algolia/autocomplete */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self)["@algolia/autocomplete-js"]={})}(this,(function(e){"use strict";function t(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function n(e){for(var n=1;n=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function a(e){return function(e){if(Array.isArray(e))return c(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return c(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return c(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=n?null===r?null:0:o}function j(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function w(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function S(e,t){var n=[];return Promise.resolve(e(t)).then((function(e){return Promise.all(e.filter((function(e){return Boolean(e)})).map((function(e){if(e.sourceId,n.includes(e.sourceId))throw new Error("[Autocomplete] The `sourceId` ".concat(JSON.stringify(e.sourceId)," is not unique."));n.push(e.sourceId);var t=function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=new Array(t);ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var oe,ie,ue,ae=null,ce=(oe=-1,ie=-1,ue=void 0,function(e){var t=++oe;return Promise.resolve(e).then((function(e){return ue&&t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var me=["props","refresh","store"],he=["inputElement","formElement","panelElement"],ge=["inputElement"],ye=["inputElement","maxLength"],be=["item","source"];function Oe(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function _e(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function we(e){var t=e.props,n=e.refresh,r=e.store,o=je(e,me);return{getEnvironmentProps:function(e){var n=e.inputElement,o=e.formElement,i=e.panelElement;return _e({onTouchStart:function(e){!r.getState().isOpen&&r.pendingRequests.isEmpty()||e.target===n||!1===[o,i].some((function(t){return n=t,r=e.target,n===r||n.contains(r);var n,r}))&&(r.dispatch("blur",null),t.debug||r.pendingRequests.cancelAll())},onTouchMove:function(e){!1!==r.getState().isOpen&&n===t.environment.document.activeElement&&e.target!==n&&n.blur()}},je(e,he))},getRootProps:function(e){return _e({role:"combobox","aria-expanded":r.getState().isOpen,"aria-haspopup":"listbox","aria-owns":r.getState().isOpen?"".concat(t.id,"-list"):void 0,"aria-labelledby":"".concat(t.id,"-label")},e)},getFormProps:function(e){return e.inputElement,_e({action:"",noValidate:!0,role:"search",onSubmit:function(i){var u;i.preventDefault(),t.onSubmit(_e({event:i,refresh:n,state:r.getState()},o)),r.dispatch("submit",null),null===(u=e.inputElement)||void 0===u||u.blur()},onReset:function(i){var u;i.preventDefault(),t.onReset(_e({event:i,refresh:n,state:r.getState()},o)),r.dispatch("reset",null),null===(u=e.inputElement)||void 0===u||u.focus()}},je(e,ge))},getLabelProps:function(e){return _e({htmlFor:"".concat(t.id,"-input"),id:"".concat(t.id,"-label")},e)},getInputProps:function(e){function i(e){(t.openOnFocus||Boolean(r.getState().query))&&le(_e({event:e,props:t,query:r.getState().completion||r.getState().query,refresh:n,store:r},o)),r.dispatch("focus",null)}var u="ontouchstart"in t.environment,a=e||{};a.inputElement;var c=a.maxLength,l=void 0===c?512:c,s=je(a,ye),p=I(r.getState());return _e({"aria-autocomplete":"both","aria-activedescendant":r.getState().isOpen&&null!==r.getState().activeItemId?"".concat(t.id,"-item-").concat(r.getState().activeItemId):void 0,"aria-controls":r.getState().isOpen?"".concat(t.id,"-list"):void 0,"aria-labelledby":"".concat(t.id,"-label"),value:r.getState().completion||r.getState().query,id:"".concat(t.id,"-input"),autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",enterKeyHint:null!=p&&p.itemUrl?"go":"search",spellCheck:"false",autoFocus:t.autoFocus,placeholder:t.placeholder,maxLength:l,type:"search",onChange:function(e){le(_e({event:e,props:t,query:e.currentTarget.value.slice(0,l),refresh:n,store:r},o))},onKeyDown:function(e){!function(e){var t=e.event,n=e.props,r=e.refresh,o=e.store,i=ve(e,se);if("ArrowUp"===t.key||"ArrowDown"===t.key){var u=function(){var e=n.environment.document.getElementById("".concat(n.id,"-item-").concat(o.getState().activeItemId));e&&(e.scrollIntoViewIfNeeded?e.scrollIntoViewIfNeeded(!1):e.scrollIntoView(!1))},a=function(){var e=I(o.getState());if(null!==o.getState().activeItemId&&e){var n=e.item,u=e.itemInputValue,a=e.itemUrl,c=e.source;c.onActive(fe({event:t,item:n,itemInputValue:u,itemUrl:a,refresh:r,source:c,state:o.getState()},i))}};t.preventDefault(),!1===o.getState().isOpen&&(n.openOnFocus||Boolean(o.getState().query))?le(fe({event:t,props:n,query:o.getState().query,refresh:r,store:o},i)).then((function(){o.dispatch(t.key,{nextActiveItemId:n.defaultActiveItemId}),a(),setTimeout(u,0)})):(o.dispatch(t.key,{}),a(),u())}else if("Escape"===t.key)t.preventDefault(),o.dispatch(t.key,null),o.pendingRequests.cancelAll();else if("Enter"===t.key){if(null===o.getState().activeItemId||o.getState().collections.every((function(e){return 0===e.items.length})))return;t.preventDefault();var c=I(o.getState()),l=c.item,s=c.itemInputValue,p=c.itemUrl,f=c.source;if(t.metaKey||t.ctrlKey)void 0!==p&&(f.onSelect(fe({event:t,item:l,itemInputValue:s,itemUrl:p,refresh:r,source:f,state:o.getState()},i)),n.navigator.navigateNewTab({itemUrl:p,item:l,state:o.getState()}));else if(t.shiftKey)void 0!==p&&(f.onSelect(fe({event:t,item:l,itemInputValue:s,itemUrl:p,refresh:r,source:f,state:o.getState()},i)),n.navigator.navigateNewWindow({itemUrl:p,item:l,state:o.getState()}));else if(t.altKey);else{if(void 0!==p)return f.onSelect(fe({event:t,item:l,itemInputValue:s,itemUrl:p,refresh:r,source:f,state:o.getState()},i)),void n.navigator.navigate({itemUrl:p,item:l,state:o.getState()});le(fe({event:t,nextState:{isOpen:!1},props:n,query:s,refresh:r,store:o},i)).then((function(){f.onSelect(fe({event:t,item:l,itemInputValue:s,itemUrl:p,refresh:r,source:f,state:o.getState()},i))}))}}}(_e({event:e,props:t,refresh:n,store:r},o))},onFocus:i,onBlur:function(){u||(r.dispatch("blur",null),t.debug||r.pendingRequests.cancelAll())},onClick:function(n){e.inputElement!==t.environment.document.activeElement||r.getState().isOpen||i(n)}},s)},getPanelProps:function(e){return _e({onMouseDown:function(e){e.preventDefault()},onMouseLeave:function(){r.dispatch("mouseleave",null)}},e)},getListProps:function(e){return _e({role:"listbox","aria-labelledby":"".concat(t.id,"-label"),id:"".concat(t.id,"-list")},e)},getItemProps:function(e){var i=e.item,u=e.source,a=je(e,be);return _e({id:"".concat(t.id,"-item-").concat(i.__autocomplete_id),role:"option","aria-selected":r.getState().activeItemId===i.__autocomplete_id,onMouseMove:function(e){if(i.__autocomplete_id!==r.getState().activeItemId){r.dispatch("mousemove",i.__autocomplete_id);var t=I(r.getState());if(null!==r.getState().activeItemId&&t){var u=t.item,a=t.itemInputValue,c=t.itemUrl,l=t.source;l.onActive(_e({event:e,item:u,itemInputValue:a,itemUrl:c,refresh:n,source:l,state:r.getState()},o))}}},onMouseDown:function(e){e.preventDefault()},onClick:function(e){var a=u.getItemInputValue({item:i,state:r.getState()}),c=u.getItemUrl({item:i,state:r.getState()});(c?Promise.resolve():le(_e({event:e,nextState:{isOpen:!1},props:t,query:a,refresh:n,store:r},o))).then((function(){u.onSelect(_e({event:e,item:i,itemInputValue:a,itemUrl:c,refresh:n,source:u,state:r.getState()},o))}))}},a)}}}function Se(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Ie(e){for(var t=1;t0},reshape:function(e){return e.sources}},e),{},{id:null!==(n=e.id)&&void 0!==n?n:d(),plugins:o,initialState:F({activeItemId:null,query:"",completion:null,collections:[],isOpen:!1,status:"idle",context:{}},e.initialState),onStateChange:function(t){var n;null===(n=e.onStateChange)||void 0===n||n.call(e,t),o.forEach((function(e){var n;return null===(n=e.onStateChange)||void 0===n?void 0:n.call(e,t)}))},onSubmit:function(t){var n;null===(n=e.onSubmit)||void 0===n||n.call(e,t),o.forEach((function(e){var n;return null===(n=e.onSubmit)||void 0===n?void 0:n.call(e,t)}))},onReset:function(t){var n;null===(n=e.onReset)||void 0===n||n.call(e,t),o.forEach((function(e){var n;return null===(n=e.onReset)||void 0===n?void 0:n.call(e,t)}))},getSources:function(n){return Promise.all([].concat(R(o.map((function(e){return e.getSources}))),[e.getSources]).filter(Boolean).map((function(e){return S(e,n)}))).then((function(e){return p(e)})).then((function(e){return e.map((function(e){return F(F({},e),{},{onSelect:function(n){e.onSelect(n),t.forEach((function(e){var t;return null===(t=e.onSelect)||void 0===t?void 0:t.call(e,n)}))},onActive:function(n){e.onActive(n),t.forEach((function(e){var t;return null===(t=e.onActive)||void 0===t?void 0:t.call(e,n)}))}})}))}))},navigator:F({navigate:function(e){var t=e.itemUrl;r.location.assign(t)},navigateNewTab:function(e){var t=e.itemUrl,n=r.open(t,"_blank","noopener");null==n||n.focus()},navigateNewWindow:function(e){var t=e.itemUrl;r.open(t,"_blank","noopener")}},e.navigator)})}(e,t),r=x(qe,n,(function(e){var t=e.prevState,r=e.state;n.onStateChange(Le({prevState:t,state:r,refresh:u},o))})),o=function(e){var t=e.store;return{setActiveItemId:function(e){t.dispatch("setActiveItemId",e)},setQuery:function(e){t.dispatch("setQuery",e)},setCollections:function(e){var n=0,r=e.map((function(e){return N(N({},e),{},{items:p(e.items).map((function(e){return N(N({},e),{},{__autocomplete_id:n++})}))})}));t.dispatch("setCollections",r)},setIsOpen:function(e){t.dispatch("setIsOpen",e)},setStatus:function(e){t.dispatch("setStatus",e)},setContext:function(e){t.dispatch("setContext",e)}}}({store:r}),i=we(Le({props:n,refresh:u,store:r},o));function u(){return le(Le({event:new Event("input"),nextState:{isOpen:r.getState().isOpen},props:n,query:r.getState().query,refresh:u,store:r},o))}return n.plugins.forEach((function(e){var n;return null===(n=e.subscribe)||void 0===n?void 0:n.call(e,Le(Le({},o),{},{refresh:u,onSelect:function(e){t.push({onSelect:e})},onActive:function(e){t.push({onActive:e})}}))})),function(e){var t,n=e.metadata,r=e.environment;if(null===(t=r.navigator)||void 0===t?void 0:t.userAgent.includes("Algolia Crawler")){var o=r.document.createElement("meta"),i=r.document.querySelector("head");o.name="algolia:metadata",setTimeout((function(){o.content=JSON.stringify(n),i.appendChild(o)}),0)}}({metadata:Ae({plugins:n.plugins,options:e}),environment:n.environment}),Le(Le({refresh:u},i),o)}var Te=function(e){var t=e.environment,n=t.document.createElementNS("http://www.w3.org/2000/svg","svg");n.setAttribute("class","aa-ClearIcon"),n.setAttribute("viewBox","0 0 24 24"),n.setAttribute("width","18"),n.setAttribute("height","18"),n.setAttribute("fill","currentColor");var r=t.document.createElementNS("http://www.w3.org/2000/svg","path");return r.setAttribute("d","M5.293 6.707l5.293 5.293-5.293 5.293c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0l5.293-5.293 5.293 5.293c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414l-5.293-5.293 5.293-5.293c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0l-5.293 5.293-5.293-5.293c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414z"),n.appendChild(r),n};function Fe(e,t){if("string"==typeof t){var n=e.document.querySelector(t);return"The element ".concat(JSON.stringify(t)," is not in the document."),n}return t}function Ue(){for(var e=arguments.length,t=new Array(e),n=0;n2&&(u.children=arguments.length>3?tt.call(arguments,2):n),"function"==typeof e&&null!=e.defaultProps)for(i in e.defaultProps)void 0===u[i]&&(u[i]=e.defaultProps[i]);return dt(e,u,r,o,null)}function dt(e,t,n,r,o){var i={type:e,props:t,key:n,ref:r,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==o?++rt:o};return null==o&&null!=nt.vnode&&nt.vnode(i),i}function vt(e){return e.children}function mt(e,t){this.props=e,this.context=t}function ht(e,t){if(null==t)return e.__?ht(e.__,e.__.__k.indexOf(e)+1):null;for(var n;t0?dt(d.type,d.props,d.key,null,d.__v):d)){if(d.__=n,d.__b=n.__b+1,null===(f=g[s])||f&&d.key==f.key&&d.type===f.type)g[s]=void 0;else for(p=0;p0&&void 0!==arguments[0]?arguments[0]:[];return{get:function(){return e},add:function(t){var n=e[e.length-1];(null==n?void 0:n.isHighlighted)===t.isHighlighted?e[e.length-1]={value:n.value+t.value,isHighlighted:n.isHighlighted}:e.push(t)}}}(n?[{value:n,isHighlighted:!1}]:[]);return t.forEach((function(e){var t=e.split(Nt);r.add({value:t[0],isHighlighted:!0}),""!==t[1]&&r.add({value:t[1],isHighlighted:!1})})),r.get()}function Rt(e){return function(e){if(Array.isArray(e))return Bt(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return Bt(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return Bt(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Bt(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n",""":'"',"'":"'"},Ut=new RegExp(/\w/i),Mt=/&(amp|quot|lt|gt|#39);/g,Ht=RegExp(Mt.source);function Vt(e,t){var n,r,o,i=e[t],u=(null===(n=e[t+1])||void 0===n?void 0:n.isHighlighted)||!0,a=(null===(r=e[t-1])||void 0===r?void 0:r.isHighlighted)||!0;return Ut.test((o=i.value)&&Ht.test(o)?o.replace(Mt,(function(e){return Ft[e]})):o)||a!==u?i.isHighlighted:a}function Wt(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Qt(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function un(e){return function(e){if(Array.isArray(e))return an(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return an(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return an(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function an(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0;if(!O.value.core.openOnFocus&&!t.query)return n;var r=Boolean(g.current||O.value.renderer.renderNoResults);return!n&&r||n},__autocomplete_metadata:{userAgents:hn,options:e}}))})),j=l(n({collections:[],completion:null,context:{},isOpen:!1,query:"",activeItemId:null,status:"idle"},O.value.core.initialState)),w={getEnvironmentProps:O.value.renderer.getEnvironmentProps,getFormProps:O.value.renderer.getFormProps,getInputProps:O.value.renderer.getInputProps,getItemProps:O.value.renderer.getItemProps,getLabelProps:O.value.renderer.getLabelProps,getListProps:O.value.renderer.getListProps,getPanelProps:O.value.renderer.getPanelProps,getRootProps:O.value.renderer.getRootProps},S={setActiveItemId:P.value.setActiveItemId,setQuery:P.value.setQuery,setCollections:P.value.setCollections,setIsOpen:P.value.setIsOpen,setStatus:P.value.setStatus,setContext:P.value.setContext,refresh:P.value.refresh},I=v((function(){return et({autocomplete:P.value,autocompleteScopeApi:S,classNames:O.value.renderer.classNames,environment:O.value.core.environment,isDetached:_.value,placeholder:O.value.core.placeholder,propGetters:w,setIsModalOpen:D,state:j.current,translations:O.value.renderer.translations})}));function E(){ze(I.value.panel,{style:_.value?{}:mn({panelPlacement:O.value.renderer.panelPlacement,container:I.value.root,form:I.value.form,environment:O.value.core.environment})})}function A(e){j.current=e;var t={autocomplete:P.value,autocompleteScopeApi:S,classNames:O.value.renderer.classNames,components:O.value.renderer.components,container:O.value.renderer.container,createElement:O.value.renderer.renderer.createElement,dom:I.value,Fragment:O.value.renderer.renderer.Fragment,panelContainer:_.value?I.value.detachedContainer:O.value.renderer.panelContainer,propGetters:w,state:j.current},r=!m(e)&&!g.current&&O.value.renderer.renderNoResults||O.value.renderer.render;!function(e){var t=e.autocomplete,r=e.autocompleteScopeApi,o=e.dom,i=e.propGetters,u=e.state;Ge(o.root,i.getRootProps(n({state:u,props:t.getRootProps({})},r))),Ge(o.input,i.getInputProps(n({state:u,props:t.getInputProps({inputElement:o.input}),inputElement:o.input},r))),ze(o.label,{hidden:"stalled"===u.status}),ze(o.loadingIndicator,{hidden:"stalled"!==u.status}),ze(o.clearButton,{hidden:!u.query})}(t),function(e,t){var r=t.autocomplete,o=t.autocompleteScopeApi,u=t.classNames,a=t.createElement,c=t.dom,l=t.Fragment,s=t.panelContainer,p=t.propGetters,f=t.state,d=t.components;if(f.isOpen){s.contains(c.panel)||"loading"===f.status||s.appendChild(c.panel),c.panel.classList.toggle("aa-Panel--stalled","stalled"===f.status);var v=f.collections.filter((function(e){var t=e.source,n=e.items;return t.templates.noResults||n.length>0})).map((function(e,t){var c=e.source,s=e.items;return a("section",{key:t,className:u.source,"data-autocomplete-source-id":c.sourceId},c.templates.header&&a("div",{className:u.sourceHeader},c.templates.header({components:d,createElement:a,Fragment:l,items:s,source:c,state:f})),c.templates.noResults&&0===s.length?a("div",{className:u.sourceNoResults},c.templates.noResults({components:d,createElement:a,Fragment:l,source:c,state:f})):a("ul",i({className:u.list},p.getListProps(n({state:f,props:r.getListProps({})},o))),s.map((function(e){var t=r.getItemProps({item:e,source:c});return a("li",i({key:t.id,className:u.item},p.getItemProps(n({state:f,props:t},o))),c.templates.item({components:d,createElement:a,Fragment:l,item:e,state:f}))}))),c.templates.footer&&a("div",{className:u.sourceFooter},c.templates.footer({components:d,createElement:a,Fragment:l,items:s,source:c,state:f})))})),m=a(l,null,a("div",{className:u.panelLayout},v),a("div",{className:"aa-GradientBottom"})),h=v.reduce((function(e,t){return e[t.props["data-autocomplete-source-id"]]=t,e}),{});e(n({children:m,state:f,sections:v,elements:h,createElement:a,Fragment:l,components:d},o),c.panel)}else s.contains(c.panel)&&s.removeChild(c.panel)}(r,t)}function C(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};c(),y.current=He(O.value.renderer,O.value.core,{initialState:j.current},e),h(),p(),P.value.refresh().then((function(){A(j.current)}))}function D(e){requestAnimationFrame((function(){var t=O.value.core.environment.document.body.contains(I.value.detachedOverlay);e!==t&&(e?(O.value.core.environment.document.body.appendChild(I.value.detachedOverlay),O.value.core.environment.document.body.classList.add("aa-Detached"),I.value.input.focus()):(O.value.core.environment.document.body.removeChild(I.value.detachedOverlay),O.value.core.environment.document.body.classList.remove("aa-Detached"),P.value.setQuery(""),P.value.refresh()))}))}return a((function(){var e=P.value.getEnvironmentProps({formElement:I.value.form,panelElement:I.value.panel,inputElement:I.value.input});return ze(O.value.core.environment,e),function(){ze(O.value.core.environment,Object.keys(e).reduce((function(e,t){return n(n({},e),{},o({},t,void 0))}),{}))}})),a((function(){var e=_.value?O.value.core.environment.document.body:O.value.renderer.panelContainer,t=_.value?I.value.detachedOverlay:I.value.panel;return _.value&&j.current.isOpen&&D(!0),A(j.current),function(){e.contains(t)&&e.removeChild(t)}})),a((function(){var e=O.value.renderer.container;return e.appendChild(I.value.root),function(){e.removeChild(I.value.root)}})),a((function(){var e=s((function(e){A(e.state)}),0);return b.current=function(t){var n=t.state,r=t.prevState;(_.value&&r.isOpen!==n.isOpen&&D(n.isOpen),_.value||!n.isOpen||r.isOpen||E(),n.query!==r.query)&&O.value.core.environment.document.querySelectorAll(".aa-Panel--scrollable").forEach((function(e){0!==e.scrollTop&&(e.scrollTop=0)}));e({state:n})},function(){b.current=void 0}})),a((function(){var e=s((function(){var e=_.value;_.value=O.value.core.environment.matchMedia(O.value.renderer.detachedMediaQuery).matches,e!==_.value?C({}):requestAnimationFrame(E)}),20);return O.value.core.environment.addEventListener("resize",e),function(){O.value.core.environment.removeEventListener("resize",e)}})),a((function(){if(!_.value)return function(){};function e(e){I.value.detachedContainer.classList.toggle("aa-DetachedContainer--modal",e)}function t(t){e(t.matches)}var n=O.value.core.environment.matchMedia(getComputedStyle(O.value.core.environment.document.documentElement).getPropertyValue("--aa-detached-modal-media-query"));e(n.matches);var r=Boolean(n.addEventListener);return r?n.addEventListener("change",t):n.addListener(t),function(){r?n.removeEventListener("change",t):n.removeListener(t)}})),a((function(){return requestAnimationFrame(E),function(){}})),n(n({},S),{},{update:C,destroy:function(){c()}})},e.getAlgoliaFacets=function(e){var t=gn({transformResponse:function(e){return e.facetHits}}),r=e.queries.map((function(e){return n(n({},e),{},{type:"facet"})}));return t(n(n({},e),{},{queries:r}))},e.getAlgoliaResults=yn,Object.defineProperty(e,"__esModule",{value:!0})})); - diff --git a/docs/site_libs/quarto-search/fuse.min.js b/docs/site_libs/quarto-search/fuse.min.js deleted file mode 100644 index ca37378..0000000 --- a/docs/site_libs/quarto-search/fuse.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Fuse.js v6.5.3 - Lightweight fuzzy-search (http://fusejs.io) - * - * Copyright (c) 2021 Kiro Risk (http://kiro.me) - * All Rights Reserved. Apache Software License 2.0 - * - * http://www.apache.org/licenses/LICENSE-2.0 - */ -var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:1,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:3,n=new Map,r=Math.pow(10,t);return{get:function(t){var i=t.match(C).length;if(n.has(i))return n.get(i);var o=1/Math.pow(i,.5*e),c=parseFloat(Math.round(o*r)/r);return n.set(i,c),c},clear:function(){n.clear()}}}var $=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.getFn,i=void 0===n?I.getFn:n,o=t.fieldNormWeight,c=void 0===o?I.fieldNormWeight:o;r(this,e),this.norm=E(c,3),this.getFn=i,this.isCreated=!1,this.setIndexRecords()}return o(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,g(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();g(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,i=void 0===r?I.getFn:r,o=n.fieldNormWeight,c=void 0===o?I.fieldNormWeight:o,a=new $({getFn:i,fieldNormWeight:c});return a.setKeys(e.map(_)),a.setSources(t),a.create(),a}function F(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.errors,r=void 0===n?0:n,i=t.currentLocation,o=void 0===i?0:i,c=t.expectedLocation,a=void 0===c?0:c,s=t.distance,u=void 0===s?I.distance:s,h=t.ignoreLocation,f=void 0===h?I.ignoreLocation:h,l=r/e.length;if(f)return l;var d=Math.abs(a-o);return u?l+d/u:d?1:l}function N(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:I.minMatchCharLength,n=[],r=-1,i=-1,o=0,c=e.length;o=t&&n.push([r,i]),r=-1)}return e[o-1]&&o-r>=t&&n.push([r,o-1]),n}var P=32;function W(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},o=i.location,c=void 0===o?I.location:o,a=i.threshold,s=void 0===a?I.threshold:a,u=i.distance,h=void 0===u?I.distance:u,f=i.includeMatches,l=void 0===f?I.includeMatches:f,d=i.findAllMatches,v=void 0===d?I.findAllMatches:d,g=i.minMatchCharLength,y=void 0===g?I.minMatchCharLength:g,p=i.isCaseSensitive,m=void 0===p?I.isCaseSensitive:p,k=i.ignoreLocation,M=void 0===k?I.ignoreLocation:k;if(r(this,e),this.options={location:c,threshold:s,distance:h,includeMatches:l,findAllMatches:v,minMatchCharLength:y,isCaseSensitive:m,ignoreLocation:M},this.pattern=m?t:t.toLowerCase(),this.chunks=[],this.pattern.length){var b=function(e,t){n.chunks.push({pattern:e,alphabet:W(e),startIndex:t})},x=this.pattern.length;if(x>P){for(var w=0,L=x%P,S=x-L;w3&&void 0!==arguments[3]?arguments[3]:{},i=r.location,o=void 0===i?I.location:i,c=r.distance,a=void 0===c?I.distance:c,s=r.threshold,u=void 0===s?I.threshold:s,h=r.findAllMatches,f=void 0===h?I.findAllMatches:h,l=r.minMatchCharLength,d=void 0===l?I.minMatchCharLength:l,v=r.includeMatches,g=void 0===v?I.includeMatches:v,y=r.ignoreLocation,p=void 0===y?I.ignoreLocation:y;if(t.length>P)throw new Error(w(P));for(var m,k=t.length,M=e.length,b=Math.max(0,Math.min(o,M)),x=u,L=b,S=d>1||g,_=S?Array(M):[];(m=e.indexOf(t,L))>-1;){var O=F(t,{currentLocation:m,expectedLocation:b,distance:a,ignoreLocation:p});if(x=Math.min(O,x),L=m+k,S)for(var j=0;j=z;q-=1){var B=q-1,J=n[e.charAt(B)];if(S&&(_[B]=+!!J),K[q]=(K[q+1]<<1|1)&J,R&&(K[q]|=(A[q+1]|A[q])<<1|1|A[q+1]),K[q]&$&&(C=F(t,{errors:R,currentLocation:B,expectedLocation:b,distance:a,ignoreLocation:p}))<=x){if(x=C,(L=B)<=b)break;z=Math.max(1,2*b-L)}}if(F(t,{errors:R+1,currentLocation:b,expectedLocation:b,distance:a,ignoreLocation:p})>x)break;A=K}var U={isMatch:L>=0,score:Math.max(.001,C)};if(S){var V=N(_,d);V.length?g&&(U.indices=V):U.isMatch=!1}return U}(e,n,i,{location:c+o,distance:a,threshold:s,findAllMatches:u,minMatchCharLength:h,includeMatches:r,ignoreLocation:f}),p=y.isMatch,m=y.score,k=y.indices;p&&(g=!0),v+=m,p&&k&&(d=[].concat(l(d),l(k)))}));var y={isMatch:g,score:g?v/this.chunks.length:1};return g&&r&&(y.indices=d),y}}]),e}(),z=function(){function e(t){r(this,e),this.pattern=t}return o(e,[{key:"search",value:function(){}}],[{key:"isMultiMatch",value:function(e){return D(e,this.multiRegex)}},{key:"isSingleMatch",value:function(e){return D(e,this.singleRegex)}}]),e}();function D(e,t){var n=e.match(t);return n?n[1]:null}var K=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"exact"}},{key:"multiRegex",get:function(){return/^="(.*)"$/}},{key:"singleRegex",get:function(){return/^=(.*)$/}}]),n}(z),q=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"$/}},{key:"singleRegex",get:function(){return/^!(.*)$/}}]),n}(z),B=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"prefix-exact"}},{key:"multiRegex",get:function(){return/^\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^\^(.*)$/}}]),n}(z),J=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-prefix-exact"}},{key:"multiRegex",get:function(){return/^!\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^!\^(.*)$/}}]),n}(z),U=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}}],[{key:"type",get:function(){return"suffix-exact"}},{key:"multiRegex",get:function(){return/^"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^(.*)\$$/}}]),n}(z),V=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-suffix-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^!(.*)\$$/}}]),n}(z),G=function(e){a(n,e);var t=f(n);function n(e){var i,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=o.location,a=void 0===c?I.location:c,s=o.threshold,u=void 0===s?I.threshold:s,h=o.distance,f=void 0===h?I.distance:h,l=o.includeMatches,d=void 0===l?I.includeMatches:l,v=o.findAllMatches,g=void 0===v?I.findAllMatches:v,y=o.minMatchCharLength,p=void 0===y?I.minMatchCharLength:y,m=o.isCaseSensitive,k=void 0===m?I.isCaseSensitive:m,M=o.ignoreLocation,b=void 0===M?I.ignoreLocation:M;return r(this,n),(i=t.call(this,e))._bitapSearch=new T(e,{location:a,threshold:u,distance:f,includeMatches:d,findAllMatches:g,minMatchCharLength:p,isCaseSensitive:k,ignoreLocation:b}),i}return o(n,[{key:"search",value:function(e){return this._bitapSearch.searchIn(e)}}],[{key:"type",get:function(){return"fuzzy"}},{key:"multiRegex",get:function(){return/^"(.*)"$/}},{key:"singleRegex",get:function(){return/^(.*)$/}}]),n}(z),H=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){for(var t,n=0,r=[],i=this.pattern.length;(t=e.indexOf(this.pattern,n))>-1;)n=t+i,r.push([t,n-1]);var o=!!r.length;return{isMatch:o,score:o?0:1,indices:r}}}],[{key:"type",get:function(){return"include"}},{key:"multiRegex",get:function(){return/^'"(.*)"$/}},{key:"singleRegex",get:function(){return/^'(.*)$/}}]),n}(z),Q=[K,H,B,J,V,U,q,G],X=Q.length,Y=/ +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/;function Z(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.split("|").map((function(e){for(var n=e.trim().split(Y).filter((function(e){return e&&!!e.trim()})),r=[],i=0,o=n.length;i1&&void 0!==arguments[1]?arguments[1]:{},i=n.isCaseSensitive,o=void 0===i?I.isCaseSensitive:i,c=n.includeMatches,a=void 0===c?I.includeMatches:c,s=n.minMatchCharLength,u=void 0===s?I.minMatchCharLength:s,h=n.ignoreLocation,f=void 0===h?I.ignoreLocation:h,l=n.findAllMatches,d=void 0===l?I.findAllMatches:l,v=n.location,g=void 0===v?I.location:v,y=n.threshold,p=void 0===y?I.threshold:y,m=n.distance,k=void 0===m?I.distance:m;r(this,e),this.query=null,this.options={isCaseSensitive:o,includeMatches:a,minMatchCharLength:u,findAllMatches:d,ignoreLocation:f,location:g,threshold:p,distance:k},this.pattern=o?t:t.toLowerCase(),this.query=Z(this.pattern,this.options)}return o(e,[{key:"searchIn",value:function(e){var t=this.query;if(!t)return{isMatch:!1,score:1};var n=this.options,r=n.includeMatches;e=n.isCaseSensitive?e:e.toLowerCase();for(var i=0,o=[],c=0,a=0,s=t.length;a-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function ve(e,t){t.score=e.score}function ge(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?I.includeMatches:r,o=n.includeScore,c=void 0===o?I.includeScore:o,a=[];return i&&a.push(de),c&&a.push(ve),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return a.length&&a.forEach((function(t){t(e,r)})),r}))}var ye=function(){function e(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=arguments.length>2?arguments[2]:void 0;r(this,e),this.options=t(t({},I),i),this.options.useExtendedSearch,this._keyStore=new S(this.options.keys),this.setCollection(n,o)}return o(e,[{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof $))throw new Error("Incorrect 'index' type");this._myIndex=t||R(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}},{key:"add",value:function(e){k(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=0,r=this._docs.length;n1&&void 0!==arguments[1]?arguments[1]:{},n=t.limit,r=void 0===n?-1:n,i=this.options,o=i.includeMatches,c=i.includeScore,a=i.shouldSort,s=i.sortFn,u=i.ignoreFieldNorm,h=g(e)?g(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return le(h,{ignoreFieldNorm:u}),a&&h.sort(s),y(r)&&r>-1&&(h=h.slice(0,r)),ge(h,this._docs,{includeMatches:o,includeScore:c})}},{key:"_searchStringList",value:function(e){var t=re(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,i=e.i,o=e.n;if(k(n)){var c=t.searchIn(n),a=c.isMatch,s=c.score,u=c.indices;a&&r.push({item:n,idx:i,matches:[{score:s,value:n,norm:o,indices:u}]})}})),r}},{key:"_searchLogical",value:function(e){var t=this,n=function(e,t){var n=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).auto,r=void 0===n||n,i=function e(n){var i=Object.keys(n),o=ue(n);if(!o&&i.length>1&&!se(n))return e(fe(n));if(he(n)){var c=o?n[ce]:i[0],a=o?n[ae]:n[c];if(!g(a))throw new Error(x(c));var s={keyId:j(c),pattern:a};return r&&(s.searcher=re(a,t)),s}var u={children:[],operator:i[0]};return i.forEach((function(t){var r=n[t];v(r)&&r.forEach((function(t){u.children.push(e(t))}))})),u};return se(e)||(e=fe(e)),i(e)}(e,this.options),r=function e(n,r,i){if(!n.children){var o=n.keyId,c=n.searcher,a=t._findMatches({key:t._keyStore.get(o),value:t._myIndex.getValueForItemAtKeyId(r,o),searcher:c});return a&&a.length?[{idx:i,item:r,matches:a}]:[]}for(var s=[],u=0,h=n.children.length;u1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?I.getFn:n,i=t.fieldNormWeight,o=void 0===i?I.fieldNormWeight:i,c=e.keys,a=e.records,s=new $({getFn:r,fieldNormWeight:o});return s.setKeys(c),s.setIndexRecords(a),s},ye.config=I,function(){ne.push.apply(ne,arguments)}(te),ye},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Fuse=t(); \ No newline at end of file diff --git a/docs/site_libs/quarto-search/quarto-search.js b/docs/site_libs/quarto-search/quarto-search.js deleted file mode 100644 index 6fd4b5b..0000000 --- a/docs/site_libs/quarto-search/quarto-search.js +++ /dev/null @@ -1,1123 +0,0 @@ -const kQueryArg = "q"; -const kResultsArg = "show-results"; - -// If items don't provide a URL, then both the navigator and the onSelect -// function aren't called (and therefore, the default implementation is used) -// -// We're using this sentinel URL to signal to those handlers that this -// item is a more item (along with the type) and can be handled appropriately -const kItemTypeMoreHref = "0767FDFD-0422-4E5A-BC8A-3BE11E5BBA05"; - -window.document.addEventListener("DOMContentLoaded", function (_event) { - // Ensure that search is available on this page. If it isn't, - // should return early and not do anything - var searchEl = window.document.getElementById("quarto-search"); - if (!searchEl) return; - - const { autocomplete } = window["@algolia/autocomplete-js"]; - - let quartoSearchOptions = {}; - let language = {}; - const searchOptionEl = window.document.getElementById( - "quarto-search-options" - ); - if (searchOptionEl) { - const jsonStr = searchOptionEl.textContent; - quartoSearchOptions = JSON.parse(jsonStr); - language = quartoSearchOptions.language; - } - - // note the search mode - if (quartoSearchOptions.type === "overlay") { - searchEl.classList.add("type-overlay"); - } else { - searchEl.classList.add("type-textbox"); - } - - // Used to determine highlighting behavior for this page - // A `q` query param is expected when the user follows a search - // to this page - const currentUrl = new URL(window.location); - const query = currentUrl.searchParams.get(kQueryArg); - const showSearchResults = currentUrl.searchParams.get(kResultsArg); - const mainEl = window.document.querySelector("main"); - - // highlight matches on the page - if (query !== null && mainEl) { - // perform any highlighting - highlight(query, mainEl); - - // fix up the URL to remove the q query param - const replacementUrl = new URL(window.location); - replacementUrl.searchParams.delete(kQueryArg); - window.history.replaceState({}, "", replacementUrl); - } - - // function to clear highlighting on the page when the search query changes - // (e.g. if the user edits the query or clears it) - let highlighting = true; - const resetHighlighting = (searchTerm) => { - if (mainEl && highlighting && query !== null && searchTerm !== query) { - clearHighlight(query, mainEl); - highlighting = false; - } - }; - - // Clear search highlighting when the user scrolls sufficiently - const resetFn = () => { - resetHighlighting(""); - window.removeEventListener("quarto-hrChanged", resetFn); - window.removeEventListener("quarto-sectionChanged", resetFn); - }; - - // Register this event after the initial scrolling and settling of events - // on the page - window.addEventListener("quarto-hrChanged", resetFn); - window.addEventListener("quarto-sectionChanged", resetFn); - - // Responsively switch to overlay mode if the search is present on the navbar - // Note that switching the sidebar to overlay mode requires more coordinate (not just - // the media query since we generate different HTML for sidebar overlays than we do - // for sidebar input UI) - const detachedMediaQuery = - quartoSearchOptions.type === "overlay" - ? "all" - : quartoSearchOptions.location === "navbar" - ? "(max-width: 991px)" - : "none"; - - // If configured, include the analytics client to send insights - const plugins = configurePlugins(quartoSearchOptions); - - let lastState = null; - const { setIsOpen } = autocomplete({ - container: searchEl, - detachedMediaQuery: detachedMediaQuery, - defaultActiveItemId: 0, - panelContainer: "#quarto-search-results", - panelPlacement: quartoSearchOptions["panel-placement"], - debug: false, - plugins, - classNames: { - form: "d-flex", - }, - translations: { - clearButtonTitle: language["search-clear-button-title"], - detachedCancelButtonText: language["search-detached-cancel-button-title"], - submitButtonTitle: language["search-submit-button-title"], - }, - initialState: { - query, - }, - getItemUrl({ item }) { - return item.href; - }, - onStateChange({ state }) { - // Perhaps reset highlighting - resetHighlighting(state.query); - - // If the panel just opened, ensure the panel is positioned properly - if (state.isOpen) { - if (lastState && !lastState.isOpen) { - setTimeout(() => { - positionPanel(quartoSearchOptions["panel-placement"]); - }, 150); - } - } - - // Perhaps show the copy link - showCopyLink(state.query, quartoSearchOptions); - - lastState = state; - }, - reshape({ sources, state }) { - return sources.map((source) => { - try { - const items = source.getItems(); - - // Validate the items - validateItems(items); - - // group the items by document - const groupedItems = new Map(); - items.forEach((item) => { - const hrefParts = item.href.split("#"); - const baseHref = hrefParts[0]; - const isDocumentItem = hrefParts.length === 1; - - const items = groupedItems.get(baseHref); - if (!items) { - groupedItems.set(baseHref, [item]); - } else { - // If the href for this item matches the document - // exactly, place this item first as it is the item that represents - // the document itself - if (isDocumentItem) { - items.unshift(item); - } else { - items.push(item); - } - groupedItems.set(baseHref, items); - } - }); - - const reshapedItems = []; - let count = 1; - for (const [_key, value] of groupedItems) { - const firstItem = value[0]; - reshapedItems.push({ - ...firstItem, - type: kItemTypeDoc, - }); - - const collapseMatches = quartoSearchOptions["collapse-after"]; - const collapseCount = - typeof collapseMatches === "number" ? collapseMatches : 1; - - if (value.length > 1) { - const target = `search-more-${count}`; - const isExpanded = - state.context.expanded && - state.context.expanded.includes(target); - - const remainingCount = value.length - collapseCount; - - for (let i = 1; i < value.length; i++) { - if (collapseMatches && i === collapseCount) { - reshapedItems.push({ - target, - title: isExpanded - ? language["search-hide-matches-text"] - : remainingCount === 1 - ? `${remainingCount} ${language["search-more-match-text"]}` - : `${remainingCount} ${language["search-more-matches-text"]}`, - type: kItemTypeMore, - href: kItemTypeMoreHref, - }); - } - - if (isExpanded || !collapseMatches || i < collapseCount) { - reshapedItems.push({ - ...value[i], - type: kItemTypeItem, - target, - }); - } - } - } - count += 1; - } - - return { - ...source, - getItems() { - return reshapedItems; - }, - }; - } catch (error) { - // Some form of error occurred - return { - ...source, - getItems() { - return [ - { - title: error.name || "An Error Occurred While Searching", - text: - error.message || - "An unknown error occurred while attempting to perform the requested search.", - type: kItemTypeError, - }, - ]; - }, - }; - } - }); - }, - navigator: { - navigate({ itemUrl }) { - if (itemUrl !== offsetURL(kItemTypeMoreHref)) { - window.location.assign(itemUrl); - } - }, - navigateNewTab({ itemUrl }) { - if (itemUrl !== offsetURL(kItemTypeMoreHref)) { - const windowReference = window.open(itemUrl, "_blank", "noopener"); - if (windowReference) { - windowReference.focus(); - } - } - }, - navigateNewWindow({ itemUrl }) { - if (itemUrl !== offsetURL(kItemTypeMoreHref)) { - window.open(itemUrl, "_blank", "noopener"); - } - }, - }, - getSources({ state, setContext, setActiveItemId, refresh }) { - return [ - { - sourceId: "documents", - getItemUrl({ item }) { - if (item.href) { - return offsetURL(item.href); - } else { - return undefined; - } - }, - onSelect({ - item, - state, - setContext, - setIsOpen, - setActiveItemId, - refresh, - }) { - if (item.type === kItemTypeMore) { - toggleExpanded(item, state, setContext, setActiveItemId, refresh); - - // Toggle more - setIsOpen(true); - } - }, - getItems({ query }) { - const limit = quartoSearchOptions.limit; - if (quartoSearchOptions.algolia) { - return algoliaSearch(query, limit, quartoSearchOptions.algolia); - } else { - // Fuse search options - const fuseSearchOptions = { - isCaseSensitive: false, - shouldSort: true, - minMatchCharLength: 2, - limit: limit, - }; - - return readSearchData().then(function (fuse) { - return fuseSearch(query, fuse, fuseSearchOptions); - }); - } - }, - templates: { - noResults({ createElement }) { - return createElement( - "div", - { class: "quarto-search-no-results" }, - language["search-no-results-text"] - ); - }, - header({ items, createElement }) { - // count the documents - const count = items.filter((item) => { - return item.type === kItemTypeDoc; - }).length; - - if (count > 0) { - return createElement( - "div", - { class: "search-result-header" }, - `${count} ${language["search-matching-documents-text"]}` - ); - } else { - return createElement( - "div", - { class: "search-result-header-no-results" }, - `` - ); - } - }, - footer({ _items, createElement }) { - if ( - quartoSearchOptions.algolia && - quartoSearchOptions.algolia["show-logo"] - ) { - const libDir = quartoSearchOptions.algolia["libDir"]; - const logo = createElement("img", { - src: offsetURL( - `${libDir}/quarto-search/search-by-algolia.svg` - ), - class: "algolia-search-logo", - }); - return createElement( - "a", - { href: "http://www.algolia.com/" }, - logo - ); - } - }, - - item({ item, createElement }) { - return renderItem( - item, - createElement, - state, - setActiveItemId, - setContext, - refresh - ); - }, - }, - }, - ]; - }, - }); - - // Remove the labeleledby attribute since it is pointing - // to a non-existent label - if (quartoSearchOptions.type === "overlay") { - const inputEl = window.document.querySelector( - "#quarto-search .aa-Autocomplete" - ); - if (inputEl) { - inputEl.removeAttribute("aria-labelledby"); - } - } - - // If the main document scrolls dismiss the search results - // (otherwise, since they're floating in the document they can scroll with the document) - window.document.body.onscroll = () => { - setIsOpen(false); - }; - - if (showSearchResults) { - setIsOpen(true); - focusSearchInput(); - } -}); - -function configurePlugins(quartoSearchOptions) { - const autocompletePlugins = []; - const algoliaOptions = quartoSearchOptions.algolia; - if ( - algoliaOptions && - algoliaOptions["analytics-events"] && - algoliaOptions["search-only-api-key"] && - algoliaOptions["application-id"] - ) { - const apiKey = algoliaOptions["search-only-api-key"]; - const appId = algoliaOptions["application-id"]; - - // Aloglia insights may not be loaded because they require cookie consent - // Use deferred loading so events will start being recorded when/if consent - // is granted. - const algoliaInsightsDeferredPlugin = deferredLoadPlugin(() => { - if ( - window.aa && - window["@algolia/autocomplete-plugin-algolia-insights"] - ) { - window.aa("init", { - appId, - apiKey, - useCookie: true, - }); - - const { createAlgoliaInsightsPlugin } = - window["@algolia/autocomplete-plugin-algolia-insights"]; - // Register the insights client - const algoliaInsightsPlugin = createAlgoliaInsightsPlugin({ - insightsClient: window.aa, - onItemsChange({ insights, insightsEvents }) { - const events = insightsEvents.map((event) => { - const maxEvents = event.objectIDs.slice(0, 20); - return { - ...event, - objectIDs: maxEvents, - }; - }); - - insights.viewedObjectIDs(...events); - }, - }); - return algoliaInsightsPlugin; - } - }); - - // Add the plugin - autocompletePlugins.push(algoliaInsightsDeferredPlugin); - return autocompletePlugins; - } -} - -// For plugins that may not load immediately, create a wrapper -// plugin and forward events and plugin data once the plugin -// is initialized. This is useful for cases like cookie consent -// which may prevent the analytics insights event plugin from initializing -// immediately. -function deferredLoadPlugin(createPlugin) { - let plugin = undefined; - let subscribeObj = undefined; - const wrappedPlugin = () => { - if (!plugin && subscribeObj) { - plugin = createPlugin(); - if (plugin && plugin.subscribe) { - plugin.subscribe(subscribeObj); - } - } - return plugin; - }; - - return { - subscribe: (obj) => { - subscribeObj = obj; - }, - onStateChange: (obj) => { - const plugin = wrappedPlugin(); - if (plugin && plugin.onStateChange) { - plugin.onStateChange(obj); - } - }, - onSubmit: (obj) => { - const plugin = wrappedPlugin(); - if (plugin && plugin.onSubmit) { - plugin.onSubmit(obj); - } - }, - onReset: (obj) => { - const plugin = wrappedPlugin(); - if (plugin && plugin.onReset) { - plugin.onReset(obj); - } - }, - getSources: (obj) => { - const plugin = wrappedPlugin(); - if (plugin && plugin.getSources) { - return plugin.getSources(obj); - } else { - return Promise.resolve([]); - } - }, - data: (obj) => { - const plugin = wrappedPlugin(); - if (plugin && plugin.data) { - plugin.data(obj); - } - }, - }; -} - -function validateItems(items) { - // Validate the first item - if (items.length > 0) { - const item = items[0]; - const missingFields = []; - if (item.href == undefined) { - missingFields.push("href"); - } - if (!item.title == undefined) { - missingFields.push("title"); - } - if (!item.text == undefined) { - missingFields.push("text"); - } - - if (missingFields.length === 1) { - throw { - name: `Error: Search index is missing the ${missingFields[0]} field.`, - message: `The items being returned for this search do not include all the required fields. Please ensure that your index items include the ${missingFields[0]} field or use index-fields in your _quarto.yml file to specify the field names.`, - }; - } else if (missingFields.length > 1) { - const missingFieldList = missingFields - .map((field) => { - return `${field}`; - }) - .join(", "); - - throw { - name: `Error: Search index is missing the following fields: ${missingFieldList}.`, - message: `The items being returned for this search do not include all the required fields. Please ensure that your index items includes the following fields: ${missingFieldList}, or use index-fields in your _quarto.yml file to specify the field names.`, - }; - } - } -} - -let lastQuery = null; -function showCopyLink(query, options) { - const language = options.language; - lastQuery = query; - // Insert share icon - const inputSuffixEl = window.document.body.querySelector( - ".aa-Form .aa-InputWrapperSuffix" - ); - - if (inputSuffixEl) { - let copyButtonEl = window.document.body.querySelector( - ".aa-Form .aa-InputWrapperSuffix .aa-CopyButton" - ); - - if (copyButtonEl === null) { - copyButtonEl = window.document.createElement("button"); - copyButtonEl.setAttribute("class", "aa-CopyButton"); - copyButtonEl.setAttribute("type", "button"); - copyButtonEl.setAttribute("title", language["search-copy-link-title"]); - copyButtonEl.onmousedown = (e) => { - e.preventDefault(); - e.stopPropagation(); - }; - - const linkIcon = "bi-clipboard"; - const checkIcon = "bi-check2"; - - const shareIconEl = window.document.createElement("i"); - shareIconEl.setAttribute("class", `bi ${linkIcon}`); - copyButtonEl.appendChild(shareIconEl); - inputSuffixEl.prepend(copyButtonEl); - - const clipboard = new window.ClipboardJS(".aa-CopyButton", { - text: function (_trigger) { - const copyUrl = new URL(window.location); - copyUrl.searchParams.set(kQueryArg, lastQuery); - copyUrl.searchParams.set(kResultsArg, "1"); - return copyUrl.toString(); - }, - }); - clipboard.on("success", function (e) { - // Focus the input - - // button target - const button = e.trigger; - const icon = button.querySelector("i.bi"); - - // flash "checked" - icon.classList.add(checkIcon); - icon.classList.remove(linkIcon); - setTimeout(function () { - icon.classList.remove(checkIcon); - icon.classList.add(linkIcon); - }, 1000); - }); - } - - // If there is a query, show the link icon - if (copyButtonEl) { - if (lastQuery && options["copy-button"]) { - copyButtonEl.style.display = "flex"; - } else { - copyButtonEl.style.display = "none"; - } - } - } -} - -/* Search Index Handling */ -// create the index -var fuseIndex = undefined; -async function readSearchData() { - // Initialize the search index on demand - if (fuseIndex === undefined) { - // create fuse index - const options = { - keys: [ - { name: "title", weight: 20 }, - { name: "section", weight: 20 }, - { name: "text", weight: 10 }, - ], - ignoreLocation: true, - threshold: 0.1, - }; - const fuse = new window.Fuse([], options); - - // fetch the main search.json - const response = await fetch(offsetURL("search.json")); - if (response.status == 200) { - return response.json().then(function (searchDocs) { - searchDocs.forEach(function (searchDoc) { - fuse.add(searchDoc); - }); - fuseIndex = fuse; - return fuseIndex; - }); - } else { - return Promise.reject( - new Error( - "Unexpected status from search index request: " + response.status - ) - ); - } - } - return fuseIndex; -} - -function inputElement() { - return window.document.body.querySelector(".aa-Form .aa-Input"); -} - -function focusSearchInput() { - setTimeout(() => { - const inputEl = inputElement(); - if (inputEl) { - inputEl.focus(); - } - }, 50); -} - -/* Panels */ -const kItemTypeDoc = "document"; -const kItemTypeMore = "document-more"; -const kItemTypeItem = "document-item"; -const kItemTypeError = "error"; - -function renderItem( - item, - createElement, - state, - setActiveItemId, - setContext, - refresh -) { - switch (item.type) { - case kItemTypeDoc: - return createDocumentCard( - createElement, - "file-richtext", - item.title, - item.section, - item.text, - item.href - ); - case kItemTypeMore: - return createMoreCard( - createElement, - item, - state, - setActiveItemId, - setContext, - refresh - ); - case kItemTypeItem: - return createSectionCard( - createElement, - item.section, - item.text, - item.href - ); - case kItemTypeError: - return createErrorCard(createElement, item.title, item.text); - default: - return undefined; - } -} - -function createDocumentCard(createElement, icon, title, section, text, href) { - const iconEl = createElement("i", { - class: `bi bi-${icon} search-result-icon`, - }); - const titleEl = createElement("p", { class: "search-result-title" }, title); - const titleContainerEl = createElement( - "div", - { class: "search-result-title-container" }, - [iconEl, titleEl] - ); - - const textEls = []; - if (section) { - const sectionEl = createElement( - "p", - { class: "search-result-section" }, - section - ); - textEls.push(sectionEl); - } - const descEl = createElement("p", { - class: "search-result-text", - dangerouslySetInnerHTML: { - __html: text, - }, - }); - textEls.push(descEl); - - const textContainerEl = createElement( - "div", - { class: "search-result-text-container" }, - textEls - ); - - const containerEl = createElement( - "div", - { - class: "search-result-container", - }, - [titleContainerEl, textContainerEl] - ); - - const linkEl = createElement( - "a", - { - href: offsetURL(href), - class: "search-result-link", - }, - containerEl - ); - - const classes = ["search-result-doc", "search-item"]; - if (!section) { - classes.push("document-selectable"); - } - - return createElement( - "div", - { - class: classes.join(" "), - }, - linkEl - ); -} - -function createMoreCard( - createElement, - item, - state, - setActiveItemId, - setContext, - refresh -) { - const moreCardEl = createElement( - "div", - { - class: "search-result-more search-item", - onClick: (e) => { - // Handle expanding the sections by adding the expanded - // section to the list of expanded sections - toggleExpanded(item, state, setContext, setActiveItemId, refresh); - e.stopPropagation(); - }, - }, - item.title - ); - - return moreCardEl; -} - -function toggleExpanded(item, state, setContext, setActiveItemId, refresh) { - const expanded = state.context.expanded || []; - if (expanded.includes(item.target)) { - setContext({ - expanded: expanded.filter((target) => target !== item.target), - }); - } else { - setContext({ expanded: [...expanded, item.target] }); - } - - refresh(); - setActiveItemId(item.__autocomplete_id); -} - -function createSectionCard(createElement, section, text, href) { - const sectionEl = createSection(createElement, section, text, href); - return createElement( - "div", - { - class: "search-result-doc-section search-item", - }, - sectionEl - ); -} - -function createSection(createElement, title, text, href) { - const descEl = createElement("p", { - class: "search-result-text", - dangerouslySetInnerHTML: { - __html: text, - }, - }); - - const titleEl = createElement("p", { class: "search-result-section" }, title); - const linkEl = createElement( - "a", - { - href: offsetURL(href), - class: "search-result-link", - }, - [titleEl, descEl] - ); - return linkEl; -} - -function createErrorCard(createElement, title, text) { - const descEl = createElement("p", { - class: "search-error-text", - dangerouslySetInnerHTML: { - __html: text, - }, - }); - - const titleEl = createElement("p", { - class: "search-error-title", - dangerouslySetInnerHTML: { - __html: ` ${title}`, - }, - }); - const errorEl = createElement("div", { class: "search-error" }, [ - titleEl, - descEl, - ]); - return errorEl; -} - -function positionPanel(pos) { - const panelEl = window.document.querySelector( - "#quarto-search-results .aa-Panel" - ); - const inputEl = window.document.querySelector( - "#quarto-search .aa-Autocomplete" - ); - - if (panelEl && inputEl) { - panelEl.style.top = `${Math.round(panelEl.offsetTop)}px`; - if (pos === "start") { - panelEl.style.left = `${Math.round(inputEl.left)}px`; - } else { - panelEl.style.right = `${Math.round(inputEl.offsetRight)}px`; - } - } -} - -/* Highlighting */ -// highlighting functions -function highlightMatch(query, text) { - if (text) { - const start = text.toLowerCase().indexOf(query.toLowerCase()); - if (start !== -1) { - const startMark = ""; - const endMark = ""; - - const end = start + query.length; - text = - text.slice(0, start) + - startMark + - text.slice(start, end) + - endMark + - text.slice(end); - const startInfo = clipStart(text, start); - const endInfo = clipEnd( - text, - startInfo.position + startMark.length + endMark.length - ); - text = - startInfo.prefix + - text.slice(startInfo.position, endInfo.position) + - endInfo.suffix; - - return text; - } else { - return text; - } - } else { - return text; - } -} - -function clipStart(text, pos) { - const clipStart = pos - 50; - if (clipStart < 0) { - // This will just return the start of the string - return { - position: 0, - prefix: "", - }; - } else { - // We're clipping before the start of the string, walk backwards to the first space. - const spacePos = findSpace(text, pos, -1); - return { - position: spacePos.position, - prefix: "", - }; - } -} - -function clipEnd(text, pos) { - const clipEnd = pos + 200; - if (clipEnd > text.length) { - return { - position: text.length, - suffix: "", - }; - } else { - const spacePos = findSpace(text, clipEnd, 1); - return { - position: spacePos.position, - suffix: spacePos.clipped ? "…" : "", - }; - } -} - -function findSpace(text, start, step) { - let stepPos = start; - while (stepPos > -1 && stepPos < text.length) { - const char = text[stepPos]; - if (char === " " || char === "," || char === ":") { - return { - position: step === 1 ? stepPos : stepPos - step, - clipped: stepPos > 1 && stepPos < text.length, - }; - } - stepPos = stepPos + step; - } - - return { - position: stepPos - step, - clipped: false, - }; -} - -// removes highlighting as implemented by the mark tag -function clearHighlight(searchterm, el) { - const childNodes = el.childNodes; - for (let i = childNodes.length - 1; i >= 0; i--) { - const node = childNodes[i]; - if (node.nodeType === Node.ELEMENT_NODE) { - if ( - node.tagName === "MARK" && - node.innerText.toLowerCase() === searchterm.toLowerCase() - ) { - el.replaceChild(document.createTextNode(node.innerText), node); - } else { - clearHighlight(searchterm, node); - } - } - } -} - -// highlight matches -function highlight(term, el) { - const termRegex = new RegExp(term, "ig"); - const childNodes = el.childNodes; - - // walk back to front avoid mutating elements in front of us - for (let i = childNodes.length - 1; i >= 0; i--) { - const node = childNodes[i]; - - if (node.nodeType === Node.TEXT_NODE) { - // Search text nodes for text to highlight - const text = node.nodeValue; - - let startIndex = 0; - let matchIndex = text.search(termRegex); - if (matchIndex > -1) { - const markFragment = document.createDocumentFragment(); - while (matchIndex > -1) { - const prefix = text.slice(startIndex, matchIndex); - markFragment.appendChild(document.createTextNode(prefix)); - - const mark = document.createElement("mark"); - mark.appendChild( - document.createTextNode( - text.slice(matchIndex, matchIndex + term.length) - ) - ); - markFragment.appendChild(mark); - - startIndex = matchIndex + term.length; - matchIndex = text.slice(startIndex).search(new RegExp(term, "ig")); - if (matchIndex > -1) { - matchIndex = startIndex + matchIndex; - } - } - if (startIndex < text.length) { - markFragment.appendChild( - document.createTextNode(text.slice(startIndex, text.length)) - ); - } - - el.replaceChild(markFragment, node); - } - } else if (node.nodeType === Node.ELEMENT_NODE) { - // recurse through elements - highlight(term, node); - } - } -} - -/* Link Handling */ -// get the offset from this page for a given site root relative url -function offsetURL(url) { - var offset = getMeta("quarto:offset"); - return offset ? offset + url : url; -} - -// read a meta tag value -function getMeta(metaName) { - var metas = window.document.getElementsByTagName("meta"); - for (let i = 0; i < metas.length; i++) { - if (metas[i].getAttribute("name") === metaName) { - return metas[i].getAttribute("content"); - } - } - return ""; -} - -function algoliaSearch(query, limit, algoliaOptions) { - const { getAlgoliaResults } = window["@algolia/autocomplete-preset-algolia"]; - - const applicationId = algoliaOptions["application-id"]; - const searchOnlyApiKey = algoliaOptions["search-only-api-key"]; - const indexName = algoliaOptions["index-name"]; - const indexFields = algoliaOptions["index-fields"]; - const searchClient = window.algoliasearch(applicationId, searchOnlyApiKey); - const searchParams = algoliaOptions["params"]; - const searchAnalytics = !!algoliaOptions["analytics-events"]; - - return getAlgoliaResults({ - searchClient, - queries: [ - { - indexName: indexName, - query, - params: { - hitsPerPage: limit, - clickAnalytics: searchAnalytics, - ...searchParams, - }, - }, - ], - transformResponse: (response) => { - if (!indexFields) { - return response.hits.map((hit) => { - return hit.map((item) => { - return { - ...item, - text: highlightMatch(query, item.text), - }; - }); - }); - } else { - const remappedHits = response.hits.map((hit) => { - return hit.map((item) => { - const newItem = { ...item }; - ["href", "section", "title", "text"].forEach((keyName) => { - const mappedName = indexFields[keyName]; - if ( - mappedName && - item[mappedName] !== undefined && - mappedName !== keyName - ) { - newItem[keyName] = item[mappedName]; - delete newItem[mappedName]; - } - }); - newItem.text = highlightMatch(query, newItem.text); - return newItem; - }); - }); - return remappedHits; - } - }, - }); -} - -function fuseSearch(query, fuse, fuseOptions) { - return fuse.search(query, fuseOptions).map((result) => { - const addParam = (url, name, value) => { - const anchorParts = url.split("#"); - const baseUrl = anchorParts[0]; - const sep = baseUrl.search("\\?") > 0 ? "&" : "?"; - anchorParts[0] = baseUrl + sep + name + "=" + value; - return anchorParts.join("#"); - }; - - return { - title: result.item.title, - section: result.item.section, - href: addParam(result.item.href, kQueryArg, query), - text: highlightMatch(query, result.item.text), - }; - }); -} diff --git a/docs/styles.css b/docs/styles.css deleted file mode 100644 index 2ddf50c..0000000 --- a/docs/styles.css +++ /dev/null @@ -1 +0,0 @@ -/* css styles */ diff --git a/how_many_iterations_cache/html/__packages b/how_many_iterations_cache/html/__packages deleted file mode 100644 index 1d3cd54..0000000 --- a/how_many_iterations_cache/html/__packages +++ /dev/null @@ -1,6 +0,0 @@ -colorDF -prettycode -RcppArmadillo -ggplot2 -patchwork -pwr diff --git a/how_many_iterations_cache/html/setup_1290329524ce8ec1243e6170291eb043.RData b/how_many_iterations_cache/html/setup_1290329524ce8ec1243e6170291eb043.RData deleted file mode 100644 index 1bab77f..0000000 Binary files a/how_many_iterations_cache/html/setup_1290329524ce8ec1243e6170291eb043.RData and /dev/null differ diff --git a/how_many_iterations_cache/html/setup_1290329524ce8ec1243e6170291eb043.rdb b/how_many_iterations_cache/html/setup_1290329524ce8ec1243e6170291eb043.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/how_many_iterations_cache/html/setup_1290329524ce8ec1243e6170291eb043.rdx b/how_many_iterations_cache/html/setup_1290329524ce8ec1243e6170291eb043.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/how_many_iterations_cache/html/setup_1290329524ce8ec1243e6170291eb043.rdx and /dev/null differ diff --git a/how_many_iterations_cache/html/unnamed-chunk-1_6a02df036426117b8bdb689e55dd6b0e.RData b/how_many_iterations_cache/html/unnamed-chunk-1_6a02df036426117b8bdb689e55dd6b0e.RData deleted file mode 100644 index 4e6dad7..0000000 Binary files a/how_many_iterations_cache/html/unnamed-chunk-1_6a02df036426117b8bdb689e55dd6b0e.RData and /dev/null differ diff --git a/how_many_iterations_cache/html/unnamed-chunk-1_6a02df036426117b8bdb689e55dd6b0e.rdb b/how_many_iterations_cache/html/unnamed-chunk-1_6a02df036426117b8bdb689e55dd6b0e.rdb deleted file mode 100644 index bfad1d8..0000000 Binary files a/how_many_iterations_cache/html/unnamed-chunk-1_6a02df036426117b8bdb689e55dd6b0e.rdb and /dev/null differ diff --git a/how_many_iterations_cache/html/unnamed-chunk-1_6a02df036426117b8bdb689e55dd6b0e.rdx b/how_many_iterations_cache/html/unnamed-chunk-1_6a02df036426117b8bdb689e55dd6b0e.rdx deleted file mode 100644 index 6f4d141..0000000 Binary files a/how_many_iterations_cache/html/unnamed-chunk-1_6a02df036426117b8bdb689e55dd6b0e.rdx and /dev/null differ diff --git a/how_many_iterations_cache/html/unnamed-chunk-2_42dd9943c648d4afe64e75cbcb280efc.RData b/how_many_iterations_cache/html/unnamed-chunk-2_42dd9943c648d4afe64e75cbcb280efc.RData deleted file mode 100644 index b872e55..0000000 Binary files a/how_many_iterations_cache/html/unnamed-chunk-2_42dd9943c648d4afe64e75cbcb280efc.RData and /dev/null differ diff --git a/how_many_iterations_cache/html/unnamed-chunk-2_42dd9943c648d4afe64e75cbcb280efc.rdb b/how_many_iterations_cache/html/unnamed-chunk-2_42dd9943c648d4afe64e75cbcb280efc.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/how_many_iterations_cache/html/unnamed-chunk-2_42dd9943c648d4afe64e75cbcb280efc.rdx b/how_many_iterations_cache/html/unnamed-chunk-2_42dd9943c648d4afe64e75cbcb280efc.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/how_many_iterations_cache/html/unnamed-chunk-2_42dd9943c648d4afe64e75cbcb280efc.rdx and /dev/null differ diff --git a/how_many_iterations_cache/html/unnamed-chunk-3_d766d59674155b81cd62e963709ed2d9.RData b/how_many_iterations_cache/html/unnamed-chunk-3_d766d59674155b81cd62e963709ed2d9.RData deleted file mode 100644 index 252e0fd..0000000 Binary files a/how_many_iterations_cache/html/unnamed-chunk-3_d766d59674155b81cd62e963709ed2d9.RData and /dev/null differ diff --git a/how_many_iterations_cache/html/unnamed-chunk-3_d766d59674155b81cd62e963709ed2d9.rdb b/how_many_iterations_cache/html/unnamed-chunk-3_d766d59674155b81cd62e963709ed2d9.rdb deleted file mode 100644 index 062d609..0000000 Binary files a/how_many_iterations_cache/html/unnamed-chunk-3_d766d59674155b81cd62e963709ed2d9.rdb and /dev/null differ diff --git a/how_many_iterations_cache/html/unnamed-chunk-3_d766d59674155b81cd62e963709ed2d9.rdx b/how_many_iterations_cache/html/unnamed-chunk-3_d766d59674155b81cd62e963709ed2d9.rdx deleted file mode 100644 index 4455b2f..0000000 Binary files a/how_many_iterations_cache/html/unnamed-chunk-3_d766d59674155b81cd62e963709ed2d9.rdx and /dev/null differ diff --git a/how_many_iterations_files/figure-html/unnamed-chunk-3-1.png b/how_many_iterations_files/figure-html/unnamed-chunk-3-1.png deleted file mode 100644 index 592b7b5..0000000 Binary files a/how_many_iterations_files/figure-html/unnamed-chunk-3-1.png and /dev/null differ diff --git a/images/LMU-OSC_favicon.jpg b/images/LMU-OSC_favicon.jpg new file mode 100644 index 0000000..a06a839 Binary files /dev/null and b/images/LMU-OSC_favicon.jpg differ diff --git a/docs/images/LMU-OSC_logo_small.jpg b/images/LMU-OSC_logo.jpg similarity index 100% rename from docs/images/LMU-OSC_logo_small.jpg rename to images/LMU-OSC_logo.jpg diff --git a/includes/matomo.html b/includes/matomo.html index c38ab4c..63de700 100644 --- a/includes/matomo.html +++ b/includes/matomo.html @@ -3,7 +3,7 @@ var _paq = window._paq = window._paq || []; /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(["setDocumentTitle", document.domain + "/" + document.title]); - _paq.push(["setCookieDomain", "*.malikaihle.github.io"]); + _paq.push(["setCookieDomain", "*.lmu-osc.github.io"]); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { diff --git a/index.qmd b/index.qmd index 0fa4acd..f799295 100644 --- a/index.qmd +++ b/index.qmd @@ -1,92 +1,92 @@ ---- -title: "Simulations for Advanced Power Analyses" -format: html -project-type: website ---- - -## About this work - -This tutorial was created by **Felix Schönbrodt** and **Moritz Fischer**, with contributions from Malika Ihle, to be part of the training offering of the Ludwig-Maximilian University Open Science Center in Munich. - -::: {.callout-note} -This book is still being developed. If you have comment to contribute to its improvement, you can submit pull requests in the respective .qmd file of the [source repository](https://github.com/MalikaIhle/Simulations-for-Advanced-Power-Analyses) by clicking on the 'Edit this page' and 'Report an issue' in the right navigation panel of each page. -::: - - -## Structure of the tutorial - -Depending on your prior knowledge, you can fast forward some steps: - -![](images/choose_your_level.png) - -### Acquire necessary basic coding skills in R - -You need to know **R programming basics**. If you are unfamiliar with R, you are advised to follow a self-paced basic tutorial prior to the workshop, e.g.: [https://www.tutorialspoint.com/r](https://www.tutorialspoint.com/r/index.htm) up to "data reshaping" (this tutorial, for example, takes around 2h and covers all necessary basics). - -For a higher-level introduction to R coding skills you can do the self-paced tutorial [Introduction to simulation in R](https://malikaihle.github.io/Introduction-Simulations-in-R/). This tutorial teaches how to simulate data and writing functions in R, with the goal to e.g. - -- check alpha so your statistical models don't yield more than 5% false-positive results -- check beta (power) for easy tests such as t-tests -- prepare a preregistration and make sure your code works -- check your understanding of statistics. - - -### Comprehensive introduction to power analyses - -Please read [Chapter 1 of the SuperpowerBook by Aaron R. Caldwell, Daniël Lakens, Chelsea M. Parlett-Pelleriti, Guy Prochilo, Frederik Aust](https://aaroncaldwell.us/SuperpowerBook/introduction-to-power-analysis.html). - -This introduction covers sample effect sizes vs population effect sizes, how to take into account the uncertainty of the sample effect size to create a safeguard effect size to be used in power analyses, why *post hoc* power analyses are pointless, and why it is better to calculate the minimal detectable effect instead. - -The rest of the Superpower book teaches how to use the `superpower` R package to simulate factorial designs and calculate power, which may be of great interest to you! In our tutorial, we chose to teach how to write simulation 'by hand' so you can understand the concept and adapt it to any of your designs. - -### Tutorial structure - -With these prerequisites, you can start to learn power calculations for different complex models. Here are the type of models we will cover, you can pick and choose what is relevant to you: - -- Ch. 1: [Linear Model I: a single dichotomous predictor](LM1.qmd)\ -- Ch. 2: [Linear Model 2: Multiple predictors](LM2.qmd)\ -- Ch. 3: [Generalized Linear Models](GLM.qmd)\ -- Ch. 4: [Linear Mixed Models](LMM.qmd)\ -- Ch. 5: [Structural Equation Modelling (SEM)](SEM.qmd) - -**We recommend that everybody works through chapters 1 and 2, and then dive into the other chapters that are relevant.** - -For each model, we will follow the structure: - -- define what type of data and variables need to be simulated, i.e. their distribution, their class (e.g. factor vs numerical value) -- generate data based on the equation of the model (data = model + error) -- run the statistical test, and record the relevant statistic (e.g. p-value) -- replicate step 2 and 3 to get the distribution of the statistic of interest (e.g. p-value) -- analyze and interpret the combined results of many simulations i.e. check for which sample size you get at a significant result in 80% of the simulations - -### Install all packages - -The following packages are necessary to reproduce the output of this tutorial. We recommend installing all of them before you dive into the individual chapters. - -```{r install all packages, message = FALSE, error = FALSE, warning=FALSE, echo=TRUE, results='hide', eval=FALSE} - -install.packages(c( - "ggplot2", - "ggdist", - "gghalves", - "pwr", - "MBESS", - "Rfast", - "DescTools", - "lme4", - "lmerTest", - "tidyr", - "Rfast", - "future.apply", - "lavaan", - "MASS"), dependencies = TRUE, repos = "https://cran.rstudio.com/") - -install.packages("devtools") -devtools::install_github("debruine/faux") -``` - -## License & Funding note - -This tutorial was initially commissioned and funded by the University of Hamburg, Faculty of Psychology and Movement Science. - -It is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/). +--- +title: "Simulations for Advanced Power Analyses" +format: html +project-type: website +--- + +## About this work + +This tutorial was created by **Felix Schönbrodt** and **Moritz Fischer**, with contributions from Malika Ihle, to be part of the training offering of the Ludwig-Maximilian University Open Science Center in Munich. + +::: {.callout-note} +This book is still being developed. If you have comment to contribute to its improvement, you can submit pull requests in the respective .qmd file of the [source repository](https://github.com/lmu-osc/Simulations-for-Advanced-Power-Analyses) by clicking on the 'Edit this page' and 'Report an issue' in the right navigation panel of each page. +::: + + +## Structure of the tutorial + +Depending on your prior knowledge, you can fast forward some steps: + +![](images/choose_your_level.png) + +### Acquire necessary basic coding skills in R + +You need to know **R programming basics**. If you are unfamiliar with R, you are advised to follow a self-paced basic tutorial prior to the workshop, e.g.: [https://www.tutorialspoint.com/r](https://www.tutorialspoint.com/r/index.htm) up to "data reshaping" (this tutorial, for example, takes around 2h and covers all necessary basics). + +For a higher-level introduction to R coding skills you can do the self-paced tutorial [Introduction to simulation in R](https://malikaihle.github.io/Introduction-Simulations-in-R/). This tutorial teaches how to simulate data and writing functions in R, with the goal to e.g. + +- check alpha so your statistical models don't yield more than 5% false-positive results +- check beta (power) for easy tests such as t-tests +- prepare a preregistration and make sure your code works +- check your understanding of statistics. + + +### Comprehensive introduction to power analyses + +Please read [Chapter 1 of the SuperpowerBook by Aaron R. Caldwell, Daniël Lakens, Chelsea M. Parlett-Pelleriti, Guy Prochilo, Frederik Aust](https://aaroncaldwell.us/SuperpowerBook/introduction-to-power-analysis.html). + +This introduction covers sample effect sizes vs population effect sizes, how to take into account the uncertainty of the sample effect size to create a safeguard effect size to be used in power analyses, why *post hoc* power analyses are pointless, and why it is better to calculate the minimal detectable effect instead. + +The rest of the Superpower book teaches how to use the `superpower` R package to simulate factorial designs and calculate power, which may be of great interest to you! In our tutorial, we chose to teach how to write simulation 'by hand' so you can understand the concept and adapt it to any of your designs. + +### Tutorial structure + +With these prerequisites, you can start to learn power calculations for different complex models. Here are the type of models we will cover, you can pick and choose what is relevant to you: + +- Ch. 1: [Linear Model I: a single dichotomous predictor](LM1.qmd)\ +- Ch. 2: [Linear Model 2: Multiple predictors](LM2.qmd)\ +- Ch. 3: [Generalized Linear Models](GLM.qmd)\ +- Ch. 4: [Linear Mixed Models](LMM.qmd)\ +- Ch. 5: [Structural Equation Modelling (SEM)](SEM.qmd) + +**We recommend that everybody works through chapters 1 and 2, and then dive into the other chapters that are relevant.** + +For each model, we will follow the structure: + +- define what type of data and variables need to be simulated, i.e. their distribution, their class (e.g. factor vs numerical value) +- generate data based on the equation of the model (data = model + error) +- run the statistical test, and record the relevant statistic (e.g. p-value) +- replicate step 2 and 3 to get the distribution of the statistic of interest (e.g. p-value) +- analyze and interpret the combined results of many simulations i.e. check for which sample size you get at a significant result in 80% of the simulations + +### Install all packages + +The following packages are necessary to reproduce the output of this tutorial. We recommend installing all of them before you dive into the individual chapters. + +```{r install all packages, message = FALSE, error = FALSE, warning=FALSE, echo=TRUE, results='hide', eval=FALSE} + +install.packages(c( + "ggplot2", + "ggdist", + "gghalves", + "pwr", + "MBESS", + "Rfast", + "DescTools", + "lme4", + "lmerTest", + "tidyr", + "Rfast", + "future.apply", + "lavaan", + "MASS"), dependencies = TRUE, repos = "https://cran.rstudio.com/") + +install.packages("devtools") +devtools::install_github("debruine/faux") +``` + +## License & Funding note + +This tutorial was initially commissioned and funded by the University of Hamburg, Faculty of Psychology and Movement Science. + +It is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/). diff --git a/index_cache/html/__packages b/index_cache/html/__packages deleted file mode 100644 index a62cc63..0000000 --- a/index_cache/html/__packages +++ /dev/null @@ -1,2 +0,0 @@ -colorDF -prettycode diff --git a/index_cache/html/install all packages_5c77cb84a510af25cae57e6192d41927.RData b/index_cache/html/install all packages_5c77cb84a510af25cae57e6192d41927.RData deleted file mode 100644 index ac9025e..0000000 Binary files a/index_cache/html/install all packages_5c77cb84a510af25cae57e6192d41927.RData and /dev/null differ diff --git a/index_cache/html/install all packages_5c77cb84a510af25cae57e6192d41927.rdb b/index_cache/html/install all packages_5c77cb84a510af25cae57e6192d41927.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/index_cache/html/install all packages_5c77cb84a510af25cae57e6192d41927.rdx b/index_cache/html/install all packages_5c77cb84a510af25cae57e6192d41927.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/index_cache/html/install all packages_5c77cb84a510af25cae57e6192d41927.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/__packages b/optimizing_code_cache/html/__packages deleted file mode 100644 index 1bbbd7f..0000000 --- a/optimizing_code_cache/html/__packages +++ /dev/null @@ -1,5 +0,0 @@ -colorDF -prettycode -RcppArmadillo -future -future.apply diff --git a/optimizing_code_cache/html/opt1_345cb673012ed4b9ea58a2a7a1b6937a.RData b/optimizing_code_cache/html/opt1_345cb673012ed4b9ea58a2a7a1b6937a.RData deleted file mode 100644 index b47438e..0000000 Binary files a/optimizing_code_cache/html/opt1_345cb673012ed4b9ea58a2a7a1b6937a.RData and /dev/null differ diff --git a/optimizing_code_cache/html/opt1_345cb673012ed4b9ea58a2a7a1b6937a.rdb b/optimizing_code_cache/html/opt1_345cb673012ed4b9ea58a2a7a1b6937a.rdb deleted file mode 100644 index 4c3d326..0000000 Binary files a/optimizing_code_cache/html/opt1_345cb673012ed4b9ea58a2a7a1b6937a.rdb and /dev/null differ diff --git a/optimizing_code_cache/html/opt1_345cb673012ed4b9ea58a2a7a1b6937a.rdx b/optimizing_code_cache/html/opt1_345cb673012ed4b9ea58a2a7a1b6937a.rdx deleted file mode 100644 index 82ac956..0000000 Binary files a/optimizing_code_cache/html/opt1_345cb673012ed4b9ea58a2a7a1b6937a.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/setup_1534fe674feb29874598b48b162a2146.RData b/optimizing_code_cache/html/setup_1534fe674feb29874598b48b162a2146.RData deleted file mode 100644 index b3e5149..0000000 Binary files a/optimizing_code_cache/html/setup_1534fe674feb29874598b48b162a2146.RData and /dev/null differ diff --git a/optimizing_code_cache/html/setup_1534fe674feb29874598b48b162a2146.rdb b/optimizing_code_cache/html/setup_1534fe674feb29874598b48b162a2146.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/optimizing_code_cache/html/setup_1534fe674feb29874598b48b162a2146.rdx b/optimizing_code_cache/html/setup_1534fe674feb29874598b48b162a2146.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/optimizing_code_cache/html/setup_1534fe674feb29874598b48b162a2146.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-10_394b43bebb072e92e25f4fd4a8d2073c.RData b/optimizing_code_cache/html/unnamed-chunk-10_394b43bebb072e92e25f4fd4a8d2073c.RData deleted file mode 100644 index 63a7838..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-10_394b43bebb072e92e25f4fd4a8d2073c.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-10_394b43bebb072e92e25f4fd4a8d2073c.rdb b/optimizing_code_cache/html/unnamed-chunk-10_394b43bebb072e92e25f4fd4a8d2073c.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/optimizing_code_cache/html/unnamed-chunk-10_394b43bebb072e92e25f4fd4a8d2073c.rdx b/optimizing_code_cache/html/unnamed-chunk-10_394b43bebb072e92e25f4fd4a8d2073c.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-10_394b43bebb072e92e25f4fd4a8d2073c.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-1_a193747e94e1472e158a93ae11a07eb6.RData b/optimizing_code_cache/html/unnamed-chunk-1_a193747e94e1472e158a93ae11a07eb6.RData deleted file mode 100644 index 3b41ae5..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-1_a193747e94e1472e158a93ae11a07eb6.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-1_a193747e94e1472e158a93ae11a07eb6.rdb b/optimizing_code_cache/html/unnamed-chunk-1_a193747e94e1472e158a93ae11a07eb6.rdb deleted file mode 100644 index c4bdff3..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-1_a193747e94e1472e158a93ae11a07eb6.rdb and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-1_a193747e94e1472e158a93ae11a07eb6.rdx b/optimizing_code_cache/html/unnamed-chunk-1_a193747e94e1472e158a93ae11a07eb6.rdx deleted file mode 100644 index 4032405..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-1_a193747e94e1472e158a93ae11a07eb6.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-2_f16144c256535abcd4c39eef8be8bd76.RData b/optimizing_code_cache/html/unnamed-chunk-2_f16144c256535abcd4c39eef8be8bd76.RData deleted file mode 100644 index d92ad00..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-2_f16144c256535abcd4c39eef8be8bd76.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-2_f16144c256535abcd4c39eef8be8bd76.rdb b/optimizing_code_cache/html/unnamed-chunk-2_f16144c256535abcd4c39eef8be8bd76.rdb deleted file mode 100644 index cca5aeb..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-2_f16144c256535abcd4c39eef8be8bd76.rdb and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-2_f16144c256535abcd4c39eef8be8bd76.rdx b/optimizing_code_cache/html/unnamed-chunk-2_f16144c256535abcd4c39eef8be8bd76.rdx deleted file mode 100644 index 2403253..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-2_f16144c256535abcd4c39eef8be8bd76.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-3_9903b1a2e2dd77f0ad04f086ef99c7e0.RData b/optimizing_code_cache/html/unnamed-chunk-3_9903b1a2e2dd77f0ad04f086ef99c7e0.RData deleted file mode 100644 index 053b36d..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-3_9903b1a2e2dd77f0ad04f086ef99c7e0.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-3_9903b1a2e2dd77f0ad04f086ef99c7e0.rdb b/optimizing_code_cache/html/unnamed-chunk-3_9903b1a2e2dd77f0ad04f086ef99c7e0.rdb deleted file mode 100644 index efa8744..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-3_9903b1a2e2dd77f0ad04f086ef99c7e0.rdb and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-3_9903b1a2e2dd77f0ad04f086ef99c7e0.rdx b/optimizing_code_cache/html/unnamed-chunk-3_9903b1a2e2dd77f0ad04f086ef99c7e0.rdx deleted file mode 100644 index 445ad84..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-3_9903b1a2e2dd77f0ad04f086ef99c7e0.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-4_c048b884f2b0aa3a182c431dc8b74516.RData b/optimizing_code_cache/html/unnamed-chunk-4_c048b884f2b0aa3a182c431dc8b74516.RData deleted file mode 100644 index 0eca3f6..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-4_c048b884f2b0aa3a182c431dc8b74516.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-4_c048b884f2b0aa3a182c431dc8b74516.rdb b/optimizing_code_cache/html/unnamed-chunk-4_c048b884f2b0aa3a182c431dc8b74516.rdb deleted file mode 100644 index 1b7c279..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-4_c048b884f2b0aa3a182c431dc8b74516.rdb and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-4_c048b884f2b0aa3a182c431dc8b74516.rdx b/optimizing_code_cache/html/unnamed-chunk-4_c048b884f2b0aa3a182c431dc8b74516.rdx deleted file mode 100644 index 9fb2532..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-4_c048b884f2b0aa3a182c431dc8b74516.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-5_83c0aec0ed94e5227c10bdf9b6bbc060.RData b/optimizing_code_cache/html/unnamed-chunk-5_83c0aec0ed94e5227c10bdf9b6bbc060.RData deleted file mode 100644 index 38f3b8c..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-5_83c0aec0ed94e5227c10bdf9b6bbc060.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-5_83c0aec0ed94e5227c10bdf9b6bbc060.rdb b/optimizing_code_cache/html/unnamed-chunk-5_83c0aec0ed94e5227c10bdf9b6bbc060.rdb deleted file mode 100644 index 16e95e9..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-5_83c0aec0ed94e5227c10bdf9b6bbc060.rdb and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-5_83c0aec0ed94e5227c10bdf9b6bbc060.rdx b/optimizing_code_cache/html/unnamed-chunk-5_83c0aec0ed94e5227c10bdf9b6bbc060.rdx deleted file mode 100644 index d3c3df3..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-5_83c0aec0ed94e5227c10bdf9b6bbc060.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-6_eaa305acfccb26f993054e871d455410.RData b/optimizing_code_cache/html/unnamed-chunk-6_eaa305acfccb26f993054e871d455410.RData deleted file mode 100644 index 0e3ff27..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-6_eaa305acfccb26f993054e871d455410.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-6_eaa305acfccb26f993054e871d455410.rdb b/optimizing_code_cache/html/unnamed-chunk-6_eaa305acfccb26f993054e871d455410.rdb deleted file mode 100644 index 06c863c..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-6_eaa305acfccb26f993054e871d455410.rdb and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-6_eaa305acfccb26f993054e871d455410.rdx b/optimizing_code_cache/html/unnamed-chunk-6_eaa305acfccb26f993054e871d455410.rdx deleted file mode 100644 index d05d00a..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-6_eaa305acfccb26f993054e871d455410.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-7_84676012e2272139bfba02e616530ffa.RData b/optimizing_code_cache/html/unnamed-chunk-7_84676012e2272139bfba02e616530ffa.RData deleted file mode 100644 index fa68239..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-7_84676012e2272139bfba02e616530ffa.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-7_84676012e2272139bfba02e616530ffa.rdb b/optimizing_code_cache/html/unnamed-chunk-7_84676012e2272139bfba02e616530ffa.rdb deleted file mode 100644 index e69de29..0000000 diff --git a/optimizing_code_cache/html/unnamed-chunk-7_84676012e2272139bfba02e616530ffa.rdx b/optimizing_code_cache/html/unnamed-chunk-7_84676012e2272139bfba02e616530ffa.rdx deleted file mode 100644 index ef6ce70..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-7_84676012e2272139bfba02e616530ffa.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-8_1dc0e9f5a17e9845a4ec965b8641f6a6.RData b/optimizing_code_cache/html/unnamed-chunk-8_1dc0e9f5a17e9845a4ec965b8641f6a6.RData deleted file mode 100644 index c271dfb..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-8_1dc0e9f5a17e9845a4ec965b8641f6a6.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-8_1dc0e9f5a17e9845a4ec965b8641f6a6.rdb b/optimizing_code_cache/html/unnamed-chunk-8_1dc0e9f5a17e9845a4ec965b8641f6a6.rdb deleted file mode 100644 index aad2394..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-8_1dc0e9f5a17e9845a4ec965b8641f6a6.rdb and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-8_1dc0e9f5a17e9845a4ec965b8641f6a6.rdx b/optimizing_code_cache/html/unnamed-chunk-8_1dc0e9f5a17e9845a4ec965b8641f6a6.rdx deleted file mode 100644 index 42686d3..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-8_1dc0e9f5a17e9845a4ec965b8641f6a6.rdx and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-9_18779fe4b0a27b68f36f69958705c566.RData b/optimizing_code_cache/html/unnamed-chunk-9_18779fe4b0a27b68f36f69958705c566.RData deleted file mode 100644 index cc5926f..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-9_18779fe4b0a27b68f36f69958705c566.RData and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-9_18779fe4b0a27b68f36f69958705c566.rdb b/optimizing_code_cache/html/unnamed-chunk-9_18779fe4b0a27b68f36f69958705c566.rdb deleted file mode 100644 index b2a9dba..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-9_18779fe4b0a27b68f36f69958705c566.rdb and /dev/null differ diff --git a/optimizing_code_cache/html/unnamed-chunk-9_18779fe4b0a27b68f36f69958705c566.rdx b/optimizing_code_cache/html/unnamed-chunk-9_18779fe4b0a27b68f36f69958705c566.rdx deleted file mode 100644 index 16a0f67..0000000 Binary files a/optimizing_code_cache/html/unnamed-chunk-9_18779fe4b0a27b68f36f69958705c566.rdx and /dev/null differ diff --git a/renv.lock b/renv.lock index 8e2438b..97f4ca2 100644 --- a/renv.lock +++ b/renv.lock @@ -1,6 +1,6 @@ { "R": { - "Version": "4.2.1", + "Version": "4.4.0", "Repositories": [ { "Name": "CRAN", @@ -9,241 +9,2129 @@ ] }, "Packages": { + "BH": { + "Package": "BH", + "Version": "1.84.0-0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "a8235afbcd6316e6e91433ea47661013" + }, + "DescTools": { + "Package": "DescTools", + "Version": "0.99.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Exact", + "MASS", + "R", + "Rcpp", + "base", + "boot", + "cli", + "data.table", + "expm", + "gld", + "grDevices", + "graphics", + "httr", + "methods", + "mvtnorm", + "readxl", + "rstudioapi", + "stats", + "utils", + "withr" + ], + "Hash": "cdd76cdd712d77020083cf669af8b3f3" + }, + "Exact": { + "Package": "Exact", + "Version": "3.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "rootSolve", + "stats", + "utils" + ], + "Hash": "1a43175d291899a4b2965b5d8db260e0" + }, + "HSAUR": { + "Package": "HSAUR", + "Version": "1.3-10", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "tools" + ], + "Hash": "63f5fe9d41016c11d4e71b0567fff2e4" + }, + "MASS": { + "Package": "MASS", + "Version": "7.3-60.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "2f342c46163b0b54d7b64d1f798e2c78" + }, + "MBESS": { + "Package": "MBESS", + "Version": "4.9.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "OpenMx", + "R", + "boot", + "lavaan", + "methods", + "mnormt", + "nlme", + "parallel", + "sem", + "semTools", + "stats" + ], + "Hash": "7da7103d9c305280bf307a224b340579" + }, + "MPsychoR": { + "Package": "MPsychoR", + "Version": "0.10-8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "stats" + ], + "Hash": "0c6b3212806543de0c5ab8267ecbe2e3" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "1920b2f11133b12350024297d8a4ff4a" + }, + "MatrixModels": { + "Package": "MatrixModels", + "Version": "0.5-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "methods", + "stats" + ], + "Hash": "0776bf7526869e0286b0463cb72fb211" + }, + "OpenMx": { + "Package": "OpenMx", + "Version": "2.21.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "BH", + "MASS", + "Matrix", + "R", + "Rcpp", + "RcppEigen", + "RcppParallel", + "StanHeaders", + "digest", + "lifecycle", + "methods", + "parallel", + "rpf" + ], + "Hash": "6362da44e762ec951a34fcb6bce60cac" + }, "R6": { "Package": "R6", "Version": "2.5.1", "Source": "Repository", "Repository": "CRAN", - "Hash": "470851b6d5d0ac559e9d01bb352b4021", - "Requirements": [] + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "Rcpp": { + "Package": "Rcpp", + "Version": "1.0.12", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "methods", + "utils" + ], + "Hash": "5ea2700d21e038ace58269ecdbeb9ec0" + }, + "RcppArmadillo": { + "Package": "RcppArmadillo", + "Version": "0.12.8.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "methods", + "stats", + "utils" + ], + "Hash": "1eb4a0603478b5557b77d75534251371" + }, + "RcppEigen": { + "Package": "RcppEigen", + "Version": "0.3.4.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "stats", + "utils" + ], + "Hash": "df49e3306f232ec28f1604e36a202847" + }, + "RcppGSL": { + "Package": "RcppGSL", + "Version": "0.3.13", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp", + "stats" + ], + "Hash": "e8fc7310d256a7b6c4de8e57ab76c55d" + }, + "RcppParallel": { + "Package": "RcppParallel", + "Version": "5.1.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "a45594a00f5dbb073d5ec9f48592a08a" + }, + "RcppZiggurat": { + "Package": "RcppZiggurat", + "Version": "0.1.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "RcppGSL", + "graphics", + "parallel", + "stats", + "utils" + ], + "Hash": "75b4a36aeeed440ad03b996081190703" + }, + "Rfast": { + "Package": "Rfast", + "Version": "2.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "RcppArmadillo", + "RcppParallel", + "RcppZiggurat" + ], + "Hash": "79e8394e1fa06a4ae954b70ca9b16e8f" + }, + "SparseM": { + "Package": "SparseM", + "Version": "1.82", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "d9654aa07d133ba02190ad518a653931" + }, + "StanHeaders": { + "Package": "StanHeaders", + "Version": "2.32.8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "RcppEigen", + "RcppParallel" + ], + "Hash": "c5e952cb62c2f8593de6cdcda0f146e5" + }, + "abind": { + "Package": "abind", + "Version": "1.4-5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods", + "utils" + ], + "Hash": "4f57884290cc75ab22f4af9e9d4ca862" + }, + "afex": { + "Package": "afex", + "Version": "1.3-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "car", + "lme4", + "lmerTest", + "methods", + "pbkrtest", + "reshape2", + "stats", + "utils" + ], + "Hash": "435d52135feffde4d76f502eacfa049c" + }, + "arm": { + "Package": "arm", + "Version": "1.14-4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "Matrix", + "R", + "abind", + "coda", + "grDevices", + "graphics", + "lme4", + "methods", + "nlme", + "stats", + "utils" + ], + "Hash": "4f37635d4d4315785c975b0d89fb8a39" + }, + "askpass": { + "Package": "askpass", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "sys" + ], + "Hash": "cad6cf7f1d5f6e906700b9d3e718c796" + }, + "backports": { + "Package": "backports", + "Version": "1.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "e1e1b9d75c37401117b636b7ae50827a" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "boot": { + "Package": "boot", + "Version": "1.3-30", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "stats" + ], + "Hash": "96abeed416a286d4a0f52e550b612343" + }, + "brio": { + "Package": "brio", + "Version": "1.1.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c1ee497a6d999947c2c224ae46799b1a" + }, + "broom": { + "Package": "broom", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "backports", + "dplyr", + "generics", + "glue", + "lifecycle", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyr" + ], + "Hash": "a4652c36d1f8abfc3ddf4774f768c934" + }, + "bslib": { + "Package": "bslib", + "Version": "0.7.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "8644cc53f43828f19133548195d7e59e" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "callr": { + "Package": "callr", + "Version": "3.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "processx", + "utils" + ], + "Hash": "d7e13f49c19103ece9e58ad2d83a7354" + }, + "car": { + "Package": "car", + "Version": "3.1-2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "abind", + "carData", + "grDevices", + "graphics", + "lme4", + "mgcv", + "nlme", + "nnet", + "pbkrtest", + "quantreg", + "scales", + "stats", + "utils" + ], + "Hash": "839b351f31d56e0147439eb22c00a66a" + }, + "carData": { + "Package": "carData", + "Version": "3.0-5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "ac6cdb8552c61bd36b0e54d07cf2aab7" + }, + "cellranger": { + "Package": "cellranger", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "rematch", + "tibble" + ], + "Hash": "f61dbaec772ccd2e17705c1e872e9e7c" + }, + "class": { + "Package": "class", + "Version": "7.3-22", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "stats", + "utils" + ], + "Hash": "f91f6b29f38b8c280f2b9477787d4bb2" + }, + "cli": { + "Package": "cli", + "Version": "3.6.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "1216ac65ac55ec0058a6f75d7ca0fd52" + }, + "coda": { + "Package": "coda", + "Version": "0.19-4.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "lattice" + ], + "Hash": "af436915c590afc6fffc3ce3a5be1569" + }, + "codetools": { + "Package": "codetools", + "Version": "0.2-20", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "61e097f35917d342622f21cdc79c256e" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "f20c47fd52fae58b4e377c37bb8c335b" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.4.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5a295d7d963cc5035284dcdbaf334f4e" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "e8a1e41acf02548751f45c718d55aa6a" + }, + "curl": { + "Package": "curl", + "Version": "5.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "411ca2c03b1ce5f548345d2fc2685f7a" + }, + "data.table": { + "Package": "data.table", + "Version": "1.15.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "8ee9ac56ef633d0c7cab8b2ca87d683e" + }, + "desc": { + "Package": "desc", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "utils" + ], + "Hash": "99b79fcbd6c4d1ce087f5c5c758b384f" + }, + "diffobj": { + "Package": "diffobj", + "Version": "0.3.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "crayon", + "methods", + "stats", + "tools", + "utils" + ], + "Hash": "bcaa8b95f8d7d01a5dedfd959ce88ab8" + }, + "digest": { + "Package": "digest", + "Version": "0.6.35", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "698ece7ba5a4fa4559e3d537e7ec3d31" + }, + "distributional": { + "Package": "distributional", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "generics", + "lifecycle", + "numDeriv", + "rlang", + "stats", + "utils", + "vctrs" + ], + "Hash": "3bad76869f2257ea4fd00a3c08c2bcce" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "e1071": { + "Package": "e1071", + "Version": "1.7-14", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "class", + "grDevices", + "graphics", + "methods", + "proxy", + "stats", + "utils" + ], + "Hash": "4ef372b716824753719a8a38b258442d" + }, + "evaluate": { + "Package": "evaluate", + "Version": "0.23", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "daf4a1246be12c1fa8c7705a0935c1a0" + }, + "expm": { + "Package": "expm", + "Version": "0.999-9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "methods" + ], + "Hash": "a9cfdee9645dd6b09ba8d4b9a9befa77" + }, + "ez": { + "Package": "ez", + "Version": "4.4-0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "Matrix", + "R", + "car", + "ggplot2", + "lme4", + "mgcv", + "plyr", + "reshape2", + "scales", + "stringr" + ], + "Hash": "bc45454842399ec0f121609938b56c2b" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15aeb8c27f5ea5161f9f6a641fafd93a" + }, + "future": { + "Package": "future", + "Version": "1.33.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "digest", + "globals", + "listenv", + "parallel", + "parallelly", + "utils" + ], + "Hash": "fd7b1d69d16d0d114e4fa82db68f184c" + }, + "future.apply": { + "Package": "future.apply", + "Version": "1.11.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "future", + "globals", + "parallel", + "utils" + ], + "Hash": "afe1507511629f44572e6c53b9baeb7c" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "ggdist": { + "Package": "ggdist", + "Version": "3.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "cli", + "distributional", + "ggplot2", + "glue", + "grid", + "gtable", + "numDeriv", + "quadprog", + "rlang", + "scales", + "tibble", + "vctrs", + "withr" + ], + "Hash": "86ebb3543cdad6520be9bf8863167a9a" + }, + "gghalves": { + "Package": "gghalves", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "ggplot2", + "grDevices", + "grid", + "gtable" + ], + "Hash": "141e061a5a68d463f11a4b5819ef0357" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "gld": { + "Package": "gld", + "Version": "2.6.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "e1071", + "graphics", + "lmom", + "stats" + ], + "Hash": "71173258033324618dc8a09b3e27269e" + }, + "globals": { + "Package": "globals", + "Version": "0.16.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "codetools" + ], + "Hash": "2580567908cafd4f187c1e5a91e98b7f" + }, + "glue": { + "Package": "glue", + "Version": "1.7.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "e0b3a53876554bd45879e596cdb10a52" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang" + ], + "Hash": "e18861963cbc65a27736e02b3cd3c4a0" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "httr": { + "Package": "httr", + "Version": "1.4.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "curl", + "jsonlite", + "mime", + "openssl" + ], + "Hash": "ac107251d9d9fd72f0ca8049988f1d7f" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "e1b9c55281c5adc4dd113652d9e26768" + }, + "knitr": { + "Package": "knitr", + "Version": "1.47", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "7c99b2d55584b982717fcc0950378612" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lavaan": { + "Package": "lavaan", + "Version": "0.6-17", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "graphics", + "methods", + "mnormt", + "numDeriv", + "pbivnorm", + "quadprog", + "stats", + "stats4", + "utils" + ], + "Hash": "993c0958431b2651438c360a070fd088" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "listenv": { + "Package": "listenv", + "Version": "0.9.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "e2fca3e12e4db979dccc6e519b10a7ee" + }, + "lmPerm": { + "Package": "lmPerm", + "Version": "2.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "stats" + ], + "Hash": "8dcc35516af6ccd602a5a171637c8239" + }, + "lme4": { + "Package": "lme4", + "Version": "1.1-35.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "Matrix", + "R", + "Rcpp", + "RcppEigen", + "boot", + "graphics", + "grid", + "lattice", + "methods", + "minqa", + "nlme", + "nloptr", + "parallel", + "splines", + "stats", + "utils" + ], + "Hash": "862f9d995f528f3051f524791955b20c" + }, + "lmerTest": { + "Package": "lmerTest", + "Version": "3.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "ggplot2", + "lme4", + "methods", + "numDeriv", + "stats" + ], + "Hash": "f04948de84602afc23bfa9f5427e954d" + }, + "lmom": { + "Package": "lmom", + "Version": "3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "stats" + ], + "Hash": "a69348cee0766082223f1c7e2a545505" + }, + "lmtest": { + "Package": "lmtest", + "Version": "0.9-40", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "stats", + "zoo" + ], + "Hash": "c6fafa6cccb1e1dfe7f7d122efd6e6a7" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "mi": { + "Package": "mi", + "Version": "1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "arm", + "methods", + "stats4" + ], + "Hash": "b913ecddefb95c7a9b922582d78e33e6" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "minqa": { + "Package": "minqa", + "Version": "1.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp" + ], + "Hash": "aba060ef3c097b26a4d304ea39d87f32" }, - "base64enc": { - "Package": "base64enc", - "Version": "0.1-3", + "mnormt": { + "Package": "mnormt", + "Version": "2.1.1", "Source": "Repository", "Repository": "CRAN", - "Hash": "543776ae6848fde2f48ff3816d0628bc", - "Requirements": [] + "Requirements": [ + "R" + ], + "Hash": "c83992ef63553d1e4b97162a4a753470" }, - "bslib": { - "Package": "bslib", - "Version": "0.3.1", + "munsell": { + "Package": "munsell", + "Version": "0.5.1", "Source": "Repository", "Repository": "CRAN", - "Hash": "56ae7e1987b340186a8a5a157c2ec358", "Requirements": [ - "htmltools", - "jquerylib", - "jsonlite", - "rlang", - "sass" - ] + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" }, - "digest": { - "Package": "digest", - "Version": "0.6.29", + "mvtnorm": { + "Package": "mvtnorm", + "Version": "1.2-5", "Source": "Repository", "Repository": "CRAN", - "Hash": "cf6b206a045a684728c3267ef7596190", - "Requirements": [] + "Requirements": [ + "R", + "stats" + ], + "Hash": "4d1891e59ac7a12b4e7e8a69349125f1" }, - "evaluate": { - "Package": "evaluate", - "Version": "0.15", + "nlme": { + "Package": "nlme", + "Version": "3.1-164", "Source": "Repository", "Repository": "CRAN", - "Hash": "699a7a93d08c962d9f8950b2d7a227f1", - "Requirements": [] + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "a623a2239e642806158bc4dc3f51565d" }, - "fastmap": { - "Package": "fastmap", - "Version": "1.1.0", + "nloptr": { + "Package": "nloptr", + "Version": "2.0.3", "Source": "Repository", "Repository": "CRAN", - "Hash": "77bd60a6157420d4ffa93b27cf6a58b8", - "Requirements": [] + "Requirements": [ + "testthat" + ], + "Hash": "277c67a08f358f42b6a77826e4492f79" }, - "fs": { - "Package": "fs", - "Version": "1.5.2", + "nnet": { + "Package": "nnet", + "Version": "7.3-19", "Source": "Repository", "Repository": "CRAN", - "Hash": "7c89603d81793f0d5486d91ab1fc6f1d", - "Requirements": [] + "Requirements": [ + "R", + "stats", + "utils" + ], + "Hash": "2c797b46eea7fb58ede195bc0b1f1138" }, - "glue": { - "Package": "glue", - "Version": "1.6.2", + "numDeriv": { + "Package": "numDeriv", + "Version": "2016.8-1.1", "Source": "Repository", "Repository": "CRAN", - "Hash": "4f2596dfb05dac67b9dc558e5c6fba2e", - "Requirements": [] + "Requirements": [ + "R" + ], + "Hash": "df58958f293b166e4ab885ebcad90e02" }, - "highr": { - "Package": "highr", - "Version": "0.9", + "openssl": { + "Package": "openssl", + "Version": "2.2.0", "Source": "Repository", "Repository": "CRAN", - "Hash": "8eb36c8125038e648e5d111c0d7b2ed4", "Requirements": [ - "xfun" - ] + "askpass" + ], + "Hash": "2bcca3848e4734eb3b16103bc9aa4b8e" }, - "htmltools": { - "Package": "htmltools", - "Version": "0.5.2", + "parallelly": { + "Package": "parallelly", + "Version": "1.37.1", "Source": "Repository", "Repository": "CRAN", - "Hash": "526c484233f42522278ab06fb185cb26", "Requirements": [ - "base64enc", - "digest", - "fastmap", - "rlang" - ] + "parallel", + "tools", + "utils" + ], + "Hash": "5410df8d22bd36e616f2a2343dbb328c" }, - "jquerylib": { - "Package": "jquerylib", - "Version": "0.1.4", + "patchwork": { + "Package": "patchwork", + "Version": "1.2.0", "Source": "Repository", "Repository": "CRAN", - "Hash": "5aab57a3bd297eee1c1d862735972182", "Requirements": [ - "htmltools" - ] + "cli", + "ggplot2", + "grDevices", + "graphics", + "grid", + "gtable", + "rlang", + "stats", + "utils" + ], + "Hash": "9c8ab14c00ac07e9e04d1664c0b74486" }, - "jsonlite": { - "Package": "jsonlite", - "Version": "1.8.0", + "pbivnorm": { + "Package": "pbivnorm", + "Version": "0.6.0", "Source": "Repository", "Repository": "CRAN", - "Hash": "d07e729b27b372429d42d24d503613a0", - "Requirements": [] + "Hash": "643e16a7da6aac3e18cadc3e14abb94b" }, - "knitr": { - "Package": "knitr", - "Version": "1.39", + "pbkrtest": { + "Package": "pbkrtest", + "Version": "0.5.2", "Source": "Repository", "Repository": "CRAN", - "Hash": "029ab7c4badd3cf8af69016b2ba27493", "Requirements": [ - "evaluate", - "highr", - "stringr", - "xfun", - "yaml" - ] + "MASS", + "Matrix", + "R", + "broom", + "dplyr", + "lme4", + "methods", + "numDeriv", + "parallel" + ], + "Hash": "3b5b99f4d3f067bb9c1d59317d071370" }, - "magrittr": { - "Package": "magrittr", + "phia": { + "Package": "phia", + "Version": "0.3-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "car", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "fa1a39e0db7b8c3035d608eeae5cae4a" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgbuild": { + "Package": "pkgbuild", + "Version": "1.4.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "callr", + "cli", + "desc", + "processx" + ], + "Hash": "a29e8e134a460a01e0ca67a4763c595b" + }, + "pkgconfig": { + "Package": "pkgconfig", "Version": "2.0.3", "Source": "Repository", "Repository": "CRAN", - "Hash": "7ce2733a9826b3aeb1775d56fd305472", - "Requirements": [] + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "pkgload": { + "Package": "pkgload", + "Version": "1.3.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "crayon", + "desc", + "fs", + "glue", + "methods", + "pkgbuild", + "rlang", + "rprojroot", + "utils", + "withr" + ], + "Hash": "876c618df5ae610be84356d5d7a5d124" + }, + "pls": { + "Package": "pls", + "Version": "2.8-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d7bb9f426c05cd90878b502bf152db49" + }, + "plyr": { + "Package": "plyr", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp" + ], + "Hash": "6b8177fd19982f0020743fadbfdbd933" + }, + "praise": { + "Package": "praise", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "a555924add98c99d2f411e37e7d25e9f" + }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" + }, + "processx": { + "Package": "processx", + "Version": "3.8.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "ps", + "utils" + ], + "Hash": "0c90a7d71988856bad2a2a45dd871bb9" + }, + "progress": { + "Package": "progress", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "crayon", + "hms", + "prettyunits" + ], + "Hash": "f4625e061cb2865f111b47ff163a5ca6" + }, + "proxy": { + "Package": "proxy", + "Version": "0.4-27", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "stats", + "utils" + ], + "Hash": "e0ef355c12942cf7a6b91a6cfaea8b3e" + }, + "ps": { + "Package": "ps", + "Version": "1.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "dd2b9319ee0656c8acf45c7f40c59de7" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "pwr": { + "Package": "pwr", + "Version": "1.3-0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "adfa41db3678b41bf9235c514c51799a" + }, + "pwr2ppl": { + "Package": "pwr2ppl", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "MBESS", + "afex", + "broom", + "car", + "dplyr", + "ez", + "lavaan", + "lmPerm", + "lmtest", + "nlme", + "phia", + "pls", + "quantreg", + "semTools", + "stats", + "tidyr" + ], + "Hash": "c45e75a3a10a97a091f3e6b84b1e9130" + }, + "quadprog": { + "Package": "quadprog", + "Version": "1.5-8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5f919ae5e7f83a6f91dcf2288943370d" + }, + "quantreg": { + "Package": "quantreg", + "Version": "5.98", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "Matrix", + "MatrixModels", + "R", + "SparseM", + "graphics", + "methods", + "stats", + "survival" + ], + "Hash": "017561f17632c065388b7062da030952" }, "rappdirs": { "Package": "rappdirs", "Version": "0.3.3", "Source": "Repository", "Repository": "CRAN", - "Hash": "5e3c5dc0b071b21fa128676560dbe94d", - "Requirements": [] + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "readxl": { + "Package": "readxl", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cpp11", + "progress", + "tibble", + "utils" + ], + "Hash": "8cf9c239b96df1bbb133b74aef77ad0a" + }, + "rematch": { + "Package": "rematch", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "cbff1b666c6fa6d21202f07e2318d4f1" + }, + "rematch2": { + "Package": "rematch2", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tibble" + ], + "Hash": "76c9e04c712a05848ae7a23d2f170a40" }, "renv": { "Package": "renv", - "Version": "0.16.0", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "reshape2": { + "Package": "reshape2", + "Version": "1.4.4", "Source": "Repository", "Repository": "CRAN", - "Hash": "c9e8442ab69bc21c9697ecf856c1e6c7", - "Requirements": [] + "Requirements": [ + "R", + "Rcpp", + "plyr", + "stringr" + ], + "Hash": "bb5996d0bd962d214a11140d77589917" }, "rlang": { "Package": "rlang", - "Version": "1.0.4", + "Version": "1.1.3", "Source": "Repository", "Repository": "CRAN", - "Hash": "6539dd8c651e67e3b55b5ffea106362b", - "Requirements": [] + "Requirements": [ + "R", + "utils" + ], + "Hash": "42548638fae05fd9a9b5f3f437fbbbe2" }, "rmarkdown": { "Package": "rmarkdown", - "Version": "2.14", + "Version": "2.27", "Source": "Repository", "Repository": "CRAN", - "Hash": "31b60a882fabfabf6785b8599ffeb8ba", "Requirements": [ + "R", "bslib", "evaluate", + "fontawesome", "htmltools", "jquerylib", "jsonlite", "knitr", - "stringr", + "methods", "tinytex", + "tools", + "utils", "xfun", "yaml" - ] + ], + "Hash": "27f9502e1cdbfa195f94e03b0f517484" + }, + "rootSolve": { + "Package": "rootSolve", + "Version": "1.8.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "stats" + ], + "Hash": "c6fa270a97604238a5ce5fe5d327fdad" + }, + "rpf": { + "Package": "rpf", + "Version": "1.0.14", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "RcppEigen", + "lifecycle", + "methods", + "mvtnorm", + "parallel" + ], + "Hash": "c8298c4497dfc962c70c8d740744c9da" + }, + "rprojroot": { + "Package": "rprojroot", + "Version": "2.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "4c8415e0ec1e29f3f4f6fc108bef0144" + }, + "rstudioapi": { + "Package": "rstudioapi", + "Version": "0.16.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "96710351d642b70e8f02ddeb237c46a7" }, "sass": { "Package": "sass", - "Version": "0.4.1", + "Version": "0.4.9", "Source": "Repository", "Repository": "CRAN", - "Hash": "f37c0028d720bab3c513fd65d28c7234", "Requirements": [ "R6", "fs", "htmltools", "rappdirs", "rlang" - ] + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "sem": { + "Package": "sem", + "Version": "3.1-15", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "boot", + "mi", + "stats", + "utils" + ], + "Hash": "5805daf371dd32f5e5fb4debe0191fd7" + }, + "semTools": { + "Package": "semTools", + "Version": "0.5-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lavaan", + "methods", + "pbivnorm", + "stats", + "utils" + ], + "Hash": "9374d79226103541b8293009f81da90f" }, "stringi": { "Package": "stringi", - "Version": "1.7.6", + "Version": "1.8.4", "Source": "Repository", "Repository": "CRAN", - "Hash": "bba431031d30789535745a9627ac9271", - "Requirements": [] + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "39e1144fd75428983dc3f63aa53dfa91" }, "stringr": { "Package": "stringr", - "Version": "1.4.0", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "survival": { + "Package": "survival", + "Version": "3.6-4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "splines", + "stats", + "utils" + ], + "Hash": "e6e3071f471513e4b85f98ca041303c7" + }, + "sys": { + "Package": "sys", + "Version": "3.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "3a1be13d68d47a8cd0bfd74739ca1555" + }, + "testthat": { + "Package": "testthat", + "Version": "3.2.1.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "brio", + "callr", + "cli", + "desc", + "digest", + "evaluate", + "jsonlite", + "lifecycle", + "magrittr", + "methods", + "pkgload", + "praise", + "processx", + "ps", + "rlang", + "utils", + "waldo", + "withr" + ], + "Hash": "3f6e7e5e2220856ff865e4834766bf2b" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.1", "Source": "Repository", "Repository": "CRAN", - "Hash": "0759e6b6c0957edb1311028a49a35e76", "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", "glue", + "lifecycle", "magrittr", - "stringi" - ] + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" }, "tinytex": { "Package": "tinytex", - "Version": "0.40", + "Version": "0.51", "Source": "Repository", "Repository": "CRAN", - "Hash": "e7b654da5e77bc4e5435a966329cd25f", "Requirements": [ "xfun" - ] + ], + "Hash": "d44e2fcd2e4e076f0aac540208559d1d" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "waldo": { + "Package": "waldo", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "diffobj", + "fansi", + "glue", + "methods", + "rematch2", + "rlang", + "tibble" + ], + "Hash": "c7d3fd6d29ab077cbac8f0e2751449e6" + }, + "withr": { + "Package": "withr", + "Version": "3.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "d31b6c62c10dcf11ec530ca6b0dd5d35" }, "xfun": { "Package": "xfun", - "Version": "0.31", + "Version": "0.44", "Source": "Repository", "Repository": "CRAN", - "Hash": "a318c6f752b8dcfe9fb74d897418ab2b", - "Requirements": [] + "Requirements": [ + "grDevices", + "stats", + "tools" + ], + "Hash": "317a0538d32f4a009658bcedb7923f4b" }, "yaml": { "Package": "yaml", - "Version": "2.3.5", + "Version": "2.3.8", "Source": "Repository", "Repository": "CRAN", - "Hash": "458bb38374d73bf83b1bb85e353da200", - "Requirements": [] + "Hash": "29240487a071f535f5e5d5a323b7afbd" + }, + "zoo": { + "Package": "zoo", + "Version": "1.8-12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "5c715954112b45499fb1dadc6ee6ee3e" } } } diff --git a/renv/.gitignore b/renv/.gitignore new file mode 100644 index 0000000..0ec0cbb --- /dev/null +++ b/renv/.gitignore @@ -0,0 +1,7 @@ +library/ +local/ +cellar/ +lock/ +python/ +sandbox/ +staging/ diff --git a/renv/activate.R b/renv/activate.R index 019b5a6..d13f993 100644 --- a/renv/activate.R +++ b/renv/activate.R @@ -2,10 +2,28 @@ local({ # the requested version of renv - version <- "0.16.0" + version <- "1.0.7" + attr(version, "sha") <- NULL # the project directory - project <- getwd() + project <- Sys.getenv("RENV_PROJECT") + if (!nzchar(project)) + project <- getwd() + + # use start-up diagnostics if enabled + diagnostics <- Sys.getenv("RENV_STARTUP_DIAGNOSTICS", unset = "FALSE") + if (diagnostics) { + start <- Sys.time() + profile <- tempfile("renv-startup-", fileext = ".Rprof") + utils::Rprof(profile) + on.exit({ + utils::Rprof(NULL) + elapsed <- signif(difftime(Sys.time(), start, units = "auto"), digits = 2L) + writeLines(sprintf("- renv took %s to run the autoloader.", format(elapsed))) + writeLines(sprintf("- Profile: %s", profile)) + print(utils::summaryRprof(profile)) + }, add = TRUE) + } # figure out whether the autoloader is enabled enabled <- local({ @@ -15,6 +33,14 @@ local({ if (!is.null(override)) return(override) + # if we're being run in a context where R_LIBS is already set, + # don't load -- presumably we're being run as a sub-process and + # the parent process has already set up library paths for us + rcmd <- Sys.getenv("R_CMD", unset = NA) + rlibs <- Sys.getenv("R_LIBS", unset = NA) + if (!is.na(rlibs) && !is.na(rcmd)) + return(FALSE) + # next, check environment variables # TODO: prefer using the configuration one in the future envvars <- c( @@ -34,9 +60,22 @@ local({ }) - if (!enabled) + # bail if we're not enabled + if (!enabled) { + + # if we're not enabled, we might still need to manually load + # the user profile here + profile <- Sys.getenv("R_PROFILE_USER", unset = "~/.Rprofile") + if (file.exists(profile)) { + cfg <- Sys.getenv("RENV_CONFIG_USER_PROFILE", unset = "TRUE") + if (tolower(cfg) %in% c("true", "t", "1")) + sys.source(profile, envir = globalenv()) + } + return(FALSE) + } + # avoid recursion if (identical(getOption("renv.autoloader.running"), TRUE)) { warning("ignoring recursive attempt to run renv autoloader") @@ -60,21 +99,90 @@ local({ # load bootstrap tools `%||%` <- function(x, y) { - if (is.environment(x) || length(x)) x else y + if (is.null(x)) y else x + } + + catf <- function(fmt, ..., appendLF = TRUE) { + + quiet <- getOption("renv.bootstrap.quiet", default = FALSE) + if (quiet) + return(invisible()) + + msg <- sprintf(fmt, ...) + cat(msg, file = stdout(), sep = if (appendLF) "\n" else "") + + invisible(msg) + + } + + header <- function(label, + ..., + prefix = "#", + suffix = "-", + n = min(getOption("width"), 78)) + { + label <- sprintf(label, ...) + n <- max(n - nchar(label) - nchar(prefix) - 2L, 8L) + if (n <= 0) + return(paste(prefix, label)) + + tail <- paste(rep.int(suffix, n), collapse = "") + paste0(prefix, " ", label, " ", tail) + + } + + heredoc <- function(text, leave = 0) { + + # remove leading, trailing whitespace + trimmed <- gsub("^\\s*\\n|\\n\\s*$", "", text) + + # split into lines + lines <- strsplit(trimmed, "\n", fixed = TRUE)[[1L]] + + # compute common indent + indent <- regexpr("[^[:space:]]", lines) + common <- min(setdiff(indent, -1L)) - leave + paste(substring(lines, common), collapse = "\n") + + } + + startswith <- function(string, prefix) { + substring(string, 1, nchar(prefix)) == prefix } bootstrap <- function(version, library) { + friendly <- renv_bootstrap_version_friendly(version) + section <- header(sprintf("Bootstrapping renv %s", friendly)) + catf(section) + # attempt to download renv - tarball <- tryCatch(renv_bootstrap_download(version), error = identity) - if (inherits(tarball, "error")) - stop("failed to download renv ", version) + catf("- Downloading renv ... ", appendLF = FALSE) + withCallingHandlers( + tarball <- renv_bootstrap_download(version), + error = function(err) { + catf("FAILED") + stop("failed to download:\n", conditionMessage(err)) + } + ) + catf("OK") + on.exit(unlink(tarball), add = TRUE) # now attempt to install - status <- tryCatch(renv_bootstrap_install(version, tarball, library), error = identity) - if (inherits(status, "error")) - stop("failed to install renv ", version) + catf("- Installing renv ... ", appendLF = FALSE) + withCallingHandlers( + status <- renv_bootstrap_install(version, tarball, library), + error = function(err) { + catf("FAILED") + stop("failed to install:\n", conditionMessage(err)) + } + ) + catf("OK") + + # add empty line to break up bootstrapping from normal output + catf("") + return(invisible()) } renv_bootstrap_tests_running <- function() { @@ -83,28 +191,32 @@ local({ renv_bootstrap_repos <- function() { + # get CRAN repository + cran <- getOption("renv.repos.cran", "https://cloud.r-project.org") + # check for repos override repos <- Sys.getenv("RENV_CONFIG_REPOS_OVERRIDE", unset = NA) - if (!is.na(repos)) + if (!is.na(repos)) { + + # check for RSPM; if set, use a fallback repository for renv + rspm <- Sys.getenv("RSPM", unset = NA) + if (identical(rspm, repos)) + repos <- c(RSPM = rspm, CRAN = cran) + return(repos) + } + # check for lockfile repositories repos <- tryCatch(renv_bootstrap_repos_lockfile(), error = identity) if (!inherits(repos, "error") && length(repos)) return(repos) - # if we're testing, re-use the test repositories - if (renv_bootstrap_tests_running()) - return(getOption("renv.tests.repos")) - # retrieve current repos repos <- getOption("repos") # ensure @CRAN@ entries are resolved - repos[repos == "@CRAN@"] <- getOption( - "renv.repos.cran", - "https://cloud.r-project.org" - ) + repos[repos == "@CRAN@"] <- cran # add in renv.bootstrap.repos if set default <- c(FALLBACK = "https://cloud.r-project.org") @@ -143,33 +255,34 @@ local({ renv_bootstrap_download <- function(version) { - # if the renv version number has 4 components, assume it must - # be retrieved via github - nv <- numeric_version(version) - components <- unclass(nv)[[1]] - - # if this appears to be a development version of 'renv', we'll - # try to restore from github - dev <- length(components) == 4L - - # begin collecting different methods for finding renv - methods <- c( - renv_bootstrap_download_tarball, - if (dev) - renv_bootstrap_download_github - else c( - renv_bootstrap_download_cran_latest, - renv_bootstrap_download_cran_archive + sha <- attr(version, "sha", exact = TRUE) + + methods <- if (!is.null(sha)) { + + # attempting to bootstrap a development version of renv + c( + function() renv_bootstrap_download_tarball(sha), + function() renv_bootstrap_download_github(sha) ) - ) + + } else { + + # attempting to bootstrap a release version of renv + c( + function() renv_bootstrap_download_tarball(version), + function() renv_bootstrap_download_cran_latest(version), + function() renv_bootstrap_download_cran_archive(version) + ) + + } for (method in methods) { - path <- tryCatch(method(version), error = identity) + path <- tryCatch(method(), error = identity) if (is.character(path) && file.exists(path)) return(path) } - stop("failed to download renv ", version) + stop("All download methods failed") } @@ -233,8 +346,6 @@ local({ type <- spec$type repos <- spec$repos - message("* Downloading renv ", version, " ... ", appendLF = FALSE) - baseurl <- utils::contrib.url(repos = repos, type = type) ext <- if (identical(type, "source")) ".tar.gz" @@ -251,13 +362,10 @@ local({ condition = identity ) - if (inherits(status, "condition")) { - message("FAILED") + if (inherits(status, "condition")) return(FALSE) - } # report success and return - message("OK (downloaded ", type, ")") destfile } @@ -314,8 +422,6 @@ local({ urls <- file.path(repos, "src/contrib/Archive/renv", name) destfile <- file.path(tempdir(), name) - message("* Downloading renv ", version, " ... ", appendLF = FALSE) - for (url in urls) { status <- tryCatch( @@ -323,14 +429,11 @@ local({ condition = identity ) - if (identical(status, 0L)) { - message("OK") + if (identical(status, 0L)) return(destfile) - } } - message("FAILED") return(FALSE) } @@ -344,8 +447,7 @@ local({ return() # allow directories - info <- file.info(tarball, extra_cols = FALSE) - if (identical(info$isdir, TRUE)) { + if (dir.exists(tarball)) { name <- sprintf("renv_%s.tar.gz", version) tarball <- file.path(tarball, name) } @@ -354,7 +456,7 @@ local({ if (!file.exists(tarball)) { # let the user know we weren't able to honour their request - fmt <- "* RENV_BOOTSTRAP_TARBALL is set (%s) but does not exist." + fmt <- "- RENV_BOOTSTRAP_TARBALL is set (%s) but does not exist." msg <- sprintf(fmt, tarball) warning(msg) @@ -363,10 +465,7 @@ local({ } - fmt <- "* Bootstrapping with tarball at path '%s'." - msg <- sprintf(fmt, tarball) - message(msg) - + catf("- Using local tarball '%s'.", tarball) tarball } @@ -393,8 +492,6 @@ local({ on.exit(do.call(base::options, saved), add = TRUE) } - message("* Downloading renv ", version, " from GitHub ... ", appendLF = FALSE) - url <- file.path("https://api.github.com/repos/rstudio/renv/tarball", version) name <- sprintf("renv_%s.tar.gz", version) destfile <- file.path(tempdir(), name) @@ -404,26 +501,105 @@ local({ condition = identity ) - if (!identical(status, 0L)) { - message("FAILED") + if (!identical(status, 0L)) return(FALSE) - } - message("OK") + renv_bootstrap_download_augment(destfile) + return(destfile) } + # Add Sha to DESCRIPTION. This is stop gap until #890, after which we + # can use renv::install() to fully capture metadata. + renv_bootstrap_download_augment <- function(destfile) { + sha <- renv_bootstrap_git_extract_sha1_tar(destfile) + if (is.null(sha)) { + return() + } + + # Untar + tempdir <- tempfile("renv-github-") + on.exit(unlink(tempdir, recursive = TRUE), add = TRUE) + untar(destfile, exdir = tempdir) + pkgdir <- dir(tempdir, full.names = TRUE)[[1]] + + # Modify description + desc_path <- file.path(pkgdir, "DESCRIPTION") + desc_lines <- readLines(desc_path) + remotes_fields <- c( + "RemoteType: github", + "RemoteHost: api.github.com", + "RemoteRepo: renv", + "RemoteUsername: rstudio", + "RemotePkgRef: rstudio/renv", + paste("RemoteRef: ", sha), + paste("RemoteSha: ", sha) + ) + writeLines(c(desc_lines[desc_lines != ""], remotes_fields), con = desc_path) + + # Re-tar + local({ + old <- setwd(tempdir) + on.exit(setwd(old), add = TRUE) + + tar(destfile, compression = "gzip") + }) + invisible() + } + + # Extract the commit hash from a git archive. Git archives include the SHA1 + # hash as the comment field of the tarball pax extended header + # (see https://www.kernel.org/pub/software/scm/git/docs/git-archive.html) + # For GitHub archives this should be the first header after the default one + # (512 byte) header. + renv_bootstrap_git_extract_sha1_tar <- function(bundle) { + + # open the bundle for reading + # We use gzcon for everything because (from ?gzcon) + # > Reading from a connection which does not supply a 'gzip' magic + # > header is equivalent to reading from the original connection + conn <- gzcon(file(bundle, open = "rb", raw = TRUE)) + on.exit(close(conn)) + + # The default pax header is 512 bytes long and the first pax extended header + # with the comment should be 51 bytes long + # `52 comment=` (11 chars) + 40 byte SHA1 hash + len <- 0x200 + 0x33 + res <- rawToChar(readBin(conn, "raw", n = len)[0x201:len]) + + if (grepl("^52 comment=", res)) { + sub("52 comment=", "", res) + } else { + NULL + } + } + renv_bootstrap_install <- function(version, tarball, library) { # attempt to install it into project library - message("* Installing renv ", version, " ... ", appendLF = FALSE) dir.create(library, showWarnings = FALSE, recursive = TRUE) + output <- renv_bootstrap_install_impl(library, tarball) + + # check for successful install + status <- attr(output, "status") + if (is.null(status) || identical(status, 0L)) + return(status) + + # an error occurred; report it + header <- "installation of renv failed" + lines <- paste(rep.int("=", nchar(header)), collapse = "") + text <- paste(c(header, lines, output), collapse = "\n") + stop(text) + + } + + renv_bootstrap_install_impl <- function(library, tarball) { # invoke using system2 so we can capture and report output bin <- R.home("bin") exe <- if (Sys.info()[["sysname"]] == "Windows") "R.exe" else "R" - r <- file.path(bin, exe) + R <- file.path(bin, exe) args <- c( "--vanilla", "CMD", "INSTALL", "--no-multiarch", @@ -431,19 +607,7 @@ local({ shQuote(path.expand(tarball)) ) - output <- system2(r, args, stdout = TRUE, stderr = TRUE) - message("Done!") - - # check for successful install - status <- attr(output, "status") - if (is.numeric(status) && !identical(status, 0L)) { - header <- "Error installing renv:" - lines <- paste(rep.int("=", nchar(header)), collapse = "") - text <- c(header, lines, output) - writeLines(text, con = stderr()) - } - - status + system2(R, args, stdout = TRUE, stderr = TRUE) } @@ -484,6 +648,9 @@ local({ # if the user has requested an automatic prefix, generate it auto <- Sys.getenv("RENV_PATHS_PREFIX_AUTO", unset = NA) + if (is.na(auto) && getRversion() >= "4.4.0") + auto <- "TRUE" + if (auto %in% c("TRUE", "True", "true", "1")) return(renv_bootstrap_platform_prefix_auto()) @@ -653,34 +820,61 @@ local({ } - renv_bootstrap_validate_version <- function(version) { + renv_bootstrap_validate_version <- function(version, description = NULL) { - loadedversion <- utils::packageDescription("renv", fields = "Version") - if (version == loadedversion) + # resolve description file + # + # avoid passing lib.loc to `packageDescription()` below, since R will + # use the loaded version of the package by default anyhow. note that + # this function should only be called after 'renv' is loaded + # https://github.com/rstudio/renv/issues/1625 + description <- description %||% packageDescription("renv") + + # check whether requested version 'version' matches loaded version of renv + sha <- attr(version, "sha", exact = TRUE) + valid <- if (!is.null(sha)) + renv_bootstrap_validate_version_dev(sha, description) + else + renv_bootstrap_validate_version_release(version, description) + + if (valid) return(TRUE) - # assume four-component versions are from GitHub; three-component - # versions are from CRAN - components <- strsplit(loadedversion, "[.-]")[[1]] - remote <- if (length(components) == 4L) - paste("rstudio/renv", loadedversion, sep = "@") + # the loaded version of renv doesn't match the requested version; + # give the user instructions on how to proceed + dev <- identical(description[["RemoteType"]], "github") + remote <- if (dev) + paste("rstudio/renv", description[["RemoteSha"]], sep = "@") else - paste("renv", loadedversion, sep = "@") + paste("renv", description[["Version"]], sep = "@") - fmt <- paste( - "renv %1$s was loaded from project library, but this project is configured to use renv %2$s.", - "Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.", - "Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.", - sep = "\n" + # display both loaded version + sha if available + friendly <- renv_bootstrap_version_friendly( + version = description[["Version"]], + sha = if (dev) description[["RemoteSha"]] ) - msg <- sprintf(fmt, loadedversion, version, remote) - warning(msg, call. = FALSE) + fmt <- heredoc(" + renv %1$s was loaded from project library, but this project is configured to use renv %2$s. + - Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile. + - Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library. + ") + catf(fmt, friendly, renv_bootstrap_version_friendly(version), remote) FALSE } + renv_bootstrap_validate_version_dev <- function(version, description) { + expected <- description[["RemoteSha"]] + is.character(expected) && startswith(expected, version) + } + + renv_bootstrap_validate_version_release <- function(version, description) { + expected <- description[["Version"]] + is.character(expected) && identical(expected, version) + } + renv_bootstrap_hash_text <- function(text) { hashfile <- tempfile("renv-hash-") @@ -700,6 +894,12 @@ local({ # warn if the version of renv loaded does not match renv_bootstrap_validate_version(version) + # execute renv load hooks, if any + hooks <- getHook("renv::autoload") + for (hook in hooks) + if (is.function(hook)) + tryCatch(hook(), error = warnify) + # load the project renv::load(project) @@ -839,26 +1039,78 @@ local({ } + renv_bootstrap_version_friendly <- function(version, shafmt = NULL, sha = NULL) { + sha <- sha %||% attr(version, "sha", exact = TRUE) + parts <- c(version, sprintf(shafmt %||% " [sha: %s]", substring(sha, 1L, 7L))) + paste(parts, collapse = "") + } + + renv_bootstrap_exec <- function(project, libpath, version) { + if (!renv_bootstrap_load(project, libpath, version)) + renv_bootstrap_run(version, libpath) + } + + renv_bootstrap_run <- function(version, libpath) { + + # perform bootstrap + bootstrap(version, libpath) + + # exit early if we're just testing bootstrap + if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA))) + return(TRUE) + + # try again to load + if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) { + return(renv::load(project = getwd())) + } + + # failed to download or load renv; warn the user + msg <- c( + "Failed to find an renv installation: the project will not be loaded.", + "Use `renv::activate()` to re-initialize the project." + ) + + warning(paste(msg, collapse = "\n"), call. = FALSE) + + } renv_json_read <- function(file = NULL, text = NULL) { + jlerr <- NULL + # if jsonlite is loaded, use that instead - if ("jsonlite" %in% loadedNamespaces()) - renv_json_read_jsonlite(file, text) + if ("jsonlite" %in% loadedNamespaces()) { + + json <- tryCatch(renv_json_read_jsonlite(file, text), error = identity) + if (!inherits(json, "error")) + return(json) + + jlerr <- json + + } + + # otherwise, fall back to the default JSON reader + json <- tryCatch(renv_json_read_default(file, text), error = identity) + if (!inherits(json, "error")) + return(json) + + # report an error + if (!is.null(jlerr)) + stop(jlerr) else - renv_json_read_default(file, text) + stop(json) } renv_json_read_jsonlite <- function(file = NULL, text = NULL) { - text <- paste(text %||% read(file), collapse = "\n") + text <- paste(text %||% readLines(file, warn = FALSE), collapse = "\n") jsonlite::fromJSON(txt = text, simplifyVector = FALSE) } renv_json_read_default <- function(file = NULL, text = NULL) { # find strings in the JSON - text <- paste(text %||% read(file), collapse = "\n") + text <- paste(text %||% readLines(file, warn = FALSE), collapse = "\n") pattern <- '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]' locs <- gregexpr(pattern, text, perl = TRUE)[[1]] @@ -906,14 +1158,14 @@ local({ map <- as.list(map) # remap strings in object - remapped <- renv_json_remap(json, map) + remapped <- renv_json_read_remap(json, map) # evaluate eval(remapped, envir = baseenv()) } - renv_json_remap <- function(json, map) { + renv_json_read_remap <- function(json, map) { # fix names if (!is.null(names(json))) { @@ -940,7 +1192,7 @@ local({ # recurse if (is.recursive(json)) { for (i in seq_along(json)) { - json[i] <- list(renv_json_remap(json[[i]], map)) + json[i] <- list(renv_json_read_remap(json[[i]], map)) } } @@ -960,35 +1212,9 @@ local({ # construct full libpath libpath <- file.path(root, prefix) - # attempt to load - if (renv_bootstrap_load(project, libpath, version)) - return(TRUE) - - # load failed; inform user we're about to bootstrap - prefix <- paste("# Bootstrapping renv", version) - postfix <- paste(rep.int("-", 77L - nchar(prefix)), collapse = "") - header <- paste(prefix, postfix) - message(header) - - # perform bootstrap - bootstrap(version, libpath) - - # exit early if we're just testing bootstrap - if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA))) - return(TRUE) - - # try again to load - if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) { - message("* Successfully installed and loaded renv ", version, ".") - return(renv::load()) - } - - # failed to download or load renv; warn the user - msg <- c( - "Failed to find an renv installation: the project will not be loaded.", - "Use `renv::activate()` to re-initialize the project." - ) + # run bootstrap code + renv_bootstrap_exec(project, libpath, version) - warning(paste(msg, collapse = "\n"), call. = FALSE) + invisible() }) diff --git a/renv/settings.json b/renv/settings.json new file mode 100644 index 0000000..2472d63 --- /dev/null +++ b/renv/settings.json @@ -0,0 +1,19 @@ +{ + "bioconductor.version": [], + "external.libraries": [], + "ignored.packages": [], + "package.dependency.fields": [ + "Imports", + "Depends", + "LinkingTo" + ], + "ppm.enabled": null, + "ppm.ignored.urls": [], + "r.version": [], + "snapshot.type": "implicit", + "use.cache": true, + "vcs.ignore.cellar": true, + "vcs.ignore.library": true, + "vcs.ignore.local": true, + "vcs.manage.ignores": true +}