From 861aa57e7ead379da197c99269cf8a5541b38804 Mon Sep 17 00:00:00 2001 From: jverzani Date: Sat, 23 Dec 2023 09:40:17 -0500 Subject: [PATCH 1/4] rm cruft --- check_version.jl | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 check_version.jl diff --git a/check_version.jl b/check_version.jl deleted file mode 100644 index c41c4f3..0000000 --- a/check_version.jl +++ /dev/null @@ -1,7 +0,0 @@ -using Purl - -chapters = ("EDA","Inference","LinearModels") -file_extension(file::String) = file[findlast(==('.'), file)+1:end] - -for ch ∈ chapters - qmd = filter(x -> file_extension(x) == "qmd", From 5cb5ee52a99c94c6fe9c43435c409a0012668c7c Mon Sep 17 00:00:00 2001 From: jverzani Date: Wed, 17 Jul 2024 17:30:35 -0400 Subject: [PATCH 2/4] edits --- EDA/bivariate-julia.html | 3899 ----------------- EDA/categorical-data-julia.html | 1416 ------ EDA/makie.html | 721 --- EDA/tabular-data-julia.html | 1733 -------- EDA/univariate-julia.html | 2062 --------- Inference/distributions.html | 856 ---- Inference/inference.html | 1626 ------- LinearModels/linear-regression.html | 1184 ----- _quarto.yml | 9 +- index.html | 407 -- index.qmd | 1 + references.bib | 8 + references.html | 227 - site_libs/bootstrap/bootstrap-icons.css | 1704 ------- site_libs/bootstrap/bootstrap-icons.woff | Bin 137124 -> 0 bytes site_libs/bootstrap/bootstrap.min.css | 10 - site_libs/bootstrap/bootstrap.min.js | 7 - site_libs/clipboard/clipboard.min.js | 7 - site_libs/quarto-html/anchor.min.js | 9 - site_libs/quarto-html/popper.min.js | 6 - .../quarto-syntax-highlighting.css | 171 - site_libs/quarto-html/quarto.js | 770 ---- site_libs/quarto-html/tippy.css | 1 - site_libs/quarto-html/tippy.umd.min.js | 2 - site_libs/quarto-nav/headroom.min.js | 7 - site_libs/quarto-nav/quarto-nav.js | 222 - site_libs/quarto-search/autocomplete.umd.js | 3 - site_libs/quarto-search/fuse.min.js | 9 - site_libs/quarto-search/quarto-search.js | 1123 ----- 29 files changed, 14 insertions(+), 18186 deletions(-) delete mode 100644 EDA/bivariate-julia.html delete mode 100644 EDA/categorical-data-julia.html delete mode 100644 EDA/makie.html delete mode 100644 EDA/tabular-data-julia.html delete mode 100644 EDA/univariate-julia.html delete mode 100644 Inference/distributions.html delete mode 100644 Inference/inference.html delete mode 100644 LinearModels/linear-regression.html delete mode 100644 index.html delete mode 100644 references.html delete mode 100644 site_libs/bootstrap/bootstrap-icons.css delete mode 100644 site_libs/bootstrap/bootstrap-icons.woff delete mode 100644 site_libs/bootstrap/bootstrap.min.css delete mode 100644 site_libs/bootstrap/bootstrap.min.js delete mode 100644 site_libs/clipboard/clipboard.min.js delete mode 100644 site_libs/quarto-html/anchor.min.js delete mode 100644 site_libs/quarto-html/popper.min.js delete mode 100644 site_libs/quarto-html/quarto-syntax-highlighting.css delete mode 100644 site_libs/quarto-html/quarto.js delete mode 100644 site_libs/quarto-html/tippy.css delete mode 100644 site_libs/quarto-html/tippy.umd.min.js delete mode 100644 site_libs/quarto-nav/headroom.min.js delete mode 100644 site_libs/quarto-nav/quarto-nav.js delete mode 100644 site_libs/quarto-search/autocomplete.umd.js delete mode 100644 site_libs/quarto-search/fuse.min.js delete mode 100644 site_libs/quarto-search/quarto-search.js diff --git a/EDA/bivariate-julia.html b/EDA/bivariate-julia.html deleted file mode 100644 index 257f472..0000000 --- a/EDA/bivariate-julia.html +++ /dev/null @@ -1,3899 +0,0 @@ - - - - - - - - - -bivariate-julia - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

Bivariate data

-

We now consider two variables measured for the same observation, or bivariate data. The combinations of numeric data or categorical data lead to different means to describe and visualize the sets of data.

-

We load our basic suite of packages:

-
-
using StatsBase, StatsPlots
-using DataFrames, CategoricalArrays, Chain
-using RDatasets
-
-

In the following, we use the widely used iris data set which has numeric measurements of sepal and petal width and lengths for 3 species of irises.

-
-
iris = dataset("datasets", "iris")
-first(iris, 3)
-
- -
3×5 DataFrame
RowSepalLengthSepalWidthPetalLengthPetalWidthSpecies
Float64Float64Float64Float64Cat…
15.13.51.40.2setosa
24.93.01.40.2setosa
34.73.21.30.2setosa
-
-
-

There are 50 cases per species:

-
-
combine(groupby(iris, :Species), nrow)
-
- -
3×2 DataFrame
RowSpeciesnrow
Cat…Int64
1setosa50
2versicolor50
3virginica50
-
-
-
-

Multiple samples

-

A common scenario is when data for a measurement is collected across multiple levels of a factor to see if there is something about that factor that has an effect on the measurement. As measurements typically have fluctuations, this “effect” must be understood relative to some understanding of the underlying variability. To gather that, a probability model might be used. For now, we content ourselves with common graphical comparisons, which can be used to distinguish large effects.

-

First we graphically explore one measurement, PetalWidth, for the 3 different species. A typical question might be, is the average petal width the same across the three species?

-

The data storage in iris is tidy data, each row is an observation, the PetalWidth is the variable. Were the data in a long format with a variable for each level of the factor, the stack command would prove useful to tidy the data.

-

The graphics are created by splitting the data along the levels of the factor and then applying the graphic to the univariate data for each group. For this task, the StatsPlots package is useful.1

-
-

Grouped dot plots

-

A dot plot is a useful graphic to show the distribution of values on a number line when the data is sufficiently spread out so as not to overlap too much. The basic graphic is \(1\)-dimensional, adding a second dimension for the grouping variable allows easy comparisons.

-

Here we discuss dot plots for different levels of a factor. In the iris data set, to create plots by each species (Figure 1), allowing a comparison of the distribution of the \(x\) variable over different levels of a factor. The following illustrates the steps done, but we will see next that such effort is unneeded as the StatsPlots recipes wrap up this data manipulation, in this case with a method for dotplot.

-
-
byspecies = groupby(iris, :Species)
-p1 = StatsPlots.plot()
-for (i, k) in enumerate(keys(byspecies)) # iterate over keys
-    species = k.Species
-    xs = byspecies[k].PetalWidth
-    ys = i .+ one.(xs)
-    dotplot!(p1, xs, ys; label=species)
-end
-p1;
-
-

The identification of the species after grouping is a bit awkward, but the idea is we split the data, then create a dot plot for each level of the grouping variable, :Species.

-

This is more easily done by passing :Species as the \(y\) value to dotplot, in which case the categorical variable is used for grouping.

-
-
p2 = @df iris dotplot(:PetalWidth, :Species; legend=false);
-
-

The @df macro was used as a convenience. This replaces the symbols in the expression with the corresponding column in the specified data frame.

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

Figure 1: Grouped dot plots allow an easy comparison of the distribution of a variable over the levels of a categorical variable. These two use the same data set, but were produced different ways. The left-most was done “by hand” and uses a legend to distinguish the species. The right-most was produced by dotplot with labels used to mark the species.

-
-
-
-

Putting the categorical variable first, presents a graphic (Figure 2) which uses jittering to disambiguate equal measurements in a species:

-
-
@df iris dotplot(:Species, :PetalWidth; legend=false)
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 2: the categorial variable’s position determines the layout chosen for the grouped dot plot. In this figure, the categorical variable was in the x position, and the numeric data was jittered left and right.

-
-
-
-

Regardless of how the graphic is produced, there appears to be a difference in the centers based on the species, as would be expected – different species have different sizes.

-
-
-

Boxplots across groups

-

Grouped boxplots are made in a similar manner to the last example using boxplot (Figure 3); we use the categorical variable indicating grouping in the x position:

-
-
@df iris boxplot(:Species, :PetalWidth; legend=false)
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 3: a basic boxplot comparing petal width across the different species.

-
-
-
-

An article from fivethirtyeight.com contained the boxplots in Figure 4. The graphic used does not show the traditional “1.5IQR” whiskers, rather it extends the box using shading.

-
-
-

A version of a box-and-whisker plot for comparing the time that a very fast crossword puzzler takes when done on the computer and the paper. The various summary statistics, such as the median, are all less on the computer.

-

Figure 4: Crossword times by method

-
-
-
-
-
- -
-
-Ode to the boxplots -
-
-
-

The box plot is a great graphic for comparing values across the levels of some factor. Each boxplot shows the center (the median), the range (the IQR), the shape (well basic symmetry about the median), the outlying values (with its identying rule of 1.5 IQRs beyond \(Q_1\) and \(Q_3\). The only drawback is boxplots can’t show multi modal data. But if the factor is the cause of that, then the collection of boxplots is great for comparison.

-
-
-
-
-

Violin and density plots

-

A violin plot is like the boxplot, only it is wider where more data is concentrated, and smaller where less is. It uses a sideways density plot to summarize the data, whereas a boxplot only uses the five-number summary. The violin function creates these (Figure 5):

-
-
p2 = @df iris violin(:Species, :PetalWidth; legend=false)
-@df iris dotplot!(p2, :Species, :PetalWidth; legend=false)
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 5: Illustration of a violin plot with a dotplot overlay for the petal width across the different species.

-
-
-
-

As illustrated for univariate data, the density function makes a density plot. There is an option for grouped data.

-

As seen below, the grouping variable is a named, not positional argument. In Figure 6 the left plot is done without grouping, the right one with grouping by species. The left plot shows a multi-modal distribution, which suggests, as we already have seen, that there is a mixture of populations in the data. There appear to be two modes, with a hint of a third. The separation between the “setosa” species and the other two is greater than the separation between the two others.

-
-
p1 = @df iris density(:PetalWidth; legend=false)
-p2 = @df iris density(:PetalWidth, group=:Species);
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 6: Density plots of PetalWidth and PetalWidth by Species. The left graphic suggests multiple modes, which are revealed in the right graphic.

-
-
-
-
-
-

Histograms across groups

-

The density plot is, perhaps, less familiar than a histogram, but makes a better graphic when comparing two or more distributions, as the individual graphics don’t overlap. There are attempts to use the histogram, and they can be effective. In Figure 7, taken from an article on fivethirtyeight.com on the time to solve cross word puzzles broken out by day of week, we see a stacked dotplot, which is visually very similar to a histogram, presented for each day of the week. The use of color allows one to distinguish the day, but the overalpping aspect of the graphic inhibits part of the distribution of most days, and only effectively shows the longer tails as the week progresses. In StatsPlots, the grouped hist function can produce similar graphics.

-
-
-
-
-

-

Crossword times by day

-
-
-

Figure 7: Dotplots for solving times of various New York Times crossword puzzled grouped by day of the puzzle.

-
-
-
-
-

Quantile comparisons

-

A “quantile-quantile plot” plots pairs of quantiles from two data sets as a scatter plot to help gauge if the shape of the two distributions is similar. If it is, the data will fall roughly on a straight line. The previously discussed quantile-normal plot is identical, with the specialization that one of the distributions is the reference normal distribution.

-

We use this plot to check if the shape of the distribution for two different species is roughly the same. To do so, we extract the PetalWidth values for the different levels of :Species. A direct way might be indexing: iris[iris.Species .== "versicolor", :PetalWidth] or with subset: subset(iris, :Species => ByRow(==("versicolor"))).PetalWidth. In the following we group by :Species then for each group apply a \(z\)-score to :PetalWidth. From here, we can compare the 1st and 2nd and then the 2nd and 3rd group with qqplot in Figure 8. There is not a strong reason to expect big differences in shape.

-
-
jitter(x, scale = 0.25) = x + scale * randn(length(x))
-gps = [select(gp, :PetalWidth => zscorejitter => :PetalWidth)
-       for gp in groupby(iris, :Species)]
-
-
-p1 = qqplot(gps[1].PetalWidth, gps[2].PetalWidth)
-p2 = qqplot(gps[2].PetalWidth, gps[3].PetalWidth);
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 8: A qqplot of the petal width from the versicolor and virginica species. The graphic suggests similar-shaped distributions. The data is jittered to disambiguate identical values.

-
-
-
-
-
-
-

Paired data

-

We have looked at a single variable over several levels of a given factor. If the data is split along the factor, there are possibly a different number of measurements for the given level. A key statistical assumption for inference will be that these different measurements are independent.

-

We might express this data as

-

\[\begin{align*} -x_{11}, & x_{12}, \dots, x_{1n_1}\\ -x_{21}, & x_{22}, \dots, x_{2n_2}\\ - & \vdots \\ -x_{m1}, & x_{m2}, \dots, x_{mn_m}. -\end{align*}\]

-

Now we turn our attention to data of two measured variables for a given observation. These two variables must have the same number of values, this being equal to the number of observations. Moreover, it need not be the case that the two measurements should be “independent,” indeed, the measurements are from the same case, so, for example with the iris data, if a plant has a large petal width it will likely also have a large petal length.

-

We might express the data mathematically as:

-

\[\begin{align*} -x_{1}, & x_{2}, \dots, x_{n}\\ -y_{1}, & y_{2}, \dots, y_{n} -\end{align*}\]

-

Or – to emphasize how the data is paired off – as \((x_1, y_1), (x_2, y_2), \dots, (x_n, y_n)\).

-
-

Numeric summaries

-

The main numeric summary of paired data is the Pearson correlation coefficient. In terms of \(z\) scores this is almost the average of their respective products:

-

\[ -cor = \frac{1}{n-1} \sum \left(\frac{x_i - \bar{x}}{s_x}\right) \cdot \left(\frac{y_i - \bar{y}}{s_y}\right). -\]

-

The cor function computes this measure.

-

Consider again the iris data, only we select below just the setosa data and the petal attributes:

-
-
l, w = @chain iris begin
-    subset(:Species => ByRow(==("setosa")))
-    select([:PetalLength, :PetalWidth])
-    eachcol
-end;
-
-

We have the correlation computed by

-
-
cor(l, w)
-
-
0.3316300408041188
-
-
-

The correlation will be seen to be measure of linear associativity. When it is close to 1 or -1, the variability in the data is well described by a line; when close to 0, not so.

-

The correlation can be computed from a matrix for each pair of columns. This is done here for illustration:

-
-
@chain iris begin
-    subset(:Species => ByRow(==("setosa")))
-    select(_, names(_, Real))
-    Matrix
-    cor
-end
-
-
4×4 Matrix{Float64}:
- 1.0       0.742547  0.267176  0.278098
- 0.742547  1.0       0.1777    0.232752
- 0.267176  0.1777    1.0       0.33163
- 0.278098  0.232752  0.33163   1.0
-
-
-

The Spearman correlation is the correlation of the data after ranking. It is a measure of monotonicity in the data. There are different ranking algorithms in StatsBase, the Spearman correlation uses tiedrank. It can be computed with this or with corspearman, as shown:

-
-
corspearman(l, w), cor(tiedrank(l), tiedrank(w))
-
-
(0.2711413763783511, 0.2711413763783511)
-
-
-

The primary graphic to assess the relationship between the two, presumably, dependent variables is a scatter plot:

-
-
scatter(l, w; legend=false, xlab="PetalLength", ylab="PetalWidth")
-
-vline!([mean(l)], linestyle=:dash)
-hline!([mean(w)], linestyle=:dash)
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 9: Scatter plot of petal length and petal width for the setosa data. The dashed lines show the “center” of the data, \((\bar{x}, \bar{y})\).

-
-
-
-

Figure 9 shows the length and width data in a scatter plot. Jittering would be helpful to show all the data, as it has been discretized and many points are overplotte. The dashed lines are centered at the means of the respective variables. If the mean is the center of a single variable, then \((\bar{x}, \bar{y})\) may be thought of as the center of the paired data. Thinking of the dashed lines meeting at the origin, four quadrants are formed. The correlation can be viewed as a measure of how much the data sits in opposite quadrants. In the figure, there seems to be more data in quadrants I and III then II and IV, which sugests a positive correlation, as confirmed numerically.

-

By writing the correlation in terms of \(z\)-scores, the product in that formula is positive if the point is in quadrant I or III and negative if in II or IV. So, for example, a big positive number suggests data is concentrated in quadrants I and III or that there is a strong association between the variables. The scaling by the standard deviations, leaves the mathematical constraint that the correlation is between \(-1\) and \(1\).

-
-
-
- -
-
-Correlation is not causation -
-
-
-

The expression “correlation is not causation” is a common one, due to efforts by the tobacco industry to give plausible alternatives to the statistical association of cigarette smoking and health problems such as lung cancer. Indeed, in some instances correlation between two variables can be related to a lurking or confounding variable. An example might be coffee consumption is positively associated with heart disease, yet is not necessarily the cause, as smoking is associated with both variables and may be the cause.

-
-
-
-
-

Trend lines

-

We mentioned that the Pearson correlation is a measure of a linear association and the Spearman rank correlation a measure of monotonic association. We now discuss some basic trend lines that highlight the association in paired data, where there is response variable.

-

We use the language explanatory variable to describe the \(x\) variable, and response variable to describe the \(y\) variable, with the understanding that \(x\) might be experimentally controlled and \(y\) a random, measured response. The “line” is an explanation of how the \(y\) values vary on average as the \(x\) values vary by the experimenter.

-

There are many choices for how to identify a trend line, though we will show one such way that is the most commonly used. To set it in a mathematical setting, a line can be parameterized by two values, \(y = \beta_0 + \beta_1 x\). Use the value \(\hat{y}_i = \beta_0 + \beta_1 x_i\), which is the \(y\) value of the point on the line corresponding to \(x_i\). The residual amount is usually defined by \(e_i = y_i - \hat{y}_i\). That is the vertical distance from the data point to the line. See Figure 10 for a visualization. One could argue that the distance to the line might be better than the vertical distance, but this choice leads to a very useful mathematical characterization.

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

Figure 10: Two different lines fit to the data. For each the residuals are computed, squared, then summed. The least-squares regression line is the line that makes this quantity minimal for a given data set.

-
-
-
-

Let \(f(x)\) be some function, then a measure of the distance of the data to the line might be the sum \(\sum_i f(e_i)\). For example, using \(f(x) = |x|\), we have through findmin:

-
-
F(x) = abs(x)
-
-resid_sum(x, y, F, b0, b1) =
-    sum(F, yi - (b0 + b1*xi) for (xi,yi) in zip(x,y))
-j(b0, b1) = resid_sum(l, w, F, b0, b1)
-
-xs = ys = -2 : 1/100 : 2
-_, k = findmin(j.(xs, ys'))
-
-(b0 = xs[first(k.I)], b1 = ys[last(k.I)])
-
-
(b0 = 0.2, b1 = 0.0)
-
-
-

This non-rigorously identifies the values of the coefficients that minimize the sum in \([-2,2]\times[-2,2]\) by searching for the smallest over a grid with resolution 1/100.

-

Using a different \(F\) changes the answer:

-
-
F(x) = x^2
-
-resid_sum(x, y, f, b0, b1) =
-    sum(f, yi - (b0 + b1*xi) for (xi,yi) in zip(x,y))
-j(b0, b1) = resid_sum(l, w, F, b0, b1)
-
-xs = ys = -2 : 1/100 : 2
-_, k = findmin(j.(xs, ys'))
-
-(b0 = xs[first(k.I)], b1 = ys[last(k.I)])
-
-
(b0 = -0.06, b1 = 0.21)
-
-
-

When \(F(x) = x^2\), that is we seek to minimize the sum of the squared residuals, there is a mathematical formulation that can be solved to identify values for the constants. This is known as the method of least squares. There is a mathematical framework that this problem fits within that uses linear algebra.

-

Consider the \(n\) linear equations of the form \(y_i = \beta_0 + \beta_1 x_i\). Written in a column form these are:

-

\[\begin{align*} -y_1 & = \beta_0 + \beta_1 x_1\\ -y_2 & = \beta_0 + \beta_1 x_2\\ -& \vdots \\ -y_n & = \beta_0 + \beta_1 x_n\\ -\end{align*}\]

-

There are \(2\) unknowns, \(\beta_0\) and \(\beta_1\). Were there two equations (\(n=2\)), we could solve by substitution to find an expression for these in terms of \((x_1,y_1)\) and \((x_2, y_2)\). We algebraically solve this below with the help of a symbolic math package:

-
-
using SymPy
-@syms x[1:2] y[1:2] β[0:1]
-eqns = [one.(x) x] * β .~ y
-
-
2-element Vector{Sym{PyCall.PyObject}}:
- x₁⋅β₁ + β₀ = y₁
- x₂⋅β₁ + β₀ = y₂
-
-
-
-
solve(eqns, β)
-
-
Dict{Sym{PyCall.PyObject}, Sym{PyCall.PyObject}} with 2 entries:
-  β₀ => (x₁*y₂ - x₂*y₁)/(x₁ - x₂)
-  β₁ => (y₁ - y₂)/(x₁ - x₂)
-
-
-

Mathematically for the linear problem, this last line is equivalent to solving the equation \(A\beta=y\) where \(A\) is a matrix with one column of 1s and the other of \(x\):

-
-
A = [one.(x) x]
-
-
2×2 Matrix{Sym{PyCall.PyObject}}:
- 1  x₁
- 1  x₂
-
-
-
-
A \ y
-
-
2-element Vector{Sym}:
- (x₁*y₂ - x₂*y₁)/(x₁ - x₂)
-       (y₁ - y₂)/(x₁ - x₂)
-
-
-

However, it is expected that there are more than 2 data points, so the line will in general be overdetermined. Symbolically, for really small \(n\), this can be solved symbolically:

-
-
@syms x[1:3] y[1:3] β[0:1]
-A = [one.(x) x]
-solve(sum(e^2 for e in A*β - y), β)
-
-
2-element Vector{Tuple{Sym{PyCall.PyObject}, Sym{PyCall.PyObject}}}:
- (-x₁*β₁/3 - x₂*β₁/3 - x₃*β₁/3 + y₁/3 + y₂/3 + y₃/3 - sqrt(2)*sqrt(-x₁^2*β₁^2 + x₁*x₂*β₁^2 + x₁*x₃*β₁^2 + 2*x₁*y₁*β₁ - x₁*y₂*β₁ - x₁*y₃*β₁ - x₂^2*β₁^2 + x₂*x₃*β₁^2 - x₂*y₁*β₁ + 2*x₂*y₂*β₁ - x₂*y₃*β₁ - x₃^2*β₁^2 - x₃*y₁*β₁ - x₃*y₂*β₁ + 2*x₃*y₃*β₁ - y₁^2 + y₁*y₂ + y₁*y₃ - y₂^2 + y₂*y₃ - y₃^2)/3, β₁)
- (-x₁*β₁/3 - x₂*β₁/3 - x₃*β₁/3 + y₁/3 + y₂/3 + y₃/3 + sqrt(2)*sqrt(-x₁^2*β₁^2 + x₁*x₂*β₁^2 + x₁*x₃*β₁^2 + 2*x₁*y₁*β₁ - x₁*y₂*β₁ - x₁*y₃*β₁ - x₂^2*β₁^2 + x₂*x₃*β₁^2 - x₂*y₁*β₁ + 2*x₂*y₂*β₁ - x₂*y₃*β₁ - x₃^2*β₁^2 - x₃*y₁*β₁ - x₃*y₂*β₁ + 2*x₃*y₃*β₁ - y₁^2 + y₁*y₂ + y₁*y₃ - y₂^2 + y₂*y₃ - y₃^2)/3, β₁)
-
-
-

The symbolic output isn’t pretty, but, with some calculus skills, the above approach algebraically yields these values for the \(\beta\)s in terms of the data, \((x_i, y_i)\), for any \(n \geq 2\):

-

\[ -\quad \hat{\beta}_1 = cor(x,y) \cdot \frac{s_y}{s_x}, \quad \hat{\beta}_0 = \bar{y} - \hat{\beta}_1 \bar{x}. -\]

-

Numerically, the linear algebra problem implemented in the \ operation used above solves the same least squares problem. So for our problem, we can get the coefficients via:

-
-
A = [one.(l) l]
-A \ w
-
-
2-element Vector{Float64}:
- -0.048220327513871966
-  0.20124509405873592
-
-
-

For this identified line:

-
    -
  • the slope (\(\hat{\beta}_1\)) is proportional to the correlation coefficient, the proportion being the ratio of the standard deviation in the response variable and standard deviation of the explanatory variable.

  • -
  • the point \((\bar{x}, \bar{y})\) is on the line.

  • -
  • the coefficients can be computed efficiently through linear algebra

  • -
-

The underlying machinery of linear algebra is given a different interface along with generalizations in the GLM (Bates et al. n.d.) package which implements the linear model through its lm function. We also add the StatsModels package, but this is included with GLM.

-

The statistical model to fit is specified through a formula defined using an interface from StatsModels. In this case the model is basically that w linearly depends on l and is written w ~ l, modeling the width of the petal in terms of the length.

-
-
using StatsModels, GLM
-res = fit(LinearModel, @formula(y ~ x), (y=w, x=l))
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, LinearAlgebra.CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-y ~ 1 + x
-
-Coefficients:
-───────────────────────────────────────────────────────────────────────────
-                  Coef.  Std. Error      t  Pr(>|t|)   Lower 95%  Upper 95%
-───────────────────────────────────────────────────────────────────────────
-(Intercept)  -0.0482203   0.121641   -0.40    0.6936  -0.292796    0.196356
-x             0.201245    0.0826325   2.44    0.0186   0.0351012   0.367389
-───────────────────────────────────────────────────────────────────────────
-
-
-

The formula is written as a macro, so the variables do not need to be symbols, as they are not immediately evaluated because the macro transforms them first. Macros are processed before the values are known. Formulas are templates, the data specification in lm fills in that template. In the above, a standard formula for regression is specified, the data specification assigns y to w and x to l.

-

The combination of the generic fit method with the LinearModel type is implemented in the lm function, used hereafter.

-

In the following, we use names from the data frame created by subset in the formula.

-
-
d = subset(iris, :Species => ByRow(==("setosa")))
-lm(@formula(PetalWidth ~ PetalLength), d)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, LinearAlgebra.CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-PetalWidth ~ 1 + PetalLength
-
-Coefficients:
-───────────────────────────────────────────────────────────────────────────
-                  Coef.  Std. Error      t  Pr(>|t|)   Lower 95%  Upper 95%
-───────────────────────────────────────────────────────────────────────────
-(Intercept)  -0.0482203   0.121641   -0.40    0.6936  -0.292796    0.196356
-PetalLength   0.201245    0.0826325   2.44    0.0186   0.0351012   0.367389
-───────────────────────────────────────────────────────────────────────────
-
-
-

The output has more detail to be explained later. For now, we only need to know that the method coef will extract the coefficients (in the first column) as a vector of length 2, which we assign to the values bhat0 and bhat1 below:

-
-
scatter(jitter(l), jitter(w); legend=false)  # spread out values
-bhat0, bhat1 = coef(res)                     # the coefficients
-plot!(x -> bhat0 + bhat1 * x)                # `predict` does this generically
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- -
-
-A constant model -
-
-
-

The linear model has much wider applicability than the simple regression model, \(y_i = \beta_0 + \beta_1 x_i + \epsilon_i\). To see one example, we can fit the model \(y_i = \beta_0 + \epsilon_i\) using least squares:

-
-
lm(@formula(y ~ 1), (y=l,)) |> coef
-
-
1-element Vector{Float64}:
- 1.462
-
-
-

The estimate is the mean of l.

-
-
-
-
-

Local regression

-

The method of least squares used above is called “regression.” It was popularized by Galton (Galton 1886) along with the phrase “regression to the mean.” The method of local regression (LOESS, with SS meaning scatterplot smoothing) essentially fits a regression line (or a degree \(d\) polynomial) to parts of the data then stitches them together. The result is a curve that summarizes any trend in the data between the response variable and the explanatory variable. Figure 11 shows some locally fit regression lines on the left, and on the right an identified loess line to show that the loess line here mostly follows the local regression models. The “loess” line was added to with these commands from the Loess package, which are wrapped up into a function to call later:

-
-
import Loess   # import so as not to conflict with other loaded packages
-
-function loess_line!(p, x, y; normalize=true, span=0.75, degree=2, kwargs...)
-  model = Loess.loess(x, y; normalize=normalize, span=span, degree=degree)
-  us = range(extrema(x)..., length=100)
-  plot!(p, us, Loess.predict(model, us); kwargs...)
-end
-
-
loess_line! (generic function with 1 method)
-
-
-

The default value of the degree keyword is 2 for a quadratic fit to the data.

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

Figure 11: On left, a scatter plot with some local regression lines (based on 10 points) layered on; on right, the same with a degree 1 loess line added.

-
-
-
-
-
-

Robust linear models

-

The formula for the slope of the simple regression coefficient depends on \(s_y/s_x\). The sample standard deviations may be influenced greatly by as few as one outlying value. That is, they are not very resistant to outliers. A theory of robust linear models has been developed which are useful in allowing a relaxation on the standard distributional assumptions, relaxations that accommodate data with more outliers. The RobustModels package implements a number of such models, of which we illustrate just MEstimator{TukeyLoss} without comment as to the implemented algorithm.

-

One way to view data sets that benefit from robust models is to envision a process where the measured response values are given by the value on a regression line plus some “normally” distributed error plus some “contaminated” error which introduces outliers.

-

To simulate that in our length and width data, we simply add in a contaminant to some of the terms below which appear in Figure 12 in the upper-right corner. That figure shows the least-squares regression line is influenced by the 3 contaminated points, but that robust regression line is not.

-
-
w′ = jitter(w)
-l′ = jitter(l)
-idx = sortperm(l′)
-w′, l′ = w′[idx], l′[idx]
-
-sigma_e = dispersion(res.model) # sqrt(deviance(res)/dof_residual(res))
-inds = [45, 48, 50]             # some indices
-
-w′[inds] = w'[inds] .+ 5*sigma_e  # contaminate 3 values
-color = fill("blue", 50); color[inds] .= "red"
-
-scatter(l′, w′; legend=:topleft, color=color)
-
-fm = @formula(w ~ l)
-res = lm(fm, (w=w′, l=l′))
-
-us = range(minimum(l′), maximum(l′), 100)
-plot!(us, GLM.predict(res, (l=us,)); linewidth=4, label="lm")
-
-import RobustModels
-estimator = RobustModels.MEstimator{RobustModels.TukeyLoss}()
-res_robust = RobustModels.rlm(fm, (w=w′, l=l′), estimator)
-
-plot!(us, RobustModels.predict(res_robust, [one.(us) us]); linewidth=4, label="rlm")
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 12: The three data points in the upper right of this graphic are “contaminated” data. Their outlying nature has undue influence on the least-squares regression line (“lm”) pulling it up towards the points. The robust regression line (“rlm”) is not so influenced.

-
-
-
-
-
-

Prediction

-

In the definition of loess_line! and Figure 12 a predict method from various packages was used to plot the trend line. The pattern was similar: predict(model, (l=data,)) created \(y\) values for the given explanatory values.

-

The language comes from the usage of statistical models.

-

The explanatory “variable”, in general, may consist of \(0\), \(1\), or more variables. Examples with \(0\) or \(1\) for lm were given. It is not hard to envision in the iris data that the petal length might be influenced by the species, the petal width, and even details of the sepal sizes. The word explanatory hints that the \(y\) values should be somehow explained by the covariates. Of course, statistical data is typically subject to variability, so even if all covariates are equal, different responses need not be the same. A statistical model may account for this by expressing a relationship:

-
-

response = predicted + error

-
-

For our purposes, the “predicted” part comes from some parameterized function of the covariates and forms the structural model for the data. On average, it is assumed the errors are \(0\), but that an individual error introduces some randomness.

-

The parameters in the model are fitted by some process (such as lm) leaving the related decomposition

-
-

response = fitted + residual

-
-

In GLM and other packages within the Julia ecosystem, the aptly named generic fit method is used to fit a model.

-

The “fitted” and “residual” values are based on the data and are known quantities; the “predicted” and “error” are postulated and may be unknowable. It is common to use the identified model as estimates for predicted values.

-

In the above examples, the predict method was used to do just that. The syntax for predict may have seemed more complicated than needed, but the model may have many covariates, so the specification needs a means to identify them. A Tables compatible specification of the data used in the prediction is used, in the above that was a named tuple.

-

The modeling environment of Julia has these generic methods for the parts of a model:

-
    -
  • predict(model, newdata): Make predictions for new values of the covariate(s).
  • -
  • fitted(model): The predicted values for the covariate(s) used to identify the parameters in the model.
  • -
  • response(model): The response values
  • -
  • residuals(model): The response values minus the fitted values
  • -
-

The residuals are not the errors, but give a sense of them. The residuals function returns the residual values for a model fit. For example, we have these summaries:

-
-
mean(residuals(res)), std(residuals(res))
-
-
(1.9984014443252818e-16, 0.38940886590833923)
-
-
-

(The mean of the residuals is always 0 for a linear model, a consequence of the method of least squares.)

-
-
-

Assessing the residuals

-

In the creation of Figure 12 the following value was used:

-
-
sigma_e = dispersion(res.model)
-
-
0.39344429872476144
-
-
-

This is the square root of the sum of the squared residuals (computed by deviance) divided by the degrees of freedom (in this case \(n-2\), but in general given by dof_residual):

-
-
mse = deviance(res) / dof_residual(res) # sense of scale
-sqrt(mse)
-
-
0.39344429872476144
-
-
-

For the simple linear regression model, the value \(\sum e_i^2\) is minimized by the choice of the estimates for the \(\beta\)s. This value is called the SSE, or sum of squared errors (or the SSR, sum of squared residuals). It can be found from the residuals or the generic method deviance:

-
-
sum(ei^2 for ei in residuals(res)), deviance(res)
-
-
(7.43032397755293, 7.430323977552928)
-
-
-

The deviance gives a sense of scale, but does not reflect the size of the data set, \(n\). The residual degrees of freedom of a model is \(n-k -1\) where \(k\) is the number of variables. Assuming an intercept, in simple linear regression \(k=1\)

-
-
n = length(l) # or nobs(res)
-n - 1 - 1, dof_residual(res)
-
-
(48, 48.0)
-
-
-

(More generally, this is \(n\) minus the number of columns in the model matrix, which for this model is one for the intercept and one for the \(x\) values.)

-

The mean squared error (MSE) is the ratio of the SSE and the degrees of freedom, or \((\sum e_i^2)/(n-k-1)\). This is like a sample variance for the error terms, the square root of it, like the standard deviation, a also measure of variability.

-

The residuals can be used to graphically assess the ability of the fitted model used to describe the data. There are a few standard graphics:2

-
    -
  • A fitted versus residuals plot. The residuals are the data less the structural part of the model. If the model fits well, the residuals should not show a pattern.
  • -
  • A quantile plot of the residuals. For statistical inference, there are distributional assumptions made on the error values. This plot helps assess if typical assumption on the shape is valid.
  • -
  • A scale location. For statistical inference, there are distributional assumptions made on the error values. This plot helps assess if typical assumption of equal variances is valid.
  • -
  • Residuals versus leverage. As regression can be sensitive to influential outliers. This is assessed with Cook’s distance which measures the change if point is used to fit the data compared to if it is not.
  • -
-

The support for these graphics in Julia is not as developed as in R, say. As such, we create some simple functions for generating this plots.

-

We first define several helper functions:

-
-
using LinearAlgebra: diag
-hat(res) = (X = modelmatrix(res); X * inv(crossmodelmatrix(res)) * X')
-hatvalues(res) = diag(hat(res))
-function rstandard(res)
-    es = [ei/sqrt(1-hi) for (ei, hi) in zip(residuals(res), hatvalues(res))]
-    s = dispersion(res.model)
-    es / s
-end
-
-
rstandard (generic function with 1 method)
-
-
-

The hat function computes a matrix that arises in the regression model, its diagonal entries are returned by hatvalues. These are used to scale the residuals to produce the standardized residuals, computed by rstandard. The generic leverage, defined in StatsBase, is intended to compute what hatvalues does, but it currently doesn’t have a method defined in GLM.

-

The four basic diagnostic plots follow those returned by default in R, though the R versions offer more insight into outlying values. In Figure 13 the four graphics are shown for the “setosa” species data with PetalWidth modeled by PetalLength.

-
-
function fitted_versus_residuals(res)
-  p = plot(; legend=false)
-  scatter!(p, fitted(res), residuals(res);
-  xlab="Fitted", ylab="Residuals", title="Residuals vs. fitted")
-  loess_line!(p, fitted(res), residuals(res))
-  p
-end
-
-function quantile_residuals(res)
-    qqnorm(rstandard(res);
-       legend=false,
-       title = "Normal Q-Q",
-       xlab="Theoretical quantiles", ylab="Standardized residuals")
-end
-
-function scale_location_plot(res)
-    x, y = fitted(res), rstandard(res)
-    y = sqrt.(abs.(y))
-
-    p = plot(; legend=false)
-    scatter!(p, x, y;
-        title="Scale location",
-        xlab="Fitted", ylab = "Standardized residuals")
-    loess_line!(p, x, y)
-    p
-end
-
-function residual_leverage(res)
-   x, y = cooksdistance(res), rstandard(res)
-
-   p = plot(; legend=false)
-   scatter!(p, x, y;
-    title = "Residuals vs. leverage",
-    xlab="Leverage", ylab="Standardized residuals")
-   loess_line!(p, x, y)
-   p
-end
-
-
residual_leverage (generic function with 1 method)
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 13: Four diagnostic plots, following the standard ones of R

-
-
-
-
-
-
- -
-
-Alternatives -
-
-
-

In the above, we created functions to compute some diagnostic values. These are also defined in the LinRegOutliers package (al. 2021) along with much else. However, the interface is not quite compatible with the model results of lm, so we didn’t leverage that work here.

-
-
-
-
-

Transformations

-

The lm function, as used above, expects linear relationships between the explanatory variable and the response variable. That is, a model of the type \(y = \beta_0 + \beta_1 x + \epsilon\). There may be data for which some mathematical transformation is necessary in order to get this structural form.

-

The animals data set, defined below, measures the body size and brain size of various species of animals. The brain size depends on the body size, but it seems to be non-linear and better modeled by \(e^y = e^{\beta_0 + \beta_1 x + \epsilon}\). In Figure 14 we can see scatter plots of the two data sets before and after a log transform.

-
-
animals = dataset("MASS", "Animals")
-transform!(animals, [:Brain, :Body] .=> ByRow(log) .=> [:logBrain, :logBody])
-
-p1 = @df animals scatter(:Body, :Brain; legend=false,
-                         title="Brain by body", xlab="Body", ylab="Brain")
-p2 = @df animals scatter(:logBody, :logBrain; legend=false,
-                         title="log(Brain) by log(body)",
-                         xlab="log(Body)", ylab="log(Brain)")
-plot(p1, p2; layout = (@layout [a b]))#, size=fig_size_2)
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 14: Plot of animals data before and after log transform of the body and brain variables.

-
-
-
-

There is an automated group of transformations called power transformations; these are implemented in the BoxCoxTrans package. Its transform function identifies an appropriate monotonic transformation. We can see the results of the transformations of both variables here along with a regression line and a robust regression line:

-
-
import BoxCoxTrans
-transform!(animals, [:Brain,:Body] .=> BoxCoxTrans.transform .=> [:bcBrain, :bcBody])
-
-p = @df animals scatter(:bcBody, :bcBrain; label=nothing,
-                        title = "Animals, Box-Cox transformed",
-                        xlab = "box-cox Body", ylab = "box-cox Brain")
-
-fm = @formula(bcBrain ~ bcBody)
-res = lm(fm,  animals)
-
-estimator = RobustModels.MEstimator{RobustModels.TukeyLoss}()
-res_robust = RobustModels.rlm(fm, animals, estimator)
-
-us = range(extrema(animals.bcBody)..., length=100)
-plot!(us, GLM.predict(res, (bcBody = us,)), linewidth=4, label="lm")
-plot!(us, RobustModels.predict(res_robust,  [one.(us) us]), linewidth=4, label="rlm")
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 15: The animals data after both the Body and Brain variables have been transformed through the Box-Cox method. This data set has three outliers that pull the lm regression line downward, but do not impact the robust regression line, which more accurately captures the trend for all but the three outliers (which happen to be dinosaur species).

-
-
-
-
-
-

Grouping by a categorical variable

-

The iris data is an example of differences between the species, one more so than the other. The group argument to scatter instructs the coloring of the markers to vary across the values of the grouping variable, effectively showing three variables at once.

-
-
p_scatter = @df iris scatter(:PetalWidth, :PetalLength; group=:Species,
-      legend=false, alpha=0.33);
-
-

In Figure 16 the basic scatter plot for petal width modeled by petal length is shown in the left figure along with a fitted regression line. We set an alpha value for the scatterplot to better see the regression lines. The line itself was prepared with:

-
-
m1 = lm(@formula(PetalLength ~ PetalWidth), iris)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, LinearAlgebra.CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-PetalLength ~ 1 + PetalWidth
-
-Coefficients:
-───────────────────────────────────────────────────────────────────────
-               Coef.  Std. Error      t  Pr(>|t|)  Lower 95%  Upper 95%
-───────────────────────────────────────────────────────────────────────
-(Intercept)  1.08356   0.072967   14.85    <1e-30   0.939366    1.22775
-PetalWidth   2.22994   0.0513962  43.39    <1e-85   2.12838     2.33151
-───────────────────────────────────────────────────────────────────────
-
-
-
-
us = range(extrema(iris.PetalWidth)..., 100)
-vs = predict(m1, (PetalWidth = us, ))
-plot!(p_scatter, us, vs; title="iris data");
-
-

The three clusters can influence the regression line fit to the scatter plot, that is species can be an influence, as would be expected. There are different ways for this expectation to be incorporated.

-

First, suppose we simply adjust the fitted lines up or down for each cluster. That is, there is some additive effect for each type of species. As a example of something we discuss at more length later, we can fit this model by including Species as an additive term:

-
-
m2 = lm(@formula(PetalLength ~ PetalWidth + Species), iris)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, LinearAlgebra.CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-PetalLength ~ 1 + PetalWidth + Species
-
-Coefficients:
-───────────────────────────────────────────────────────────────────────────────
-                       Coef.  Std. Error      t  Pr(>|t|)  Lower 95%  Upper 95%
-───────────────────────────────────────────────────────────────────────────────
-(Intercept)          1.2114    0.0652419  18.57    <1e-39   1.08246     1.34034
-PetalWidth           1.01871   0.152242    6.69    <1e-09   0.717829    1.31959
-Species: versicolor  1.69779   0.180948    9.38    <1e-15   1.34018     2.05541
-Species: virginica   2.27669   0.281325    8.09    <1e-12   1.7207      2.83269
-───────────────────────────────────────────────────────────────────────────────
-
-
-

The second row in the output of m2 has an identical interpretation as for m1 – it is the slope of the regression line. The first line of the output in m1 is the \(x\)-intercept, which moves the line up or down. Whereas the first of m2 is the \(x\) intercept for a line that describes just one of the species, in this case setosa. (A coding for the regression model with a categorical variable chooses one reference level, in this case “setosa.”). The 3rd and 4th lines are the slopes for the other two species.

-

We can plot these individually, one-by-one, in a similar manner as before, however when we call predict we include a level for :Species. The result is the middle figure in Figure 16.

-
-
p_scatter2 = deepcopy(p_scatter)
-
-gdf = groupby(iris, :Species)
-for (k,d)  pairs(gdf)   # GroupKey, SubDataFrame
-
-    s = string(k.Species)
-
-    us = range(extrema(d.PetalWidth)..., 100)
-    vs = predict(m2, DataFrame(PetalWidth=us, Species=s))
-    plot!(p_scatter2, us, vs; linewidth=5)
-
-end
-
-

Now we identify different regression lines (slope and intercepts) for each cluster. This is done throuh a multiplicative model and is specified in the model formula of StatsModels with a *:

-
-
m3 = lm(@formula(PetalLength ~ PetalWidth * Species), iris)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, LinearAlgebra.CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-PetalLength ~ 1 + PetalWidth + Species + PetalWidth & Species
-
-Coefficients:
-─────────────────────────────────────────────────────────────────────────────────────────────
-                                     Coef.  Std. Error      t  Pr(>|t|)  Lower 95%  Upper 95%
-─────────────────────────────────────────────────────────────────────────────────────────────
-(Intercept)                       1.32756     0.130933  10.14    <1e-17   1.06877     1.58636
-PetalWidth                        0.54649     0.490003   1.12    0.2666  -0.422038    1.51502
-Species: versicolor               0.453712    0.373701   1.21    0.2267  -0.284935    1.19236
-Species: virginica                2.91309     0.406031   7.17    <1e-10   2.11054     3.71564
-PetalWidth & Species: versicolor  1.32283     0.555241   2.38    0.0185   0.225359    2.42031
-PetalWidth & Species: virginica   0.100769    0.524837   0.19    0.8480  -0.936611    1.13815
-─────────────────────────────────────────────────────────────────────────────────────────────
-
-
-

The first four coefficients are interpreted similarly to those for m2, the remaining 2 summarize the interaction between the petal width and the species type.

-

We can produce a graphic, the right-most figure in Figure 16:

-
-
p_scatter3 = deepcopy(p_scatter)
-
-gdf = groupby(iris, :Species)
-for (k,d)  pairs(gdf)   # GroupKey, SubDataFrame
-
-    s = string(k.Species)
-
-    us = range(extrema(d.PetalWidth)..., 100)
-    vs = predict(m3, DataFrame(PetalWidth=us, Species=s))
-    plot!(p_scatter3, us, vs; linewidth=5)
-
-end
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 16: Three scatterplots of petal width by petal length in the iris data set. The right-hand figure includes the regression line fit to all the data. The middle one fits regression lines identified through an additive model that includes the species. The slopes are parallel, but the lines have an additive shift. The right figure shows a multiplicative model wherein the slopes and intercepts for each species are chosen.

-
-
-
-
-
-al., S. et (2021), “LinRegOutliers: A julia package for detecting outliers in linear regression,” Journal of Open Source Software, 6. https://doi.org/10.21105/joss.02892. -
-
-Bates, D., and others (n.d.). GLM.jl. https://doi.org/10.5281. -
-
-Galton, F. (1886), Regression towards mediocrity in hereditary stature. The Journal of the Anthropological Institute of Great Britain and Ireland, [Royal Anthropological Institute of Great Britain; Ireland, Wiley], 15, 246–263. -
-
-
-
-
-
-
-
    -
  1. For the Makie plotting package, the AlgebraOfGraphics package is a powerful alternative to StatsPlots and reminiscent of R’s ggplot system. We will illustrate its use in a later chapter.↩︎

  2. -
  3. These are graphics that R uses with linear models.↩︎

  4. -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/EDA/categorical-data-julia.html b/EDA/categorical-data-julia.html deleted file mode 100644 index 14ff83b..0000000 --- a/EDA/categorical-data-julia.html +++ /dev/null @@ -1,1416 +0,0 @@ - - - - - - - - - -categorical-data-julia - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

Categorical data

-

In the last chapter, the main variable was numeric either a measurement taken over the levels of a factor or as a pair of numeric variables. In this chapter we consider univariate and bivariate categorical data.

-

We will use the following, by now standard, packages in this chapter:

-
-
using StatsBase, StatsPlots
-using DataFrames, Chain, CategoricalArrays
-using RDatasets
-
-
-

Univariate categorical data

-

We first start with a single categorical variable before turning our attention to the case of one or more such variables.

-
-

Tabulations

-

Let’s consider a data set from R’s MASS package on a student survey; in particular the Smoke variable, which is stored as a categorical variable. The levels method reports 4 levels for this variable:

-
-
using RDatasets
-survey = dataset("MASS", "survey")
-smokes = survey.Smoke
-levels(smokes)
-
-
4-element Vector{String}:
- "Heavy"
- "Never"
- "Occas"
- "Regul"
-
-
-

How frequent is each? We would need to tabulate to answer that question.

-

The countmap function from the StatsBase package counts occurrences in each level, returning a dictionary:

-
-
countmap(smokes)
-
-
Dict{Union{Missing, CategoricalValue{String, UInt8}}, Int64} with 5 entries:
-  "Occas" => 19
-  missing => 1
-  "Heavy" => 11
-  "Never" => 189
-  "Regul" => 17
-
-
-

There are numerous alternatives.

-

Using a data frames approach, this tabulation can be done by grouping and applying the nrow function:

-
-
combine(groupby(survey, :Smoke), nrow)
-
- -
5×2 DataFrame
RowSmokenrow
Cat…?Int64
1missing1
2Heavy11
3Never189
4Occas19
5Regul17
-
-
-

Similarly, we can split (with group from SplitApplyCombine) and apply length:

-
-
import SplitApplyCombine: group
-@chain survey begin
-    eachrow
-    copy
-    group(r -> r.Smoke, _)
-    map(length, _)
-end
-
-
5-element Dictionaries.Dictionary{Any, Int64}
- CategoricalValue{String, UInt8} "Never" │ 189
- CategoricalValue{String, UInt8} "Regul" │ 17
- CategoricalValue{String, UInt8} "Occas" │ 19
- CategoricalValue{String, UInt8} "Heavy" │ 11
-                                 missing │ 1
-
-
-

The above easily tabulates the same, and would be useful if the data were not in a data frame.

-

For tabulation, a separate package has benefits. We use the FreqTables package and its freqtables function. This returns a named vector:

-
-
using FreqTables
-tbl = freqtable(smokes)
-
-
5-element Named Vector{Int64}
-Dim1    │ 
-────────┼────
-Heavy   │  11
-Never   │ 189
-Occas   │  19
-Regul   │  17
-missing │   1
-
-
-

Named vectors are from the NamedArrays package and offer indexing by name, similar to a data frame.

-

Any of these approaches show for this data set that even in the \(90\)s “Never” is by far the most common response for past smoking habits, and that there is one missing response.

-
-
-

Visualizations

-

Tabulations can be visualized beyond using a table.

-

A bar chart represents the above tablulations using bars proportional to the counts. The bar function makes a bar chart for the labels in x and the given data. For this example, we have either have to add “missing” to the levels, or, as is done here, excise it from the data set.

-
-
bar(levels(smokes), collect(tbl[1:end-1]); legend=false)
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 1: Basic barplot.

-
-
-
-

Another common graphic is to use a dot, not a bar, to represent the value. Still another graphic is the pie chart; common in the business world, but not a favorite within introductory statistics.

-
-
-
-

Paired categorical data

-

In the survey data set we could look at pairs of data where both are categorical and ask questions about the pair. For example, the data contains information on smoking and identified gender (Sex). Is one gender more likely to smoke?

-

Again, we can use grouping and apply to see the counts:

-
-
tbl = combine(groupby(survey, [:Sex, :Smoke]), nrow)
-first(tbl, 3) # 3 of 10 rows displayed
-
- -
3×3 DataFrame
RowSexSmokenrow
Cat…?Cat…?Int64
1missingNever1
2FemaleHeavy5
3FemaleNever99
-
-
-

A contingency table is the more familiar means to view two-way categorical count data. A count of all combinations of the levels of one and the levels of the other is presented in a grid.

-

With unstack we can do this within DataFrames:

-
-
@chain survey begin
-    select([:Sex, :Smoke])
-    dropmissing
-    groupby([:Sex, :Smoke])
-    combine(nrow => :value)
-    unstack(:Smoke, :value)
-end
-
- -
2×5 DataFrame
RowSexHeavyNeverOccasRegul
Cat…Int64?Int64?Int64?Int64?
1Female59995
2Male6891012
-
-
-

The above dropped missing values; to keep them in, the allowmissing argument may be specified to unstack:

-
-
@chain survey begin
-    groupby([:Sex, :Smoke])
-    combine(nrow => :value)
-    unstack(:Smoke, :value; allowmissing=true)
-end
-
- -
3×6 DataFrame
RowSexNeverHeavyOccasRegulmissing
Cat…?Int64?Int64?Int64?Int64?Int64?
1missing1missingmissingmissingmissing
2Female99595missing
3Male89610121
-
-
-

The missing values can be replaced with 0 using the coalesce function which scans through its arguments returning the first that is not equal to missing:

-
-
@chain survey begin
-    groupby([:Sex, :Smoke])
-    combine(nrow => :value)
-    unstack(:Smoke, :value; allowmissing=true)
-    transform(Not(1) .=> ByRow(x -> coalesce(x, 0)); renamecols=false)
-end
-
- -
3×6 DataFrame
RowSexNeverHeavyOccasRegulmissing
Cat…?Int64Int64Int64Int64Int64
1missing10000
2Female995950
3Male89610121
-
-
-

More conveniently, the freqtable command will produce contingency tables:

-
-
tbl = freqtable(survey, :Sex, :Smoke)
-
-
3×5 Named Matrix{Int64}
-Sex ╲ Smoke │   Heavy    Never    Occas    Regul  missing
-────────────┼────────────────────────────────────────────
-Female      │       5       99        9        5        0
-Male        │       6       89       10       12        1
-missing     │       0        1        0        0        0
-
-
-

The freqtable interface allows the user to pass in two variables of data, or, as above, a tabular data set and two variable names. The freqtable method summarized them with the levels of the first variables naming the rows, and levels of the second naming the columns.

-

It is essentially this function using DataFrames:

-
-
function xtabs(d, f, g)
-    @chain d begin
-        groupby([f, g])
-        combine(nrow => :value)
-        unstack(g, :value; allowmissing=true)
-        transform(Not(1) .=> ByRow(x ->coalesce(x, 0)); renamecols=false)
-    end
-end
-
-
xtabs (generic function with 1 method)
-
-
-
-

Conditional distributions of two-way tables

-

At first glance, there does not seem to be much difference in the smoking variable between the identified genders. As tables may have many more counts in a given row or column, it can be helpful to take proportions of the rows or columns to compare. The FreqTables package provides the prop function to do so. By default, it takes a proportion of all the data; the keyword margins=1 is used to see proportions for each row, margins=2 to see proportions for each column. For example, to compare the distribution of Smokes for each level of Sex, we take proportions across each row:

-
-
prop(tbl; margins=1)  # check `sum(prop(tbl; margins=1); dims=2)` returns 1
-
-
3×5 Named Matrix{Float64}
-Sex ╲ Smoke │      Heavy       Never       Occas       Regul     missing
-────────────┼───────────────────────────────────────────────────────────
-Female      │  0.0423729    0.838983   0.0762712   0.0423729         0.0
-Male        │  0.0508475    0.754237   0.0847458    0.101695  0.00847458
-missing     │        0.0         1.0         0.0         0.0         0.0
-
-
-

There does not seem to be large differences between the rows, perhaps indicating that the gender doesn’t seem to have an effect on the smoking prevalency, though surveyed females were more likely to have never smoked and less likely to to be regular smokers compared to their male counterparts.

-

What about the exercise variable?

-
-
tbl = freqtable(survey, :Exer, :Smoke)
-prop(tbl; margins=1)
-
-
3×5 Named Matrix{Float64}
-Exer ╲ Smoke │     Heavy      Never      Occas      Regul    missing
-─────────────┼──────────────────────────────────────────────────────
-Freq         │ 0.0608696   0.756522   0.104348  0.0782609        0.0
-None         │ 0.0416667       0.75      0.125  0.0416667  0.0416667
-Some         │ 0.0306122   0.857143  0.0408163  0.0714286        0.0
-
-
-

Again, not much difference across the levels of Exer.

-

Finding the row (or column) proportions as above finds the conditional distribution for a given value. (Answering the question, say, what is the distribution of the second variable given the first variables has a specific level?)

-
-

Row proportions with Data Frames.

-

The task of finding row proportions with a data frame can be similarly addressed by applying a transform to each row. The following uses a percentage scale:

-
-
perc(x, Σ) = round(100 * x / Σ, digits=1)
-perc(x::Missing, Σ) = x
-perc(r) = map(x -> perc(x,sum(skipmissing(r))), r)
-
-function perc_table(d)
-    nms = names(d, Union{AbstractString, CategoricalValue})
-    combine(d, nms, AsTable(Not(nms)) => ByRow(perc) => AsTable; renamecols=false)
-end
-
-xtabs(survey, :Exer, :Smoke) |> perc_table
-
- -
3×6 DataFrame
RowExerHeavyNeverOccasRegulmissing
Cat…Float64Float64Float64Float64Float64
1Freq6.175.710.47.80.0
2None4.275.012.54.24.2
3Some3.185.74.17.10.0
-
-
-
-
-
-

Marginal distributions of two-way tables

-

A marginal distribution from a two-way table is found by adding all the values in each row, or each column. With two-way tables generated from the full data, there are more direct ways to realize these, but from a two-way table, we just need to apply sum to each row or column. The sum function takes a dims argument to specify the dimension, which, in this case, is 2 for adding along the columns (the second dimension) and 1 for adding down the rows (the first dimension):1

-
-
sum(tbl, dims=1) # kinda like `freqtable(survey.Smoke)`
-
-
1×5 Named Matrix{Int64}
-Exer ╲ Smoke │   Heavy    Never    Occas    Regul  missing
-─────────────┼────────────────────────────────────────────
-sum(Exer)    │      11      189       19       17        1
-
-
-
-
sum(tbl, dims=2) # like `freqtable(survey.Exer)`
-
-
3×1 Named Matrix{Int64}
-Exer ╲ Smoke │ sum(Smoke)
-─────────────┼───────────
-Freq         │        115
-None         │         24
-Some         │         98
-
-
-
-
-

Two-way tables from summarized data

-

Suppose a data set was presented in the following two-way table:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
A two-way contingency table of fabricated data showing counts of student grades and mode of instruction.
GradeIn personHybridAsynchronous Online
A - C1055
D101510
F51010
-

This table could be stored as a two-way table in different ways. Here we show how to make this a data frame, then expand it to variables, then summarize.

-
-
df = DataFrame([
-(Grade="A-C", IP=10, Hybrid=5,  Asynchronous=5),
-(Grade="D",   IP=10, Hybrid=15, Asynchronous=10),
-(Grade="F",   IP=5,  Hybrid=10, Asynchronous=10)
-])
-
- -
3×4 DataFrame
RowGradeIPHybridAsynchronous
StringInt64Int64Int64
1A-C1055
2D101510
3F51010
-
-
-

There are 80 students summarized here:

-
-
sum([sum(r[2:end]) for r in  eachrow(df)])
-
-
80
-
-
-

Here we make a data frame with 80 cases:

-
-
ddf = @chain df begin
-    stack(Not(:Grade), variable_name=:InstructionType)
-    transform(:value => ByRow(n -> 1:n); renamecols=false)
-    flatten(:value)
-    select(Not(:value))
-end
-
-describe(ddf)
-
- -
2×7 DataFrame
Rowvariablemeanminmedianmaxnmissingeltype
SymbolNothingStringNothingStringInt64DataType
1GradeA-CF0String
2InstructionTypeAsynchronousIP0String
-
-
-

(It isn’t the most efficient, but we utilize flatten to repeat the values easily created by the range operator, 1:n above. As these values are not of interest, we subsequently drop them.)

-

To see we could return to the original table, we first give the InstructionType the right ordering of the levels, then create a frequency table:

-
-
ordered_levels = ["IP", "Hybrid", "Asynchronous"]
-ddf.InstructionType = categorical(ddf.InstructionType;
-                                       ordered=true, levels=ordered_levels)
-freqtable(ddf, :Grade, :InstructionType)
-
-
3×3 Named Matrix{Int64}
-Grade ╲ InstructionType │           IP        Hybrid  Asynchronous
-────────────────────────┼─────────────────────────────────────────
-A-C                     │           10             5             5
-D                       │           10            15            10
-F                       │            5            10            10
-
-
-
-
-

Graphical summaries of two-way contingency tables

-

We review a few visualizations of dependent categorical variables.

-
-

Grouped bar plots

-

The bar plot for a single categorical variable shows frequency counts for each level. A grouped bar plot shows a distribution of the second variable for the grouping variable.

-

A useful data structure for this graphic is found using groupby with 2 variables:

-
-
tbl = @chain survey begin
-    select([:Sex, :Smoke])
-    dropmissing
-    groupby([:Sex, :Smoke])
-    combine(nrow => :value)
-end
-
-p1 = @df tbl groupedbar(:Sex, :value, group=:Smoke; xlab="Identified gender")
-p2 = @df tbl groupedbar(:Sex, :value, group=:Smoke; xlab="Identified gender",
-                        bar_position = :stack)
-
-plot(p1, p2, layout = (@layout [a b]))
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 2: Grouped bar chart of smoking distribution for different levels of the :Sex variable. The bar_position argument can be passed a value :stack to use a stacked display.

-
-
-
-

As seen in the left graphic of Figure 2, there are groups of bars for each level of the first variable (:Sex); the groups represent the variable passed to the group keyword argument. The values are looked up in the data frame with the computed column that was named :value through the combine function.

-

The same graphic on the left – without the labeling – is also made more directly with groupedbar(freqtable(survey, :Sex, :Smoke))

-
-
-

Andrews plot

-

An Andrews plot is implemented in StatsPlots showing differences in a collection of numeric variables for a given categorical variable. For each row, a trigonometric polynomial with coefficients given by the numeric values in the given row creates a function which is plotted. If the values across the categorical variable are similar, the graphs will be; if not, then the groupings will show up.

-

We first show an example with the iris data where the categorical value is :Species and the numeric ones the first 4 values. This will be shown in the left graphic of Figure 3:

-
-
iris = dataset("datasets", "iris")
-andrews_1 = @df iris andrewsplot(:Species, cols(1:4));
-
-

For the next plot, we use the survey data. There are some efforts needed to wrangle the data: we convert the categorical variables to the numeric levels (levelcode) except for :Sex which we use for the grouping variable. We also drop any missing values:

-
-
iscategorical(x) = isa(x, CategoricalArray) # predicate function
-tbl = @chain survey begin
-    combine(_, :Sex, findall(iscategorical, eachcol(_))[2:end] .=> ByRow(levelcode);
-            renamecols=false)
-    dropmissing
-end
-
-andrews_2 = @df tbl andrewsplot(:Sex, cols(2:ncol(tbl)));
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 3: Andrews plots of the iris data (left graphic) and the survey data (right graphic). The iris plot shows clear differences based on the :Species variable; the survey data does not for the :Sex variable.

-
-
-
-
-
-

Mosaic plots

-

A mosaic plot presents a graphical view of a two-way contingency table. These are somewhat similar to the grouped bar plot with stacking, but the width of the bars depends on the frequency of the given level.

-

This graphic is not part of StatsPlots. We borrow with modification this implementation from OnlineStats:

-
-
using FreqTables
-mosaic_plot(f, g; kwargs...) = mosaic_plot!(Plots.Plot(), f, g; kwargs...)
-function mosaic_plot!(p, f, g; xrotation=-45, kwargs...)
-    tbl = freqtable(f, g)
-    a = sum(tbl, dims=2)
-    b = sum(tbl, dims=1)
-
-    a′ = [a.array...]
-    x = vcat(0, cumsum(a′)) / sum(a′)
-
-    tbl′ = convert(Matrix{Float64}, tbl.array)
-    tbl′[tbl′ .== 0] .+= 1/10  # give some width when missing
-    m = prop(tbl′, margins=1)
-    y = reverse(cumsum(prop(tbl′, margins=1), dims=2), dims=2)
-
-    bar!(p,
-         midpoints(x), y;
-         legend=false,
-         bar_width = diff(x),
-         xlims=(0,1), ylims=(0,1), linewidth = 0.5,
-         xticks =  (midpoints(x), string.(names(a,1))),
-         xrotation=xrotation, xmirror=true,
-         yticks = (midpoints(vcat(y[1,:],0)), reverse(string.(names(b,2)))),
-         kwargs...
-         )
-end
-
-
mosaic_plot! (generic function with 1 method)
-
-
-

For the first variable, displayed on the \(x\)-axis, the relative width of the bar is proportional to the marginal proportions; for each level on the \(x\)-axis, the vertical bars show the relative proportions of the second variable. For example,

-
-
@df survey mosaic_plot(:Exer, :Smoke, xlab="Exercise", ylab="Smoke")
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 4: Mosaic plot of the Exercise and Smoke variables in the survey data set.

-
-
-
-
-
-
-
-
-
-
    -
  1. The margins argument and dims argument are a bit confusing, a 1 for margins means each row is normalized, a 1 for dims means add down a row. Which is similar, but prop(tbl; margins=1) could be realized through tbl . / sum(tbl, dims=2).↩︎

  2. -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/EDA/makie.html b/EDA/makie.html deleted file mode 100644 index 6d0f5e7..0000000 --- a/EDA/makie.html +++ /dev/null @@ -1,721 +0,0 @@ - - - - - - - - - -makie - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

The AlgebraOfGraphics package

-

The StatsPlots package has been used to illustrate the standard graphics of exploratory statistics. That package leverages Plots, a Julia interface to multiple plotting backends. The GR one renders the images seen. There are a few alternatives. The Makie (Danisch and Krumbiegel 2021) plotting system along with the AlgebraOfGraphics (Vertechi et al. n.d.) package makes a very compelling one.

-

The AlgebraOfGraphics packages offers a declarative style to create statistical graphics. An example from the documentation shows the code to do the following “declare the dataset; declare the analysis; declare the arguments used; declare the grouping and the respective visual attribute; draw the visualization.” This is all done through a series of composable commands, illustrated by example below. The Pumas project has a much more extensive tutorial than is presented here.

-

We will see that it is very easy to visualize multiple variables through an appropriate choice of graphic or transformation, with further choices of coloring, faceting, or other means to demarcate different factors. The “declarative” style shines here, as the user simply specifies a variable, and the package converts this, as needed, to a color or shape .

-

We begin by loading the packages. The CairoMakie backend is used here, GLMakieis good for interactive usage at the command line, WGLMakie is for web-based graphics, all are part of the same Makie plotting ecosystem.

-
-
using StatsBase, DataFrames, CategoricalArrays, RDatasets
-using CairoMakie, AlgebraOfGraphics
-set_aog_theme!()
-
-

We use the color theme of aog, as declared in the last command. The packages are compute-intensive and can take a while to load.

-

Following the package tutorial, we load the Palmer penguins data set of Allison Horst. This includes data collected and made available by Dr. Kristen Gorman and the Palmer Station, Antarctica LTER, a member of the Long Term Ecological Research Network. The data can be downloaded from the GitHub site, but it is also wrapped into a Julia package:

-
-
using PalmerPenguins
-penguins = dropmissing(DataFrame(PalmerPenguins.load()))
-first(penguins, 3)
-
- -
3×7 DataFrame
Rowspeciesislandbill_length_mmbill_depth_mmflipper_length_mmbody_mass_gsex
String15String15Float64Float64Int64Int64String7
1AdelieTorgersen39.118.71813750male
2AdelieTorgersen39.517.41863800female
3AdelieTorgersen40.318.01953250female
-
-
-

This data set has several correlated numeric variables on bill length, bill depth, flipper length, and body mass; and several categorical variables, such as species, island, and sex. An even more complete data set can be downloaded from the GitHub site.

-
-

Univariate graphical summaries

-

We run through the basic graphics for univariate statistics. We shall see that the framework makes multi-variate display quite direct, and at times easier than a univariate display.

-
-

Boxplot and violin plot

-

A boxplot (Figure 1) for each species is created by composing a series of declarative commands:

-
-
p = data(penguins) *
-    visual(BoxPlot) *
-    mapping(:species, :bill_length_mm => "Bill length (mm)", color=:species);
-
-

This illustrates many of the idioms used in the AlgebraOfGraphics.

-

The data(penguins) command sets up the data. Here a data frame is passed, but this can be any Tables compatible structure, such as a struct of arrays such as data((;x, y)) for some pair of variables x and y.

-

The mapping call takes values in the data to positions and attributes of the graphic. It uses position to identify the x, y, and (at times) z values for the graphic. The y variable specification above illustrates a mini language nearly identical to the DataFrames mini language. For a box plot, an indicator of the groups goes in the x position, the data values in the y position. The color=:species argument uses a mapping between the levels of the :species variable and color ramp to give the graphic a distinct color for each species. Omitting this argument produces a monotone graphic with the chosen theme.

-

The visual(BoxPlot) command declares the visualization or transformation to be used to view the data. The visual function expects a type indicating the plot type to use and optional keyword arguments. In this case, BoxPlot is the type associated with the Makie.boxplot function. At times this type must be qualified, such as with Text, for annotations.

-

Both the mapping and visual calls can be used to set attributes:

-
    -
  • visual is used to set attributes for each element independent of the data. For example, a box plot has the argument orientation which is not data dependent, so is adjusted within the visual call.
  • -
  • mapping is used to have attributes depend on values of a variable, like color is used above.
  • -
-

The attributes are those for the underlying plotting function. For visual(BoxPlot), these can be seen at the help page for boxplot, displayed with the command ?boxplot.

-

The mapping calls shows two uses of the mini language for data manipulation. The basic form is source => function => target and works very much like the DataFrames mini language does for select or transform, but unlike those, the function is always applied by row. This makes some transformations, such as \(z\)-scores not possible within this call – transformations requiring the entire column need to be done within the values passed to data. The abbreviated forms are just source, as used with the color=:species argument; source => function; and source => target, such as :bill_length_mm => "bill length (mm)" used to rename the variable for labeling purposes. When the source involves more than one column selector, tuples should be used to group them.

-

A few functions are provided to bypass the usual mapping of the data. (For example, color maps levels of a factor to a color ramp behind the scenes.) Among these are nonnumeric to pass a numeric variable to a value expecting a categorical variable and verbatim to avoid this mapping. The latter, => verbatim, will be necessary to add when annotating a figure.

-

The object p can be rendered to the screen with the draw method resulting in Figure 1. Just draw(p) will render the graphic, the following also shows how the figure keyword argument can be used to set attributes using a named tuple, in this case the figure size. Similarly axis values can be modified in this manner. In the following, we set a title attribute for the axis.

-
-
draw(p; figure=(;size=(600,400)),
-     axis=(; title="Bill length"))
-
-
-
-

-

Figure 1: Boxplots of bill length for the three penguin species in penguins.

-
-
-
-
-
-
-
- -
-
-To save a figure to a file -
-
-
-

The output of draw is used to render to the screen and also to save to a file (as a .png or .svg file). The pattern save("filename.[png|svg]", draw(p)) will save the image to the named file using the given extension to specify the format used.

-
-
-

This is the basic pattern where different choices are combined, or merged, with the * operation. The pieces can be re-purposed. In the following, we make use of this data:

-
-
d = data(penguins);
-
-

Box plots are very effective for quickly comparing distributions of a numeric variable across the levels of some factor. The calling syntax preferences that style, where both an x and y value are specified to mapping. To create a box plot of a single variable, without grouping, the graphic takes a bit more to construct. In the following we create a single valued x variable to produce the upper left graphic in Figure 2:

-
-
p1 = d * visual(BoxPlot) *
-    mapping(1 => one, :bill_length_mm => "Bill length (mm)");
-
-

The mini language is used above two different ways: with a function to create the single value for x (AlgebraOfGraphics will treat this to a factor, so one isn’t needed, just some single-valued function) and with a target for labeling the y variable. As mentioned, such transformations can also be done within the data frame before it is passed to data, which is necessary for some types of transformations.

-

To add another layer, in this case a scatter plot, we can add the plotting objects:

-
-
p2a = d * visual(BoxPlot) * mapping(:species, :bill_length_mm, color=:species)
-p2b = d * visual(Scatter) * mapping(:species, :bill_length_mm)
-p2 = p2a + p2b;
-
-

The Scatter transformation plots pairs of points in a Cartesian plane.

-

Combinations with + add a layer; those with * merge layers. The algebra name also refers to algebraically desirable short cuts. For example, we repeat d and the mapping for each p2a and p2b, but these can be used just once by distributing them:

-
-
m = mapping(:species, :bill_length_mm => "bill length (mm)", color=:species);
-p3 =  d * ( visual(BoxPlot) + visual(Scatter) ) * m;
-
-

Both p2 and p3 are shown in the lower row of Figure 2. There is just one slight difference, the dots representing the data in p2 are not colored, as the mapping did not instruct that in forming p2b.

-

Specifying a violin plot requires just a slight modification to the above: we change the BoxPlot visual to Violin. Violin plots have an argument side that allows both sides of the violin to reflect an extra grouping variable. We use the :sex variable in the following, as it has only two levels. With this, each side of the violin plot reflects grouping by the :sex factor, the legend is used to lookup which level of the factor is represented.

-
-
p4 = d * visual(Violin) * mapping(:species, :bill_length_mm, color=:species, side=:sex);
-
-

The visual(Violin) call wraps the function Makie.violin whose documentation contains additional possible arguments beyond side.

-

The AlgebraOfGraphics package builds on the Makie package and can use its layout system. Makie’s layout system leverages matrix notation to specify cell position. The draw! method accepts a figure object as a first argument. In Figure 2 we layout 2 rows and 2 columns of figures, as follows:

-
-
f = Figure()
-draw!(f[1,1], p1)
-draw!(f[1,2], p4)
-draw!(f[2,1], p2)
-draw!(f[2,2], p3)
-f
-
-
-
-

-

Figure 2: Figure showing four different graphics displayed. In this case, a single boxplot; a violin plot; a boxplot with scatter; and a similar one with the data and mapping easily reused for each visual.

-
-
-
-
-
-
-

Dot plot

-

The boxplot does an excellent job of summarizing a data set with a few indicators making it quite useful when there are many data points. A dot plot is useful when there are a limited number of values and advantageous as the graphic shows all the data.

-

A dot plot (Figure 3) can be constructed easily enough by ensuring, in this case, the y variable is non-numeric:

-
-
huddle = penguins[sample(1:size(penguins,1), 50),:] # a sample
-
-p1 = data(huddle) * visual(Scatter) *
-    mapping(:bill_length_mm=>"Bill length (mm)", :species => nonnumeric);
-
-

(In this example, species is categorical, so the extra => nonnumeric is unnecessary.)

-

Compare the above to a boxplot of the same sampled data:

-
-
p2 = data(huddle) * visual(BoxPlot) *
-    mapping(:species, :bill_length_mm => "Bill length (mm)"; color=:species);
-
-

The boxplot makes it easy to compare medians across the levels of the species factor to gauge graphically if there is a differentiated effect on the response.

-

The following is an enhanced dot plot which emphasizes a comparison of center by adding a line and sorting so that this line only moves to the right as the eye travels up the levels of the factor. The code is a modification of some from (Alday et al. 2022).

-
-
"`dotplot`: show values for each group as dotplot sorted by some center"
-function _arrange_dotplot_data(df, value::Symbol, group::Symbol, center=mean;
-                              jitter=true)
-    transform!(df, value => Array, group => CategoricalArray;
-               renamecols=false) # set up types
-
-    sumry = combine(groupby(df, group), value => center => value)
-    sort!(sumry, value)
-    ordered_levels = string.(sumry[!, group])
-    levels!(sumry[!, group], ordered_levels) # relevel, used in plotting
-    levels!(df[!, group], ordered_levels)
-    jitter && (df = combine(groupby(df, group),
-                            value => (x -> x .+ std(x)/100), renamecols=false))
-
-
-    df, sumry
-end
-
-
-df, sumry = _arrange_dotplot_data(huddle, :bill_length_mm, :species, median)
-mm = mapping(:bill_length_mm => "Bill length (mm)", :species)
-p3 = data(df) * mm *
-    visual(Scatter; marker='○', markersize=12)  # use a character for a marker
-p3 += data(sumry) * mm * visual(Lines);         # add summary line
-
-

All these figures appear in Figure 3.

-
-
-
-
-

-

Figure 3: A basic dot plot, a comparable box plot, and an enhanced dot plot. For small data sets, the dot plot can show comparisons of spread and center quite well; reordering based on the center emphasizes the differentiated effect on the response of the grouping variable.

-
-
-
-
-
-
-

Faceting

-

The package also supports faceting where different panels share the same scales allowing easy cross comparison. Faceting is specified through the keyword layout or either (or both) of row and col keywords. The layout keyword uses levels of the variable name it is passed and arranges the plots over these levels. A col declaration will make columns for each level of the specified variable, whereas a row declaration will create rows for each level of the specified variables. By default both the x and y axes are linked. These linkings can be decoupled when drawing by passing in values to the facet argument, along the lines of: draw(p, facet=(; linkxaxes=:none, linkyaxes=:none)).

-
-
-

Histograms

-

The AlgebraOfGraphics has certain functions it refers to as transformations of the data. These include histogram, density, frequency, linear, smooth, and expectation; most all will be illustrated by example below.

-

These are used like visual was above, but arguments are passed directly to the transformation.

-

The histogram function plays the role of visual in this graphic. (The visual function is still useful to apply data-independent attributes.) Here we arrange to color by species:

-
-
p1 = d * histogram() * mapping(:bill_length_mm, color=:species);
-
-

The histograms overlap. The layout command can be used to declare one panel per level. We do this with :sex:

-
-
p2 = d * histogram() * mapping(:bill_length_mm, color=:species, layout=:sex);
-
-

See Figure 4 for the graphics.

-
-
-

Density plot

-

The histogram function has options for overriding the default bin selection and has several options for scaling the figure through its normalization argument. We use this in the next graphic which layers a density plot over a scaled histogram using the :pdf scaling. The density transformation is qualified with the module name to prevent a conflict with one in Makie1.

-
-
layers = histogram(normalization=:pdf) + AlgebraOfGraphics.density()
-p3 = d * layers * mapping(:bill_length_mm, color=:species, layout=:sex);
-
-

In this next figure we add in a scatter plot of the data on top of the density plots. For the scatter plot, we use the Scatter visual for which we create jittered \(y\) values to disambiguate the data, these are added as a column to the data in d1, below:

-
-
p4a = d *  AlgebraOfGraphics.density() *
-    mapping(:bill_length_mm, color=:species)
-
-d1 = data(transform(penguins,
-                    :bill_length_mm => ByRow(x -> 0.02 * rand()) => :ys))
-
-p4b = d1 * visual(Scatter) * mapping(:bill_length_mm, :ys, color=:species)
-p4 = p4a + p4b;
-
-
-
-
-
-

-

Figure 4: Histogram and Density plots.

-
-
-
-
-
-
-

Quantile-normal plots

-

The QQNorm and QQPlot visuals are used to make quantile-quantile plots; QQNorm expects a mapping to :x (first position) whereas QQPlot expects mappings to :x and :y (the first two positions).

-

The following will give a visual check if bill length is normally distributed, the graphic indicates slightly shorter tails than expected

-
-
p1 = data(penguins) * visual(QQNorm, qqline=:fit) *
-    mapping(:bill_length_mm);
-
-

The following will give a visual check if bill length has a similarly shaped distribution as bill depth, in this case with each species highlighted:

-
-
p2 = data(penguins) * visual(QQPlot, qqline=:fit) *
-    mapping(:bill_length_mm, :bill_depth_mm, color=:species);
-
-

Both are shown in Figure 5.

-
-
-
-
-
-

-
-
-
-
-

-
-
-
-

Figure 5: Quantile-quantile plots. The left graphic uses a reference normal distribution (through QQNorm), the right one uses QQPlot to compare the distribution of two variables after grouping by species.

-
-
-
-
-
-

Line plots

-

A scatter plot shows \(x\) and \(y\) pairs as points, a line plot connects these points. There are numerous ways to draw lines with the AlgebraOfGraphics including: visual(Lines), for connect-the-dots lines; visual(LinesFill), for shading; visual(HLines) and visual(VLines), for horizontal and vertical lines; visual(Rangebars) to draw vertical or horizontal line segments.

-

The graph of a function can be drawn using Lines, as in this example, where we add in different range bars to emphasize the role that the two parameters play in this function’s graph:

-
-
ϕ(x; μ=0, σ=1) = 1/sqrt(2*pi*σ^2) * exp(-(1/(2σ)) * (x - μ)^2)
-
-xs = range(-3, 3, length=251)
-ys = ϕ.(xs)
-c = data((x=xs, y=ys)) * visual(Lines) * mapping(:x, :y)
-
-c += data(DataFrame(x=0, hi=ϕ(0), lo=0)) * visual(Rangebars) *
-    mapping(:x, :hi, :lo)
-
-c += data(DataFrame(xmin=0, xmax=1, y=ϕ(1))) * visual(Rangebars, direction=:x) *
-    mapping(:y, :xmin, :xmax)
-
-c += data((x=[1/10, 1/2], y=[0, ϕ(1)], label=["μ", "σ"])) *
-    visual(Makie.Text) *
-    mapping(:x, :y, text = :label => verbatim)
-draw(c)
-
-

-
-
-

The Rangebars visual has a direction argument, used above to make a horizontal range bar.

-

The annotation has two subtleties: the qualification of Makie.Text is needed, as there is a Text type in base Julia. More idiosyncratically, the use of verbatim in mapping is needed to avoid an attempt to map the labels to a glyph, such as a pre-defined marker.

-
-
-

Bivariate relationships

-

Scatterplots with trend lines are easily produced within the AlgebraOfGraphics framework: the Scatter visual creates scatter plots; for trend lines there is the smooth transformation to fit a loess line, and the linear transformation to fit linear models.

-

This first set of commands shows how to fit a smoother (upper left graphic in Figure 6). The smooth function has arguments which pass on to Loess.loess.

-
-
layers = visual(Scatter) + smooth()
-p1 = d * layers * mapping(:bill_length_mm, :bill_depth_mm);
-
-

The linear function draws the fitted regression line and shades an interval automatically (the interval argument). Linear prediction under model assumptions provides a means to identify confidence intervals for the mean response (the average value were the covariates held fixed and the response repeatedly samples) and for the predicted response for a single observation. The latter are wider, as single observations have more variability than averages of observations. A value of nothing suppresses this aspect.

-

This next set of commands shows (upper-right figure of Figure 6) one way to add a linear regression line. As the mapping for linear does not include the grouping variable, (color) the line is based on all the data:

-
-
d1 = d * mapping(:bill_length_mm, :bill_depth_mm)
-p2a = d1 * visual(Scatter) * mapping(color=:species)
-p2b = d1 * linear()
-p2 = p2a + p2b;
-
-

Whereas with this next specification, color is mapped for both the linear transformation and the Scatter visual. This groups the data and separate lines are fit to each. We can see (lower-left figure of Figure 6) that whereas the entire data shows a negative correlation, the cohorts are all positively correlated, an example of Simpson’s paradox.

-
-
layers = visual(Scatter) + linear()
-p3 = d1 * layers *  mapping(color=:species);
-
-

Adding layout=:sex shows more clearly (lower-right figure of Figure 6) that each group has a regression line fit, that is the multiplicative model is fit.

-
-
p4 = d1 * layers *  mapping(color=:species, layout=:sex);
-
-
-
-
-
-

-

Figure 6: Scatter plots of bill depth by bill width produced by varying specifications.

-
-
-
-
-
-

Corner plot

-

A corner plot, as produced by the PairPlots package through its pairplot function, is a quick plot to show pair-wise relations amongst multiple numeric values. The graphic uses the lower part of a grid to show paired scatterplots with, by default, contour lines highlighting the relationship. On the diagonal are univariate density plots.

-
-
using PairPlots
-nms = names(penguins, 3:5)
-p = select(penguins, nms .=> replace.(nms, "_mm" => "", "_" => " ")) # adjust names
-pairplot(p)
-
-

-
-
-
-
-

3D scatterplots

-

A 3-d scatter plot of 3 numeric variables can be readily arranged, with just one unexpected trick:

-
    -
  • The mapping object should contain an x, y, and z variable specification with numeric variables.

  • -
  • The draw call should include an axis = (type = Axis3,) call, specifying that a 3D (Makie) axis should be used in the display.

  • -
-
-
d = data(penguins)
-p = d * mapping(:bill_length_mm => :bl,  :bill_depth_mm => :bd,  :flipper_length_mm=>:fl; color=:species,
-              row=:sex, col=:island)
-draw(p, axis=((type=Axis3,)))
-
-
-
-

-

Figure 7: 3D scatter plots of bill length, bill depth, and flipper length with faceting by island and sex variables.

-
-
-
-
-
-
-
-

Categorical data

-

The distribution of the surveyed species is not the same. A bar chart can illustrate (upper-left graphic of Figure 8). The frequency transform does the counting:

-
-
p1 = d * frequency() * mapping(:species);
-
-

Two categories can be illustrated, we need dodge set here to avoid overplotting of the bars. In this example, following the AlgebraOfGraphics tutorial, we add in information about the island. This shows (upper-right graphic of Figure 8) that two species are found on just 1 island, whereas Adelie is found on all three.

-
-
p2 = d * frequency() *
-    mapping(:species, color=:island, dodge=:island);
-
-

Using stack in place of dodge presents a stacked bar chart (lower-left graphic of Figure 8):

-
-
p3 = d * frequency() *
-    mapping(:species, color=:island, stack=:island);
-
-

A third category can be introduced using layout, col, or row (lower-right graphic of Figure 8):

-
-
p4 = d * frequency() *
-    mapping(:species, color=:island, stack=:island) *
-    mapping(row=:sex);
-
-
-
-
-
-

-

Figure 8: Scatter plots of bill depth by bill width produced by varying specifications.

-
-
-
-
-
-
-

Customizing plots through axis

-

There are a numerous customizations available when drawing a plot. We discuss a small handful of them here. See the PumasAI tutorial and the documentation for more details.

-

The draw command allows the passing of values to the axis mechanism of Makie. This allows customization of various features such as the title, the ticks, the aspect ration, and the grids.

-

Makie plots are themeable. In the above we used set_aog_theme!(). This theme sets a number of defaults for the axis attributes:

-
Axis = (
-        xgridvisible=false,
-        ygridvisible=false,
-        topspinevisible=false,
-        rightspinevisible=false,
-        bottomspinecolor=:darkgray,
-        leftspinecolor=:darkgray,
-        xtickcolor=:darkgray,
-        ytickcolor=:darkgray,
-        xticklabelfont=lightfont,
-        yticklabelfont=lightfont,
-        xlabelfont=mediumfont,
-        ylabelfont=mediumfont,
-        titlefont=mediumfont,
-    )
-

To override these or pass other attributes on to the rendering, the axis keyword argument accepts a named tuple of values. So, for example, to set the graphics title, we would see axis=(; title="Some title"), to instruct the labels in a barplot on the x axis to be rotated, we would see axis=(; xticklabelrotation = pi/2). Of course these would typically combined, as above.

-

The following lists some useful attributes. A complete list is in the Makie docs for the Axis constructor.

-

The aspect ratio for a graphic is adjustable through the aspect attribute.

-

The following labeling attributes can be adjusted: title, subtitle, xlabel, ylabel. These take a string (or an observable) for the value to display. This value can be adjusted, for example, there are titlealign, titlecolor, titlefont, titlesize, and titlevisible attributes. Similar attributes exist for the other labels.

-

An axis has ticks. These are often numbers. For the ticks on an x axis there are attributes xticks, xtickcolor, xtickformat, xticksize, and xtickwidth. Similarly with y. There are also minor ticks, adjustable with, for example, xminorticks, xminortickcolor, xminorticksize, etc.

-

For ticks representing categorical values, labels are used. Attributes for tick labels include: xticklabelalign, xticklabelcolor, xticklabelfont, xticklabelrotation, and xticklabelsize.

-

The displayed grid is adjustable through attributes like xgridcolor, xgridstyle, xgridvisible, xgridwidth, along with “minor” versions.

-

For 3 dimension plots, the Axis3 object is used for display. This has similarly named attributes for z values.

-
-
-Alday, P., Kliegl, R., and Bates, D. (2022), Embrace uncertainty: Fitting mixed-effects models with julia, https://juliamixedmodels.github.io/EmbraceUncertainty/. -
-
-Danisch, S., and Krumbiegel, J. (2021), “Makie.jl: Flexible high-performance data visualization for julia,” Journal of Open Source Software, The Open Journal, 6, 3349. https://doi.org/10.21105/joss.03349. -
-
-Vertechi, P., and others (n.d.). AlgebraOfGraphics.jl. -
-
-
-
-
-
-
    -
  1. The Makie density function could be accessed through visual(Density) without module qualification. The density function in AlgebraOfGraphics has a nice transparency feature which makes its use desirable.↩︎

  2. -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/EDA/tabular-data-julia.html b/EDA/tabular-data-julia.html deleted file mode 100644 index 3ba8929..0000000 --- a/EDA/tabular-data-julia.html +++ /dev/null @@ -1,1733 +0,0 @@ - - - - - - - - - -tabular-data-julia - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

Tabular data

-

Some data sets are naturally stored in a tabular format, like a spreadsheet. This chapter looks at such data.

-

In the following, we will utilize these basic packages, among others introduced along the way:

-
-
using StatsBase, StatsPlots
-
-
-

Data frames

-

The basic framework for exploratory data analysis is a data set with 1 or more observations for \(n\) different cases. For each case (or observation) a variable will be a length-\(n\) data set, with values measuring the same type of quantity, or perhaps missing. The variables can be stored as vectors. The collection of variables is traditionally arranged in a rectangular manner with each row representing the measurements for a given case and each column representing the measured values for each variable. That is the columns are homogeneous (as they measure the same thing), but the rows need not be (as different variables are measuring different quantities).

-

Matrices contain rectangular data, but are homogeneous. As such, a new structure is needed to store tabular data in general. A data frame is a concept borrowed from S Plus where they were introduced in 1991. The R language has data frames as a basic built-in data container. In Julia the structure is implemented in an external package, DataFrames.

-
-
using DataFrames
-
-
-
-
- -
-
-An OG package -
-
-
-

The DataFrames package dates back to 2012, at least, and must be one of Julia’s oldest packages. Julia was on version 0.1 then. It is also under continual development and has a comprehensive set of documentation and tutorials. Bouchet-Valat and Kamiński (2023) has an excellent overview including comparisons to other implementations in other languages and context for certain design decisions. This introduction attempts to illustrate some basic usage; consult the provided documentation for additional details and applications.

-
-
-
-

Data frame construction

-

There are different ways to construct a data frame.

-

Consider the task of the Wirecutter in trying to select the best carry on travel bag. After compiling a list of possible models by scouring travel blogs etc., they select some criteria (capacity, compartment design, aesthetics, comfort, …) and compile data, similar to what one person collected in a spreadsheet. Here we create a much simplified spreadsheet for 3 listed bags with measurements of volume, price, laptop compatability, loading style, and a last-checked date – as this market improves constantly.

-
product         v  p    l loads       checked
-Goruck GR2      40 395  Y front panel 2022-09
-Minaal 3.0      35 349  Y front panel 2022-09
-Genius          25 228  Y clamshell   2022-10
-

We see that product is a character, volume and price numeric, laptop compatability a Boolean value, load style one of a few levels, and the last checked date, a year-month date.

-

We create vectors to hold each. We load the CategoricalArrays and Dates packages for a few of the variables:

-
-
using CategoricalArrays, Dates
-product = ["Goruck GR2", "Minaal 3.0", "Genius"]
-volume = [40, 35, 25]
-price = [395, 349, 228]
-laptop_compatability = categorical(["Y","Y","Y"])
-loading_style = categorical(["front panel", "front panel", "clamshell"])
-date_checked = Date.(2022, [9,9,10])
-
-
3-element Vector{Date}:
- 2022-09-01
- 2022-09-01
- 2022-10-01
-
-
-

With this, we use the DataFrame constructor to combine these into one data set.

-
-
d = DataFrame(product = product, volume=volume, price=price,
-              var"laptop compatability"=laptop_compatability,
-              var"loading style"=loading_style, var"date checked"=date_checked)
-
- -
3×6 DataFrame
Rowproductvolumepricelaptop compatabilityloading styledate checked
StringInt64Int64Cat…Cat…Date
1Goruck GR240395Yfront panel2022-09-01
2Minaal 3.035349Yfront panel2022-09-01
3Genius25228Yclamshell2022-10-01
-
-
-

The call to the constructor is like that of a named tuple and again, var"..." is employed for names with spaces.

-

As called above, the constructor binds vectors of equal length into a new multi-variate container.

-

The describe function will summarize a data frame using different summaries for different types:

-
-
describe(d)
-
- -
6×7 DataFrame
Rowvariablemeanminmedianmaxnmissingeltype
SymbolUnion…AnyAnyAnyInt64DataType
1productGeniusMinaal 3.00String
2volume33.33332535.0400Int64
3price324.0228349.03950Int64
4laptop compatabilityYY0CategoricalValue{String, UInt32}
5loading styleclamshellfront panel0CategoricalValue{String, UInt32}
6date checked2022-09-012022-09-012022-10-010Date
-
-
-

In the above construction, we repeated the names of the variables to the constructor. A style, also akin to the construction of named tuple, could also have been used where variables after a semicolon are implicitly assumed to have the desired name:

-
-
d = DataFrame(; product, volume, price,
-              var"laptop compatability"=laptop_compatability,
-              var"loading style"=loading_style, var"date checked"=date_checked)
-
- -
3×6 DataFrame
Rowproductvolumepricelaptop compatabilityloading styledate checked
StringInt64Int64Cat…Cat…Date
1Goruck GR240395Yfront panel2022-09-01
2Minaal 3.035349Yfront panel2022-09-01
3Genius25228Yclamshell2022-10-01
-
-
-

(In fact, with the rename! function, or other conveniences, it can be more direct to define the data frame, then rename the columns, avoiding the var above.)

-

We see that the DataFrame type displays values as we might expect with the column header showing the name of the variable and the storage type. The row numbers are added and may be used for reference to the data values.

-

There is a convention for attaching new columns to a data frame that allows another alternate construction:

-
-
d = DataFrame()   # empty data frame
-d.product = product
-d.volume = volume
-d.price = price
-d."laptop compatability" = laptop_compatability
-d."loading style" = loading_style
-d."date checked" = date_checked
-d
-
- -
3×6 DataFrame
Rowproductvolumepricelaptop compatabilityloading styledate checked
StringInt64Int64Cat…Cat…Date
1Goruck GR240395Yfront panel2022-09-01
2Minaal 3.035349Yfront panel2022-09-01
3Genius25228Yclamshell2022-10-01
-
-
-

This uses one of a few different ways to refer to a column in a data frame.

-

As another alternative, the insertcols! function can insert multiple columns, for example the last one in the example, could have been written insertcols!(d, "date checked" => date_checked).

-

Before exploring how we might query the data frame, we note that an alternate means to describe the data set is by row, or case. For each row, we might use a named tuple with the names indicating the variable, and the values the measurement. The DataFrame constructor would accept this, here we shorten the names and only do one row (for now):

-
-
d = DataFrame([
- (b = "Goruck GR2", v = 40, p = 395, lap = "Y", load = "front panel", d = Date("2022-09-01"))
-])
-
- -
1×6 DataFrame
Rowbvplaploadd
StringInt64Int64StringStringDate
1Goruck GR240395Yfront panel2022-09-01
-
-
-

Here, the variable types are different, as it takes more effort to make one categorical. The same way we defined a column allows us to redefine that column (replacing the container, not re-using it for storage). For example, we could do:

-
-
d.lap = categorical(d.lap)
-d.load = categorical(d.load)
-
-
1-element CategoricalArray{String,1,UInt32}:
- "front panel"
-
-
-

and then the two variables would be categorical.

-

There are other ways to define row by row:

-
    -
  • A data frame with empty variables can be constructed and then values as tuples may be push!ed onto the data frame
  • -
-
-
push!(d,  ("Minaal 3.0", 35, 349, "Y", "front panel", Date("2022-09-01")))
-
- -
2×6 DataFrame
Rowbvplaploadd
StringInt64Int64Cat…Cat…Date
1Goruck GR240395Yfront panel2022-09-01
2Minaal 3.035349Yfront panel2022-09-01
-
-
-

A named tuple can also be used.

-
    -
  • Similarly, rows specified by dictionaries may be pushed onto a data frame
  • -
-
-
push!(d, Dict(:b => "Genius", :v => 25, :p => 228, :lap => "Y",
-              :load => "clamshell", :d => Date("2022-10-01")))
-
- -
3×6 DataFrame
Rowbvplaploadd
StringInt64Int64Cat…Cat…Date
1Goruck GR240395Yfront panel2022-09-01
2Minaal 3.035349Yfront panel2022-09-01
3Genius25228Yclamshell2022-10-01
-
-
-

(A dictionary is a key => value container like a named tuple, but keys may be arbitrary Julia objects – not always symbols – so we explicitly use symbols in the above command.)

-
-
-
- -
-
-The Tables interface -
-
-
-

In the Julia ecosystem, the Tables package provides a lightweight means to describe tabular data, like a data frame, TableOperations provides some basic means to query the data. The different ways in which a data frame can be defined, reflect the different ways Tables objects can be created.

-

As tables are viewed as rows of homogeneous types, a vector of named tuples is a natural description, whereas tables are also naturally viewed as a collection of named columns, each column a vector. As such, both of these specify a table.

-
    -
  • As a struct of arrays. This is used by calling DataFrame(a=[1,2], b= [2,1]), say.
  • -
  • As an array of structs. This is used by calling DataFrame([(a=1,b=2), (a=2,b=1)])
  • -
-
-
-
-

RDataset

-

Data frames are often read in from external sources, and not typed in. The RDataSets package has collected data sets from many different R packages and bundled them into data sets for Julia in the form of data frames. We will use some of these data sets in the examples. To load a data set we need to know its R package and R name:

-
-
using RDatasets
-cars = dataset("MASS", "Cars93")  # package, data set name
-first(cars, 2)  # first 2 rows only
-
- -
2×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1AcuraIntegraSmall12.915.918.82531NoneFront41.814063002890Yes13.25177102683726.5112705non-USAAcura Integra
2AcuraLegendMidsize29.233.938.71825Driver & PassengerFront63.220055002335Yes18.05195115713830.0153560non-USAAcura Legend
-
-
-
-
-

CSV.read

-

For interacting with spreadsheet data, when it is stored like a data frame, it may be convenient to export the data in some format and then read that into a Julia session. A common format is to use comma-separated values, or “csv” data. The CSV package reads such files. The CSV package reads in delimited text data (the source) using the Tables interface and can be instructed to write to a data frame (the sink). The CSV.read file is used below. Here we write to some file to show that we can read it back:

-
-
using CSV
-f = tempname() * ".csv"
-CSV.write(f, d)  # write simple data frame to file `f`
-d1 = CSV.read(f, DataFrame)
-
- -
3×6 DataFrame
Rowbvplaploadd
String15Int64Int64String1String15Date
1Goruck GR240395Yfront panel2022-09-01
2Minaal 3.035349Yfront panel2022-09-01
3Genius25228Yclamshell2022-10-01
-
-
-

Comma-separated-value files are simple text files, with no metadata to indicate what type a value may be.1 Some guesswork is necessary. We observe that the two categorical variables were read back in as strings. As seen above, that can be addressed through column conversion.

-

The filename, may be more general. For example, it could be download(url) for some url that points to a csv file to read data from the internet.

-
-
-
- -
-
-Read and write -
-
-
-

The methods read and write are qualified in the above usage with the CSV module. In the Julia ecosystem, the FileIO package provides a common framework for reading and writing files; it uses the verbs load and save. This can also be used with DataFrames, though it works through the CSVFiles package – and not CSV, as illustrated above. The read command would look like DataFrame(load(fname)) and the write command like save(fname, df). Here fname would have a “.csv” extension so that the type of file could be detected.

-
-
- - ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Basic usage to read/write .csv file into a data frame.
CommandDescription
CSV.read(file_name, DataFrame)Read csv file from file with given name
CSV.read(IOBuffer(string), DataFrame)Read csv file from a string using an IOBuffer
CSV.read(download(url), DataFrame)Read csv file an url
open(file_name,"w") do io; CSV.write(io, dd); endWrite data frame df to csv file
DataFrame(load(file_name))Read csv file from file with given name using CSVFiles
save(file_name, df)Write data frame df to a csv file using CSVFiles
-
-
-

TableScraper

-

The TableScraper package can scrape tables from an HTML file in a manner that can be passed to DataFrame for conversion to a data frame. The command DataFrame.(scrape_tables(url)) may just work to create the tables, but in practice, only well-formed tables can be scraped successfully; tables used for formatting purposes may fail. TableScraper is built on Cascadia and Gumbo, as is the Harbest package, which provides more access to the underlying data than TableScraper.

-
-
-
-

Column names

-

The variable names of a data set are returned by the names function, as strings:

-
-
names(d)
-
-
6-element Vector{String}:
- "b"
- "v"
- "p"
- "lap"
- "load"
- "d"
-
-
-

This function has a second argument which can be used to narrow or filter the names that are returned. For example, the following is a convenience for matching columns whose element type is a subtype of Real:

-
-
names(d, Real)
-
-
2-element Vector{String}:
- "v"
- "p"
-
-
-

The rename! function allows the names to be changed in-place (without returning a new data frame). There are many different calling patterns, but:

-
    -
  • to replace one name, the form rename!(df, :v => :nv) is used, where :v is the old name, and :nv the new one. The form follows the generic replace! function. The pair notation can be broadcast (.=>) to replace more than one name, as with replace!(df, [:v1, :v2] .=> [:nv1, :nv2]).
  • -
  • to replace allnames, the form rename!(df, [...]) is used, where the vector is of length matching the number of columns in df.
  • -
-
-
-

Indexing and assignment

-

The values in a data frame can be referenced programatically by a row number and column number, both 1-based. For example, the 2nd row and 3rd column of d can be seen to be 349 by observation

-
-
d
-
- -
3×6 DataFrame
Rowbvplaploadd
StringInt64Int64Cat…Cat…Date
1Goruck GR240395Yfront panel2022-09-01
2Minaal 3.035349Yfront panel2022-09-01
3Genius25228Yclamshell2022-10-01
-
-
-

but it can be referenced through indexing (row number first, column number second):

-
-
d[2, 3]
-
-
349
-
-
-

A new value can also be assigned with the same indexing reference. Suppose, that bag went on sale. This new price could be adjusted, as follows:

-
-
d[2, 3] = 309 # a sale price
-
-
309
-
-
-
-

Column selectors

-

The 3rd column has a name, p in this case. We can use a column selector to avoid having to know which column number a variable is. For this a string or a symbol may be used, as in:

-
-
d[2, :p], d[2, "p"]
-
-
(309, 309)
-
-
-

More generally, it is possible to use more than 1 column in column selection. For example, to get both the volume and price, for the second bag we have:

-
-
d[2, [:v, :p]]
-
- -
DataFrameRow (2 columns)
Rowvp
Int64Int64
235309
-
-
-

As well, either [2, 3] or ["v", "p"] could have been used.

-

For data frames with many columns, a regular expression can be used. The column selector r"^l" would match all columns whose name begins with l (lap and load) in this case. (Regular expressions can be very complicated, but here we only assume that r"name" will match “name” somewhere in the string; r"^name" and r"name$" will match “name” at the beginning and ending of a string. Using a regular expression will return a data frame row (when a row index is specified) – not a value – as it is possible to return 0, 1 or more columns in the selection.

-

Column selectors, as seen may be column number(s), column names as strings, column names as symbols, and regular expressions.

-

In addition:

-
    -
  • Boolean vectors of a length matching the number of columns (d[2, [false, true, true, false, false, false]] would return the 2nd and 3rd columns.)
  • -
  • The value All() which selects all columns
  • -
  • the value Between(a, b) where, a and b are column selectors (such as Between(:v, 3)).
  • -
  • Not(...) to select those columns not specified in ...
  • -
  • Cols(a,b,...) which combines different selectors, such as Cols(:v, 3).
  • -
-
-
-

Slices

-

The All() column selector has a shortcut inherited from the indexing of arrays in base Julia, this being a colon, :. When used in the column position it refers to all the values in the row:

-
-
d[2, :]
-
- -
DataFrameRow (6 columns)
Rowbvplaploadd
StringInt64Int64Cat…Cat…Date
2Minaal 3.035309Yfront panel2022-09-01
-
-
-

When used in the row position, it refers to all rows:

-
-
d[:, 3]
-
-
3-element Vector{Int64}:
- 395
- 309
- 228
-
-
-

In the above use, a vector is returned, not a data frame, as only one column was selected. But which vector? Is it a copy of the one that d holds, or is it the same container? In base Julia the answer depends on what side of an equals sign the use is on (meaning, if used in assignment the answer is different, as mentioned later). In this usage, a copy is returned.

-

To get the underlying column (not a copy), the selector ! is used instead:

-
-
d[!, 3]
-
-
3-element Vector{Int64}:
- 395
- 309
- 228
-
-
-

The notation d.p also refers to the 3rd variable. In this case, d.p is a view of the underlying data (using !) and not a copy.

-

The notation d[:,:] will refer to all rows and all columns of d, and in this case will be a copy of the data frame.

-
-
-

Assignment

-

Single values can be assigned, as previously illustrated. These values are assigned to the underlying vector in the data frame, so as with vectors, the assigned value will be promoted to the column type, if necessary. Assigning a value that can’t be promoted will throw and error, as it would were such an assignment tried for a vector. To do such an assignment, the underlying vector must be changed to a wider type.

-

The row selector : or ! had different semantics when accessing the underlying data, the first returning a copy, the second a view. When used with assignment though, : refers to the underlying vector or does in-place assignment; whereas ! (and the dot access) will replace the the underlying container with the assigned container.

-

To illustrate, suppose the prices are rounded down, but actually contain an extra 99 cents. We can reassign price as following:

-
-
d[!, :p] = price .+ 0.99
-
-
3-element Vector{Float64}:
- 395.99
- 349.99
- 228.99
-
-
-

as even though price .+ 0.99 is no longer an integer vector, the ! row indicator will instruct d to point to this new vector. Had d[:, :p] = ... been used above, an error would have been thrown as the underlying container is integer, but the new prices require floating point values to represent them.

-

Broadcast assignment (using .=) will be similar. The right hand side is expanded, and then assigned. Attempts to assign the wrong value type using : to indicate the rows will error.

-
-
-

Missing values

-

Trying to assign a value of missing to a data frame requires the underlying vector to allow missing values. As with vectors, the allowmissing function is useful. For example, to give the 2nd bag a price of missing, we could do:

-
-
allowmissing!(d, :p)
-d[2, :p] = missing
-
-
missing
-
-
-

By using the mutating form (with the trailing !), the data frame d is mutated in the allowmissing! call. This could be observed by looking a the underlying column type.

-

The new type is a union of the old type and Missing, so we could reassign the old value:

-
-
d[2, :p] = 349.99
-d
-
- -
3×6 DataFrame
Rowbvplaploadd
StringInt64Float64?Cat…Cat…Date
1Goruck GR240395.99Yfront panel2022-09-01
2Minaal 3.035349.99Yfront panel2022-09-01
3Genius25228.99Yclamshell2022-10-01
-
-
-
-
-

Tables.jl interface

-

A table, like a data frame can be expected to loop, or iterate, over columns, say to find which columns are numeric. Or, they can be expected to iterate over rows, say to find which values are within a certain range. What should be the case with a data frame? As there are reasons to prefer either, none is specified. Rather, there is a difference based on the calling function. Data frames respect the Tables interface, which has methods to iterate over rows or columns. When iterating over columns, the individual vectors are given; when iterating over rows, a DataFrameRow object is returned. Calling copy on these rows objects will return a named tuple.

-

The basic iterators are eachrow and eachcol. For example, to see the column type for a data frame, we can broadcast eltype over the variables returned by eachcol:

-
-
eltype.(eachcol(d))  |> permutedims
-
-
1×6 Matrix{Type}:
- String  Int64  Union{Missing, Float64}  …  Date
-
-
-

To get the data frame as a vector of named tuples, then broadcasting NamedTuple over eachrow can be used: NamedTuple.(eachrow(d)); Base.copy is an alias. Tables.rowtable(d) will produce this vector of named tuples, as well.

-
-
-
-

Sorting

-

A data frame can be sorted by specifying a permutation of the indices, such as is returned by sortperm. For example, d[sortperm(d.price), :] will sort by price in increasing order. The keyword argument rev can be passed true to reverse the default sort order.

-

The generic sort function has a method for data frames, that offers a more convenient functionality. The sort! method does in-place sorting. The simplest call is to pass in a column to sort by, as in

-
-
sort(d, :p)
-
- -
3×6 DataFrame
Rowbvplaploadd
StringInt64Float64?Cat…Cat…Date
1Genius25228.99Yclamshell2022-10-01
2Minaal 3.035349.99Yfront panel2022-09-01
3Goruck GR240395.99Yfront panel2022-09-01
-
-
-

This sorts by the price (:p) variable in increasing order. To sort by decreasing order a keyword argument rev=true can be specified:

-
-
sort(d, "v"; rev=true)
-
- -
3×6 DataFrame
Rowbvplaploadd
StringInt64Float64?Cat…Cat…Date
1Goruck GR240395.99Yfront panel2022-09-01
2Minaal 3.035349.99Yfront panel2022-09-01
3Genius25228.99Yclamshell2022-10-01
-
-
-

There can be one or more column selectors. For this, the order function can be used to reverse just one of the columns, as in the following, which first sorts the dates in reverse order (newest first) and then within a data, uses lowest price first to sort:

-
-
sort(d, [order("d", rev=true), :p])
-
- -
3×6 DataFrame
Rowbvplaploadd
StringInt64Float64?Cat…Cat…Date
1Genius25228.99Yclamshell2022-10-01
2Minaal 3.035349.99Yfront panel2022-09-01
3Goruck GR240395.99Yfront panel2022-09-01
-
-
-
-
-

Filtering rows

-

Filtering, or subsetting, is a means to select a specified selection of rows.

-

Let’s consider the cars data set, previously defined by loading dataset("MASS", "Cars93"). This data set consists of 27 measurements on 93 cars from the 1990s. The data set comes from R’s MASS package, and is documented there.

-

Two ready ways to filter are by using specific indices, or by using Boolean indices generated by a logical comparison.

-

For the cars data set, the latter can be used to extract the Volkswagen models. To generate the indices we compare the :Manufacturer variable with the string Volkswagen to return a vector of length 93 that indicates if the manufacturer is VW. This is done with broadcasting: cars.Manufacturer .== "Volkswagen". We use the easy dot access to variables in cars, alternatively cars[:, :Manufacturer] (or, more efficiently, !, as is used by the dot access) could be used were :Manufacturer coming from some variable. With this row selector, we have:

-
-
cars[cars.Manufacturer .== "Volkswagen", :]
-
- -
4×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenEurovanVan16.619.722.71721NoneFront52.510945002915Yes21.17187115723834.0missing3960non-USAVolkswagen Eurovan
3VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
4VolkswagenCorradoSporty22.923.323.71825NoneFront62.817858002385Yes18.5415997663626.0152810non-USAVolkswagen Corrado
-
-
-

This approach lends itself to the description “find all rows matching some value” then “extract the identified rows,” – written as two steps to emphasize there are two passes through the data. Another mental model would be loop over the rows, and keep those that match the query. This is done generically by the filter function for collections in Julia or by the subset function of DataFrames.

-

The filter(predicate, collection) function is used to identify just the values in the collection for which the predicate function returns true. When a data frame is used with filter, the iteration is over the rows, so the wrapping eachrow iterator is not needed. We need a predicate function to replace the .== above. One follows. It doesn’t need .==, as r is a data frame row and access produces a value not a vector:

-
-
filter(r -> r.Manufacturer == "Volkswagen", cars)
-
- -
4×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenEurovanVan16.619.722.71721NoneFront52.510945002915Yes21.17187115723834.0missing3960non-USAVolkswagen Eurovan
3VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
4VolkswagenCorradoSporty22.923.323.71825NoneFront62.817858002385Yes18.5415997663626.0152810non-USAVolkswagen Corrado
-
-
-

There are some conveniences that have been developed to make the predicate functions even easier to write, which will be introduced incrementally:

-

The basic binary comparisons, like ==, are defined by functions essentially of the form (a,b) -> ==(a,b). There are partially applied versions, essentially a -> ==(a,b), with b applied, formed by ==(b). So the syntax ==("Volkswagen") is a predicate taking a single argument which is then compared for equality to the string "Volkswagen".

-

However, r is a data frame row, what we need to pass to ==("Volkswagen") is the value of the :Manufacturer column. For this another convenience is available.

-

The pair syntax, => of the form column selector(s) => predicate function will pass just the value(s) in the column(s) to the predicate function. Combined, the above can be simplified to:

-
-
filter(:Manufacturer => ==("Volkswagen"), cars)
-
- -
4×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenEurovanVan16.619.722.71721NoneFront52.510945002915Yes21.17187115723834.0missing3960non-USAVolkswagen Eurovan
3VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
4VolkswagenCorradoSporty22.923.323.71825NoneFront62.817858002385Yes18.5415997663626.0152810non-USAVolkswagen Corrado
-
-
-

It doesn’t save much for this simple example, but this specification style is the basis of the DataFrames mini language, which provides a standardized and performant means of wrangling a data frame.

-

Filtering may be done on one or more values. There are different approaches.

-

For example, to first filter by the manufacturer, then by city mileage, we might write a more complicated predicate function using && to combine Boolean values:

-
-
pred(r) = r.Manufacturer == "Volkswagen" && r.MPGCity >= 20
-filter(pred, cars)
-
- -
2×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
-
-
-

The mini language can also be used. On the left hand side of (the first) => a vector of column selectors may be indicated, in which the predicate function (on the right hand side of the first =>) will get multiple arguments that can be used to implement the logic:

-
-
pred(m, mpg) = m == "Volkswagen" && mpg >= 20
-filter([:Manufacturer, :MPGCity] => pred, cars)
-
- -
2×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
-
-
-

Finally, we could use filter twice:

-
-
cars1 = filter(:Manufacturer => ==("Volkswagen"), cars)
-cars2 = filter(:MPGCity => >=(20), cars1)
-
- -
2×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
-
-
-

The above required the introduction of an intermediate data frame to store the result of the first filter call to pass to the second. This threading through of the modified data is quite common in processing pipelines. The first two approaches with complicated predicate functions can grow unwieldly, so staged modification is common. To support that, the chaining or piping operation (|>) is often used:

-
-
filter(:Manufacturer => ==("Volkswagen"), cars) |>
-    d -> filter(:MPGCity => >=(20), d)
-
- -
2×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
-
-
-

The right-hand side of |> expects a function for application, so an anonymous function is created. There are macros available in user-contributed packages that shorten this pattern by using a placeholder.

-

A popular one is, @chain, in the Chain package, with its basic usage based on two simple principles:

-
    -
  • a new line is implicitly a pipe
  • -
  • a _ receives the argument from the pipe. If none is given, the first argument does
  • -
-

For example,

-
-
using Chain
-
-
-
@chain cars begin
-    filter(:Manufacturer => ==("Volkswagen"), _)
-    filter(:MPGCity => >=(20), _)
-end
-
- -
2×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
-
-
-
-

Subset

-

The calling style filter(predicate, collection) is consistently used with several higher-order functions, for example, map and reduce and is supported by Julia’s do syntax.

-

However, with data frames, the convention is to have action functions expect the data frame in the first position.

-

The subset function also returns a filtered copy of the data frame with rows matching a certain selection criteria. It’s usage is somewhat similar to filter with the order of its arguments reversed, but the predicate must return a vector of indices, so broadcasting or the ByRow function may be necessary.

-

For example, these produce the same data frame:

-
-
d1 = subset(cars, :Manufacturer => m -> m .== "Volkswagen") # use .== not just ==
-d2 = subset(cars, :Manufacturer => ByRow(==("Volkswagen"))) # use `ByRow` to ensure predicate is applied to each
-
- -
4×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenEurovanVan16.619.722.71721NoneFront52.510945002915Yes21.17187115723834.0missing3960non-USAVolkswagen Eurovan
3VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
4VolkswagenCorradoSporty22.923.323.71825NoneFront62.817858002385Yes18.5415997663626.0152810non-USAVolkswagen Corrado
-
-
-

Unlike filter, subset allows multiple arguments to be applied:

-
-
subset(cars,
-       :Manufacturer => ByRow(==("Volkswagen")),
-       :MPGCity      => ByRow(>=(20)))
-
- -
2×27 DataFrame
RowManufacturerModelTypeMinPricePriceMaxPriceMPGCityMPGHighwayAirBagsDriveTrainCylindersEngineSizeHorsepowerRPMRevPerMileManTransAvailFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleRearSeatRoomLuggageRoomWeightOriginMake
Cat…StringCat…Float64Float64Float64Int32Int32Cat…Cat…Cat…Float64Int32Int32Int32Cat…Float64Int32Int32Int32Int32Int32Float64?Int32?Int32Cat…String
1VolkswagenFoxSmall8.79.19.52533NoneFront41.88155002550Yes12.4416393633426.0102240non-USAVolkswagen Fox
2VolkswagenPassatCompact17.620.022.42130NoneFront42.013458002685Yes18.55180103673531.5142985non-USAVolkswagen Passat
-
-
-

The mini language uses a default of identity for the function so, if the columns are Boolean, they can be used to subset the data:

-
-
dd = DataFrame(a = [true, false], b = [true, missing])
-subset(dd, :a)  # just first row
-
- -
1×2 DataFrame
Rowab
BoolBool?
1truetrue
-
-
-

For data with missing values, like in the :b column of dd, the comparison operators implement 3-valued logic, meaning the response can be true, false, or missing. Using subset(dd, :b) above will error. The keyword skipmissing can be passed true to have these skipped over. The default is false.

-
-
subset(dd, :b; skipmissing=true)
-
- -
1×2 DataFrame
Rowab
BoolBool?
1truetrue
-
-
-

The dropmissing function filters out each row that has a missing value.

-
-
-
-

Selecting columns; transforming data

-

Filtering of a data frame can be viewed as accessing the data in df with a boolean set of row indices, as in df[inds, :]. (There are flags to have a “view” of the data, as in df[inds, !]). Filtering returns a data frame with a possibly reduced number of rows, and the same number of columns.

-

The select function is related in that it can be viewed as passing a Boolean vector of column indices to select specific columns or variables, as in df[:, inds]. The syntax of select also extends this allowing transformations of the data and reassignment, as could be achieved through this pattern: df.new_variable = f(other_variables_in_df).

-

The select function will return a data frame with the same number of rows (cases) as the original data frame. The variables returned are selected using column selectors. There is support for the DataFrames mini language.

-

First, we can select one or more columns by separating column selectors with commas. In the following, using the backpack data in the variable d, we select the first column by column number, the second using a symbol, the third by a string, and the fourth and fifth using a regular expression:2

-
-
select(d, 1, :v, "p", r"^l")
-
- -
3×5 DataFrame
Rowbvplapload
StringInt64Float64?Cat…Cat…
1Goruck GR240395.99Yfront panel
2Minaal 3.035349.99Yfront panel
3Genius25228.99Yclamshell
-
-
-

One use of select is to rearrange the column order, say by moving a column to the front. The : or All() column selector will add the rest. That is this command would move “d” to the front of the other columns: select(d, :d, All()).

-

Selection also allows an easy renaming of column names, using the pairs notation column name => new column name:

-
-
select(d, :v => :volume, "p" => "price")
-
- -
3×2 DataFrame
Rowvolumeprice
Int64Float64?
140395.99
235349.99
325228.99
-
-
-

This usage is a supported abbreviation of source_column => function => target_column_name, where the function is identity. Here the function is not necessarily a predicate function, as it is with subset, but rather a function which returns a vector of the same length as the number of columns in the data frame (as select always returns the same number of columns as the data frame it is passed.)

-

The function receives column vectors and should return a column vector. For a scalar function, it must be applied to each entry of the column vector, that is, each row. Broadcasting is one manner to do this. As used previously, the DataFrames package provides ByRow to also apply a function to each row of the column vector(s).

-

For example to compute a ratio of price to volume, we might have:

-
-
select(d, [:p, :v] => ByRow((p,v) -> p/v) => "price to volume")
-
- -
3×1 DataFrame
Rowprice to volume
Float64
19.89975
29.99971
39.1596
-
-
-

The above would be equivalent to select(d, [:p, :v] => ((p,v) -> p./v) => "price to volume") (broadcasted p ./ v and parentheses), but not select(d, [:p, :v] => (p,v) -> p./v => "price to volume"), as the precedence rules for => do not parse the expressions identically. That is, the use of parentheses around an anonymous function is needed.

-

The specification of a target column name is not necessary, as one will be generated. In this particular example, it would be :p_v_function. For named functions, the automatic namings are a bit more informative, as the method name replaces the “function” part. The renamecols keyword argument can be set to false to drop the addition of a function name, which in this case would leave :p_v as the name. For a single-column selector it would not rename the column, rather replace it.

-

The above reduced the number of columns to just that specified. The : selector will select all columns (as it does with indexing) and return them:

-
-
select(d, [:p, :v] => ByRow((p,v) -> p/v) => "price to volume", :) # return all columns
-
- -
3×7 DataFrame
Rowprice to volumebvplaploadd
Float64StringInt64Float64?Cat…Cat…Date
19.89975Goruck GR240395.99Yfront panel2022-09-01
29.99971Minaal 3.035349.99Yfront panel2022-09-01
39.1596Genius25228.99Yclamshell2022-10-01
-
-
-

As seen in this last example, the source column selectors can select more than one column. The function receives multiple arguments. It is possible that there be multiple target columns. For example, we could compute both price-to-volume and volume-to-price quantities. A function to do so would be

-
-
pv_and_vp(p,v) = [p/v, v/p]
-
-
pv_and_vp (generic function with 1 method)
-
-
-

When we use this, we see the two values are stored in one column.

-
-
select(d, [:p, :v] => ByRow(pv_and_vp))
-
- -
3×1 DataFrame
Rowp_v_pv_and_vp
Array…
1[9.89975, 0.101013]
2[9.99971, 0.100003]
3[9.1596, 0.109175]
-
-
-

To split these up, we can give two names:

-
-
select(d, [:p, :v] => ByRow(pv_and_vp) => [:pv, :vp])
-
- -
3×2 DataFrame
Rowpvvp
Float64Float64
19.899750.101013
29.999710.100003
39.15960.109175
-
-
-
-

AsTable

-

The AsTable function has two meanings in the mini language. If on the target side, it assumes the output of the function is a vector of containers (in this example the vectors [p/v, v/p]) that should be expanded into multiple columns. It uses the keys (were the containers named tuples) or, in this case generates them, to create multiple columns in the destination:

-
-
select(d, [:p, :v] => ByRow(pv_and_vp) => AsTable)
-
- -
3×2 DataFrame
Rowx1x2
Float64Float64
19.899750.101013
29.999710.100003
39.15960.109175
-
-
-

Had we used a named tuple in our function, pv_and_vp(p, v) = (pv = p/v, vp = v/p), then the column names would come from the tuple’s names and need not be generated.

-

When AsTable is used on the source columns, as in AsTable([:p,:v]) then the function receives a named tuple with column vector entries. (For this, pv_and_vp would be written pv_and_vp(r) = [r.p/r.v, r.v/r.p], with r representing a row in a two-column table with names :p and :v.

-
-
-

Transform

-

Extending the columns in the data frame by select is common enough that the function transform is supplied which always keeps the columns of the original data frame, though they can also be modified through the mini language. The use of transfrom is equivalent to select(df, :, args...).

-
-
-
- -
-
-The DataFrames’ mini language -
-
-
-

The mini language is well documented in the documentation string of transform and the blog post of Bogumil Kasminski “DataFrames.jl minilanguage explained.”

-
-
-
-
-
-

Combine

-

The combine function creates a new data frame whose columns are the result of transformations applied to the source data frame. The name will be more clear after we discuss grouping or splitting of a data set.

-

A typical usage of combine is to apply some reduction to the data, for example finding an average.

-

In this example, we use the mean function from the StatsBase package and apply that to the :MPGCity measurements in the cars data:

-
-
combine(cars, :MPGCity => mean)
-
- -
1×1 DataFrame
RowMPGCity_mean
Float64
122.3656
-
-
-

(A second pair can be used to specify a name for the target, as in :MPGCity => mean => :AvgMPGCity)

-

There are several numeric variables in this data set, we may wish to find the mean of more than 1 at a time. For this, we can broadcast the columns via .=>, as in this example:

-
-
combine(cars, [:MPGCity, :MPGHighway] .=> mean)
-
- -
1×2 DataFrame
RowMPGCity_meanMPGHighway_mean
Float64Float64
122.365629.086
-
-
-

(That is an alternate to combine(cars, :MPGCity => mean, :MPGHighway => mean).)

-

Broadcasting => lends itself to succinctly applying a function to multiple columns.

-

For example, to apply mean to all the numeric values, we might select them first (using the filtering feature of names), then pass to the mini language, as follows:

-
-
nms = names(cars, Real)
-combine(cars, nms .=> mean; renamecols=false)
-
- -
1×16 DataFrame
RowMinPricePriceMaxPriceMPGCityMPGHighwayEngineSizeHorsepowerRPMRevPerMileFuelTankCapacityPassengersLengthWheelbaseWidthTurnCircleWeight
Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64
117.125819.509721.898922.365629.0862.66774143.8285280.652332.216.66455.08602183.204103.94669.376338.9573072.9
-
-
-

More than one transformation at a time is possible, as already seen in the mini language. With combine the number of rows is determined by the output of the transformation. In this example, the mean reduces to a number, but unique reduces :Origin to the number of levels. The result of mean is repeated, it is not the mean for each group (a task we demonstrate shortly):

-
-
combine(cars, :MPGCity => mean, :Origin => unique)
-
- -
2×2 DataFrame
RowMPGCity_meanOrigin_unique
Float64String
122.3656non-USA
222.3656USA
-
-
-
-
-

Flatten

-

As mentioned, the repeat function is used to repeat values in a vector, or other iterable. The simplest usage looks like this:

-
-
repeat([1,2,3], 2) |> show
-
-
[1, 2, 3, 1, 2, 3]
-
-
-

A common use of repetition is to take two variables, say x and y, perhaps of unequal length and create a data frame with two columns: one to store the values and one to indicate if a value came from x or y. This could be achieved like

-
-
x = [1,2,3]
-y = [4, 5]
-
-DataFrame(variable=vcat(repeat([:x], length(x)), repeat([:y], length(y))),
-          values = vcat(x,y))
-
- -
5×2 DataFrame
Rowvariablevalues
SymbolInt64
1x1
2x2
3x3
4y4
5y5
-
-
-

The flatten function offers an alternative. Consider the data frame:

-
-
x = [1,2,3]
-y = [4, 5]
-dxy = DataFrame(variable=[:x,:y], value=[x, y])
-
- -
2×2 DataFrame
Rowvariablevalue
SymbolArray…
1x[1, 2, 3]
2y[4, 5]
-
-
-

The flatten function will iterate over the indicated columns and repeat the other variables:

-
-
flatten(dxy, :value)
-
- -
5×2 DataFrame
Rowvariablevalue
SymbolInt64
1x1
2x2
3x3
4y4
5y5
-
-
-
-
-

SplitApplyCombine

-

The split-apply-combine strategy for wrangling data frames was popularized in the influential paper “The Split-Apply-Combine Strategy for Data Analysis: (Wickham 2011) by H. Wickham which introduces this description:”where you break up a big problem into manageable pieces, operate on each piece independently and then put all the pieces back together.”

-

The groupby function for data frames does the breaking up, the DataFrames mini language can operate on each piece, and the combine function can put things together again.

-

The groupby function splits a data frame into pieces of type GroupedDataFrame. The basic signature is groupby(df, cols; sort=nothing, skipmissing=false). The cols are one or more column selectors. The value of sort defaults to nothing leaving the order of the groups in the result undefined, but the grouping uses the fastest identified algorithm. The skipmissing argument can instruct the skipping of rows which have missing values in the selected columns.

-

For example, grouping by loading style, returns two sub data frames.

-
-
gdf = groupby(d, :load)
-
- -

GroupedDataFrame with 2 groups based on key: load

First Group (2 rows): load = CategoricalValue{String, UInt32} "front panel"
Rowbvplaploadd
StringInt64Float64?Cat…Cat…Date
1Goruck GR240395.99Yfront panel2022-09-01
2Minaal 3.035349.99Yfront panel2022-09-01

Last Group (1 row): load = CategoricalValue{String, UInt32} "clamshell"
Rowbvplaploadd
StringInt64Float64?Cat…Cat…Date
1Genius25228.99Yclamshell2022-10-01
-
-
-

Indices may be used to get each sub data frame. Moreover, the keys method returns keys, like indices, to efficiently look up the different sub data frames in gdf. The levels for the keys are found in the property .load, as with keys(gdf)[1].load. Grouped data frames may be iterated over; the pairs iterator iterates over both keys and values.

-

The select and subset functions work over GroupedDataFrame objects, here we see the returned values are combined:

-
-
subset(gdf, :p => ByRow(<=(350)))
-
- -
2×6 DataFrame
Rowbvplaploadd
StringInt64Float64?Cat…Cat…Date
1Minaal 3.035349.99Yfront panel2022-09-01
2Genius25228.99Yclamshell2022-10-01
-
-
-

(The command [subset(g, :p => ByRow(<=(350))) for g in gdf] avoids the combine step.)

-

We can see that combine also works over GroupedDataFrame objects. This example shows how to find the mean city mileage of groups formed by the value of :Origin in the cars data set:

-
-
combine(groupby(cars, :Origin), :MPGCity => mean)
-
- -
2×2 DataFrame
RowOriginMPGCity_mean
Cat…Float64
1USA20.9583
2non-USA23.8667
-
-
-

More than one column can be used to group the data. This example first groups by the number of cylinders, then groups by origin. For each of the 9 resulting groups, the group mean for city mileage is taken:

-
-
combine(groupby(cars, [:Cylinders, :Origin]), :MPGCity => mean)
-
- -
9×3 DataFrame
RowCylindersOriginMPGCity_mean
Cat…Cat…Float64
13non-USA39.3333
24USA24.4091
34non-USA25.2222
45non-USA18.5
56USA18.35
66non-USA18.5455
78USA17.0
88non-USA17.0
9rotarynon-USA17.0
-
-
-

The @chain macro may make this more readable:

-
-
@chain cars begin
-    groupby([:Cylinders, :Origin])
-    combine(:MPGCity => mean)
-    describe
-end
-
- -
3×7 DataFrame
Rowvariablemeanminmedianmaxnmissingeltype
SymbolUnion…AnyUnion…AnyInt64DataType
1Cylinders3rotary0CategoricalValue{String, UInt8}
2OriginUSAnon-USA0CategoricalValue{String, UInt8}
3MPGCity_mean21.706717.018.539.33330Float64
-
-
-
-

Example 1 (Lego sets) For an example, we download a data set from the internet describing all the sets of Legos sold during some time period. This data set is part of the data sets provided by openintro.org, an organization trying to make interesting educational products that are free and transparent.

-
-
csv_data = download("https://www.openintro.org/data/csv/lego_population.csv")
-legos = CSV.read(csv_data, DataFrame)
-first(legos, 2)
-
- -
2×14 DataFrame
Rowitem_numberset_namethemepiecespriceamazon_priceyearagespagesminifigurespackagingweightunique_piecessize
Int64StringString31String7String31String31Int64String15String7String3String31String31String7String7
141916Extra Dots - Series 2DOTS1093.993.442020Ages_6+NANAFoil packNA6Small
241908Extra Dots - Series 1DOTS1093.993.992020Ages_6+NANAFoil packNA6Small
-
-
-

Let’s explore this data.

-

The size of the data set is printed, though suppressed above as only the first 2 rows are requested to be shown, or can be programmatically identified with size(legos).

-
-
size(legos)
-
-
(1304, 14)
-
-
-

Each row is a product; there are a lot of different ones.

-

The types identified by CSV.read are not perfect. The data uses NA for not-available. We read again using the argument missingstring="NA":

-
-
legos = CSV.read(csv_data, DataFrame; missingstring="NA")
-first(legos, 2)  # show first 2 rows
-
- -
2×14 DataFrame
Rowitem_numberset_namethemepiecespriceamazon_priceyearagespagesminifigurespackagingweightunique_piecessize
Int64StringString31?Int64?Float64?Float64?Int64String15Int64?Int64?String31?String31?Int64?String7?
141916Extra Dots - Series 2DOTS1093.993.442020Ages_6+missingmissingFoil packmissing6Small
241908Extra Dots - Series 1DOTS1093.993.992020Ages_6+missingmissingFoil packmissing6Small
-
-
-

The :theme and :packaging variables are categorical, so we make them so:

-
-
legos.theme = categorical(legos.theme)
-legos.packaging = categorical(legos.packaging);
-first(legos, 2)
-
- -
2×14 DataFrame
Rowitem_numberset_namethemepiecespriceamazon_priceyearagespagesminifigurespackagingweightunique_piecessize
Int64StringCat…?Int64?Float64?Float64?Int64String15Int64?Int64?Cat…?String31?Int64?String7?
141916Extra Dots - Series 2DOTS1093.993.442020Ages_6+missingmissingFoil packmissing6Small
241908Extra Dots - Series 1DOTS1093.993.992020Ages_6+missingmissingFoil packmissing6Small
-
-
-

We see data on the number of pieces and the age range. Is there some relationship?

-

The :age variable should be an ordered factor, ordered by the youngest age intended for use. The :ages variable has a pattern Ages_startXXX where XXX may be + to indicate or up, or a dash to indicate a range. We use this to identify the youngest intended age.

-
-
# alternative to
-# `(m = match(r"Ages_(\d+)", x); m === nothing ? missing : parse(Int, m.captures[1]))`
-function pick_first_age(x)
-    ismissing(x) && return missing
-    nos = collect(string(0:9...))
-    out = ""
-    for xi in x[6:end] # drop Ages_
-        !(xi in nos) && break
-        out = out * xi
-    end
-    out == "" && return missing
-    parse(Int, out)
-end
-
-
-transform!(legos, :ages => ByRow(pick_first_age) => :youngest_age)
-legos.youngest_age = categorical(legos.youngest_age, ordered=true)
-first(legos[:,r"age"], 2)
-
- -
2×3 DataFrame
Rowagespagesyoungest_age
String15Int64?Cat…?
1Ages_6+missing6
2Ages_6+missing6
-
-
-

With that ordering, an expected pattern becomes clear – kits for older users have on average more pieces – though there are unexpected exceptions:

-
-
@chain legos begin
-    groupby(:youngest_age)
-    combine([:pieces, :unique_pieces] .=> meanskipmissing
-            .=> [:avg_pieces, :avg_unique_pieces])
-end
-
- -
16×3 DataFrame
Rowyoungest_ageavg_piecesavg_unique_pieces
Cat…?Float64Float64
1missing127.59240.5604
2137.208326.2174
3255.61731.2979
43140.041.0
54194.7487.2857
65147.71285.1557
76191.07987.1289
87265.556108.816
98491.193173.801
109859.182252.773
1110417.52109.768
12112357.86214.143
13121307.29256.4
14141617.25349.5
15162454.79409.107
16182073.84214.294
-
-
-

(We composed the two functions skipmissing with mean using the \circ[tab] operator and use .=> in both places to avoid defining the transformation function twice.)

-
-
-
-
- -
-
-The Lego Group -
-
-
-

Lego, was the largest toy company in the world by 2021. By 2015 it had produced over 600 billion parts.

-
-
-

Lego is a mature company with an origin dating to 1934. The number of products per year should be fairly stable, though it is easy enough to check. The data set has a :year variable, so we would only need to group the data by that, then count the number of cases per each group. The nrow function will count the cases. This function is special cased in the DataFrames’ mini language and can appear by itself:

-
-
combine(groupby(legos, :year), nrow) # use `nrow => :n`, or some such, to label differently
-
- -
3×2 DataFrame
Rowyearnrow
Int64Int64
12018442
22019423
32020439
-
-
-

We can take other summaries by year, for example, here we find the average price by year and size:

-
-
@chain legos begin
-    groupby([:year, :size])
-    combine(nrow => :n,
-            :price => meanskipmissing => :avg_price
-            )
-end
-
- -
9×4 DataFrame
Rowyearsizenavg_price
Int64String7?Int64Float64
12018Small32445.7792
22018Large2128.59
32018missing9725.8135
42019Small32250.4663
52019Large1429.6329
62019missing8713.8536
72020Small33549.8104
82020Large1833.5456
92020missing8621.6959
-
-
-

The youngest age range is a bit long. We wish to collapse it to the ranges 1-6, 7-12, and 12-18. The data has been stored as a categorical array. The cut function can group numeric data easily, but to group categorical data, the cut-ranges must be promoted to a CategoricalValue. We have the following, where Ref is used to stop the broadcasting for that value:

-
-
vals = CategoricalValue.([1, 6, 12, 18], Ref(legos.youngest_age))
-transform!(legos,
-           :youngest_age => (x ->cut(x, vals; extend=true))
-           => :youngest_age_range);
-
-

We now check if the number of pieces has dramatically changed over the year. We break up the data by the age range, as we expect variation in that value so it is best to dis-aggregate over that factor:

-
-
@chain legos begin
-    groupby([:youngest_age_range, :year])
-    combine(nrow => :n,
-            :pieces => meanskipmissing => :avg_pieces)
-end
-
- -
12×4 DataFrame
Rowyoungest_age_rangeyearnavg_pieces
Cat…?Int64Int64Float64
1missing201833165.379
2missing20193495.7778
3missing202044111.8
4[1, 6)201898131.561
5[1, 6)201992122.533
6[1, 6)202092153.413
7[6, 12)2018292341.866
8[6, 12)2019275389.945
9[6, 12)2020276365.887
10[12, 18]2018191956.21
11[12, 18]2019221938.95
12[12, 18]2020272111.3
-
-
-

No real pattern shows up.

-

There are many different themes. The command unique(legos.theme) shows 41. Which are the most popular? To see, the data is grouped by the theme, each group is counted, missing themes are dropped, then the data frame is sorted and the top 5 rows are shown:

-
-
@chain legos begin
-    groupby(:theme)
-    combine(nrow => :n)
-    dropmissing
-    sort(:n; rev=true)
-    first(5)
-end
-
- -
5×2 DataFrame
Rowthemen
Cat…Int64
1Star Wars™119
2Friends103
3City101
4NINJAGO®78
5DUPLO®53
-
-
-
-
-

The SplitApplyCombine package

-

There is support for the split-apply-combine paradigm outside of the DataFrames package in the package SplitApplyCombine. This package is much lighter weight than DataFrames. This support targets other representations of tabular data, such as a vector of nested tuples:

-
-
using SplitApplyCombine
-tbl = [
-(b = "Goruck GR2", v = 40, p = 395, lap = "Y", load = "front panel", d = Date("2022-09-01")),
-(b = "Minaal 3.0", v = 35, p = 349, lap = "Y", load = "front panel", d = Date("2022-09-01")),
-(b = "Genius    ",     v = 25, p = 228, lap = "Y", load = "clamshell",   d = Date("2022-10-01"))
-]
-
-
3-element Vector{NamedTuple{(:b, :v, :p, :lap, :load, :d), Tuple{String, Int64, Int64, String, String, Date}}}:
- (b = "Goruck GR2", v = 40, p = 395, lap = "Y", load = "front panel", d = Date("2022-09-01"))
- (b = "Minaal 3.0", v = 35, p = 349, lap = "Y", load = "front panel", d = Date("2022-09-01"))
- (b = "Genius    ", v = 25, p = 228, lap = "Y", load = "clamshell", d = Date("2022-10-01"))
-
-
-

Many of the main verbs are generic functions: filter, map, reduce; group does grouping. There are some others. A few examples follow.

-

For indexing this structure, we can’t index by [row, col], rather we can use [row][col] notation, the first to extract the row, the second to access the entries in the column. For example, he we index a selected row by position, by name, and then with multiple names:

-
-
tbl[2][2], tbl[2].v, tbl[2][ [:v, :p] ]
-
-
(35, 35, (v = 35, p = 349))
-
-
-

The invert function reverses the access pattern, allowing in this case, [col][row] access, with:

-
-
itbl = invert(tbl)
-itbl[3][1], itbl[:p][2]
-
-
(395, 349)
-
-
-

In some ways, the invert function flips the viewpoint from an array of structs to a struct of arrays.

-

To filter out rows, we have the same calling style as with data frames: filter(pred, tbl). With the outer container of tbl being an array, no special method is utilized, iteration is over each row. For example,

-
-
filter(r -> r.v >= 35, tbl) |> DataFrame
-
- -
2×6 DataFrame
Rowbvplaploadd
StringInt64Int64StringStringDate
1Goruck GR240395Yfront panel2022-09-01
2Minaal 3.035349Yfront panel2022-09-01
-
-
-

(The call to DataFrame is to take advantage of the prettier printing. DataFrame can consume a vector of named tuples for its input.)

-

The DataPipes package composes a bit more nicely with this approach, but we continue using Chain for examples3, like this one of nested calls to filter:

-
-
@chain tbl begin
-    filter(r -> r.v >= 35,  _)
-    filter(r -> r.p >= 350, _)
-    DataFrame
-end
-
- -
1×6 DataFrame
Rowbvplaploadd
StringInt64Int64StringStringDate
1Goruck GR240395Yfront panel2022-09-01
-
-
-

Selecting columns and adding a transformation can be achieved through map, which iterates over each named tuple in this example:

-
-
map(r -> (b=r.b, v=r.v, p=r.p, pv = round(r.p/r.v; digits=2)), tbl) |>
-    DataFrame
-
- -
3×4 DataFrame
Rowbvppv
StringInt64Int64Float64
1Goruck GR2403959.88
2Minaal 3.0353499.97
3Genius 252289.12
-
-
-

The group function can group by values of a function call. This example from the package’s Readme is instructive:

-
-
nms = ["Andrew Smith", "John Smith", "Alice Baker",
-       "Robert Baker", "Jane Smith", "Jason Bourne"]
-group(last, split.(nms))
-
-
3-element Dictionaries.Dictionary{SubString{String}, Vector{Vector{SubString{String}}}}
-  "Smith" │ Vector{SubString{String}}[["Andrew", "Smith"], ["John", "Smith"], […
-  "Baker" │ Vector{SubString{String}}[["Alice", "Baker"], ["Robert", "Baker"]]
- "Bourne" │ Vector{SubString{String}}[["Jason", "Bourne"]]
-
-
-

The names are split on spaces, and the keys of the group are determined by the last function, which here gets the last piece after splitting (aka the “last” name, but by coincidence, not intelligence).

-

For numeric operations, this is quite convenient. Here we group by the remainder upon division by 3:

-
-
group(x -> rem(x,3), 1:10)
-
-
3-element Dictionaries.Dictionary{Int64, Vector{Int64}}
- 1 │ [1, 4, 7, 10]
- 2 │ [2, 5, 8]
- 0 │ [3, 6, 9]
-
-
-

The grouping basically creates a dictionary and adds to the key determined by the function.

-

To apply a function to the values of a base Julia dictionary is not directly supported by map, however the dictionary used by group (from the Dictionaries packages) does allow this, so we could, for example, add up the values in each group with:

-
-
map(sum, group(x -> rem(x,3), 1:10))
-
-
3-element Dictionaries.Dictionary{Int64, Int64}
- 1 │ 22
- 2 │ 15
- 0 │ 18
-
-
-

The group function can do this in one call, by placing the function to apply in between the by function and the table. Here we apply first to pick off the first name. That is, ["Andrew", "Smith"] becomes just "Andrew", the first element of that vector.

-
-
gps = group(last, first, split.(nms))
-
-
3-element Dictionaries.Dictionary{SubString{String}, Vector{SubString{String}}}
-  "Smith" │ SubString{String}["Andrew", "John", "Jane"]
-  "Baker" │ SubString{String}["Alice", "Robert"]
- "Bourne" │ SubString{String}["Jason"]
-
-
-
-

Example 2 (A tally function) The Tally package provides a quick way to tally up numbers. Here we do a simplified version.

-

To tally, we have three steps: group by each by value, count the number in each groups, sort the values. This is implemented with this composition of functions:

-
-
tally(v; by=identity) = SplitApplyCombine.sortkeys(map(length, group(by, v)))
-
-
tally (generic function with 1 method)
-
-
-

To see, we have:

-
-
v = rand(1:5, 50)
-d = tally(v)
-
-
5-element Dictionaries.Dictionary{Int64, Int64}
- 1 │ 12
- 2 │ 13
- 3 │ 11
- 4 │ 9
- 5 │ 5
-
-
-

Using map, we can provide an alternate, oft used view for tallying:

-
-
tallies = "\u007C"^4
-ftally = "┼┼┼┼ "
-function prison_count(x)
-    d, r = divrem(x, 5)
-    ftally^d * tallies[1:r]
-end
-
-
-map(prison_count, d)
-
-
5-element Dictionaries.Dictionary{Int64, Any}
- 1 │ "┼┼┼┼ ┼┼┼┼ ||"
- 2 │ "┼┼┼┼ ┼┼┼┼ |||"
- 3 │ "┼┼┼┼ ┼┼┼┼ |"
- 4 │ "┼┼┼┼ ||||"
- 5 │ "┼┼┼┼ "
-
-
-
-
-

Example 3 (A simple stem and leaf diagram) The grouping and sorting made quite accessible by SplitApplyCombine could also be used to produce a simple stem-and-leaf type diagram. Here we don’t bother displaying stems with no associated leaves, which does change the interpretation of spread in the data, and could easily be added in, though it takes us a bit afield, as it requires some manipulation of the display:

-
-
x = rand(0:100, 32)  # 32 test scores
-stem_unit = 10       # stem * 10 is value of stem
-@chain x begin
-    round.(Int, 10*_/stem_unit)
-    SplitApplyCombine.group(x -> x ÷ 10, x->rem(x,10), _)
-    SplitApplyCombine.sortkeys(_)
-    map(sort, _)
-    map(x -> parse(Int,join(x)), _)
-end
-
-
10-element Dictionaries.Dictionary{Int64, Int64}
-  0 │ 69
-  2 │ 359
-  3 │ 22377
-  4 │ 1157
-  5 │ 36
-  6 │ 679
-  7 │ 44
-  8 │ 145
-  9 │ 368
- 10 │ 0
-
-
-
-
-

Let’s return to the cars data and use group to identify the average mileage per type. For this task, we group by :Typeand then apply a function to extract the mileage from a row. The copy.(eachrow(cars)) command creates a vector of named tuples.

-
-
cs = copy.(eachrow(cars))  # Can also call `NamedTuple` for `copy`
-gps = group(r -> r.Type, r -> r.MPGCity, cs)
-
-
6-element Dictionaries.Dictionary{CategoricalValue{String, UInt8}, Vector{Int32}}
- CategoricalValue{String, UInt8} "Smal… │ Int32[25, 29, 23, 29, 31, 23, 46, 42,…
- CategoricalValue{String, UInt8} "Mids… │ Int32[18, 19, 22, 22, 19, 16, 21, 21,…
- CategoricalValue{String, UInt8} "Comp… │ Int32[20, 25, 25, 23, 22, 22, 24, 26,…
- CategoricalValue{String, UInt8} "Larg… │ Int32[19, 16, 16, 17, 20, 20, 20, 18,…
- CategoricalValue{String, UInt8} "Spor… │ Int32[19, 17, 18, 22, 24, 30, 24, 26,…
-  CategoricalValue{String, UInt8} "Van" │ Int32[18, 15, 17, 15, 18, 17, 18, 18,…
-
-
-

We now can map mean over each group:

-
-
ms = map(mean, gps)
-
-
6-element Dictionaries.Dictionary{CategoricalValue{String, UInt8}, Float64}
-   CategoricalValue{String, UInt8} "Small" │ 29.857142857142858
- CategoricalValue{String, UInt8} "Midsize" │ 19.545454545454547
- CategoricalValue{String, UInt8} "Compact" │ 22.6875
-   CategoricalValue{String, UInt8} "Large" │ 18.363636363636363
-  CategoricalValue{String, UInt8} "Sporty" │ 21.785714285714285
-     CategoricalValue{String, UInt8} "Van" │ 17.0
-
-
-

This requires two passes through the grouped data, the first to get just the mileage values, the next to call mean. The following DataFrames syntax hides this:

-
-
combine(groupby(cars, :Type), :MPGHighway => mean)
-
- -
6×2 DataFrame
RowTypeMPGHighway_mean
Cat…Float64
1Compact29.875
2Large26.7273
3Midsize26.7273
4Small35.4762
5Sporty28.7857
6Van21.8889
-
-
-

Reductions, like sum and count which can be written easily using the higher-order function reduce, have support to combine the grouping and reduction. The groupreduce function takes the additional arguments of reduce (an operation and an optional init value). For example, to get the group sum, we could call:

-
-
groupreduce(r -> r.Type, r -> r.MPGCity, +, cs)
-
-
6-element Dictionaries.Dictionary{CategoricalValue{String, UInt8}, Int32}
-   CategoricalValue{String, UInt8} "Small" │ 627
- CategoricalValue{String, UInt8} "Midsize" │ 430
- CategoricalValue{String, UInt8} "Compact" │ 363
-   CategoricalValue{String, UInt8} "Large" │ 202
-  CategoricalValue{String, UInt8} "Sporty" │ 305
-     CategoricalValue{String, UInt8} "Van" │ 153
-
-
-

The group sum and group count have shortcuts, so the mean could have been computed as:

-
-
groupsum(r -> r.Type, r -> r.MPGCity, cs) ./ groupcount(r -> r.Type, cs)
-
-
6-element Dictionaries.Dictionary{CategoricalValue{String, UInt8}, Float64}
-   CategoricalValue{String, UInt8} "Small" │ 29.857142857142858
- CategoricalValue{String, UInt8} "Midsize" │ 19.545454545454547
- CategoricalValue{String, UInt8} "Compact" │ 22.6875
-   CategoricalValue{String, UInt8} "Large" │ 18.363636363636363
-  CategoricalValue{String, UInt8} "Sporty" │ 21.785714285714285
-     CategoricalValue{String, UInt8} "Van" │ 17.0
-
-
-

(Unlike for a Dict instance, the dictionaries above allow such division.)

-
-

The “combine” part of the paradigm takes the pieces and reassembles. The data frames example returns a data frame, whereas with SplitApplyCombine.jl we have a dictionary. A dictionary does not map directly to a data frame. While both dictionaries and named tuples are associative arrays, we wouldn’t necessarily want to map our data to a row. Rather, here we use the pairs iterator to iterate over the keys and values to produce an array of named tuples:

-
-
[ (; Type, MPG) for (Type,MPG) in pairs(ms)] |> DataFrame
-
- -
6×2 DataFrame
RowTypeMPG
Cat…Float64
1Small29.8571
2Midsize19.5455
3Compact22.6875
4Large18.3636
5Sporty21.7857
6Van17.0
-
-
-
-
-
-

Aggregating data

-

Somewhat inverse to flattening data is to aggregate data, by which we mean combining multiple columns into a single one, or combining multiple rows into a single one. The select and combine methods can do these tasks.

-

Consider the three price variables in the cars data. We can find an average of the three price numbers easily enough. This function allows the specification as different arguments, not in a container:

-
-
_mean(xs...) = mean(xs)
-
-
_mean (generic function with 1 method)
-
-
-

We can then use select to aggregate the three price variables. In the following, these are selected with a regular expression:

-
-
cars_price = select(cars, 1:3, r"Price" => ByRow(_mean) => :price)
-first(cars_price, 3)
-
- -
3×4 DataFrame
RowManufacturerModelTypeprice
Cat…StringCat…Float64
1AcuraIntegraSmall15.8667
2AcuraLegendMidsize33.9333
3Audi90Compact29.1
-
-
-

To aggregate over columns, the groupby construct can be used to specify the groupings to aggregate over. In the following this is the car type:

-
-
combine(groupby(cars_price, :Type), :price => mean => :avg_price)
-
- -
6×2 DataFrame
RowTypeavg_price
Cat…Float64
1Compact18.2104
2Large24.303
3Midsize27.2152
4Small10.1667
5Sporty19.4024
6Van19.1111
-
-
-
-
-

Tidy data; stack, unstack

-

The split-apply-combine paradigm suggest that storing data so that it can be split readily is preferable to other ways of storage. The notion of “tidy data,” (Wickham 2014) as presented in R’s tidyr package and described in this vignette suggests data sets should follow:

-
    -
  • Every column is a variable.
  • -
  • Every row is an observation.
  • -
  • Every cell is a single value.
  • -
-

These terms are defined by

-
-

A dataset is a collection of values, usually either numbers (if quantitative) or strings (if qualitative). Values are organised in two ways. Every value belongs to a variable and an observation. A variable contains all values that measure the same underlying attribute (like height, temperature, duration) across units. An observation contains all values measured on the same unit (like a person, or a day, or a race) across attributes.

-
-

In addition to flatten, the DataFrames package provides the functions stack and unstack for tidying data. We illustrate with an abbreviated version of an example in the tidyr vignette.

-

Consider this data from the Global Historical Climatology Network for one weather station (MX17004) in Mexico for five months in 2010. The full data has 31 days per month, this abbreviated data works with just the first 8:

-
-
weather = """
-"id","year","month","element","d1","d2","d3","d4","d5","d6","d7","d8"
-"MX17004","2010","1","tmax","---","---","---","---","---","---","---","---"
-"MX17004","2010","1","tmin","---","---","---","---","---","---","---","---"
-"MX17004","2010","2","tmax","---","27.3","24.1","---","---","---","---","---"
-"MX17004","2010","2","tmin","---","14.4","14.4","---","---","---","---","---"
-"MX17004","2010","3","tmax","---","---","---","---","32.1","---","---","---"
-"MX17004","2010","3","tmin","---","---","---","---","14.2","---","---","---"
-"MX17004","2010","4","tmax","---","---","---","---","---","---","---","---"
-"MX17004","2010","4","tmin","---","---","---","---","---","---","---","---"
-"MX17004","2010","5","tmax","---","---","---","---","---","---","---","---"
-"MX17004","2010","5","tmin","---","---","---","---","---","---","---","---"
-"""
-
-w = CSV.read(IOBuffer(weather), DataFrame; missingstring="---")
-first(w, 4)
-
- -
4×12 DataFrame
Rowidyearmonthelementd1d2d3d4d5d6d7d8
String7Int64Int64String7MissingFloat64?Float64?MissingFloat64?MissingMissingMissing
1MX1700420101tmaxmissingmissingmissingmissingmissingmissingmissingmissing
2MX1700420101tminmissingmissingmissingmissingmissingmissingmissingmissing
3MX1700420102tmaxmissing27.324.1missingmissingmissingmissingmissing
4MX1700420102tminmissing14.414.4missingmissingmissingmissingmissing
-
-
-

This example is said to have variables stored in both rows and columns. The first step to tidying the data is to stack days 1 through 8 into a column. We use the Between column selector to select the columns for d1 though d8 (an alternative to, say, r"^d"):

-
-
w1 = @chain w begin
-    stack(Between(:d1, :d8); variable_name = :day)
-    filter(:value => !ismissing, _)
-end
-
- -
6×6 DataFrame
Rowidyearmonthelementdayvalue
String7Int64Int64String7StringFloat64?
1MX1700420102tmaxd227.3
2MX1700420102tmind214.4
3MX1700420102tmaxd324.1
4MX1700420102tmind314.4
5MX1700420103tmaxd532.1
6MX1700420103tmind514.2
-
-
-

As in the example being followed, missing values are skipped, requiring the reader to imply the lack of measurement on a given day.

-

The day is coded with a leading “d” and as a string, not an integer. We can strip this prefix out using a regular expression or, as here, string indexing then parse the result to an integer:

-
-
transform!(w1, :day => ByRow(x -> parse(Int, x[2:end])) => :day)
-
- -
6×6 DataFrame
Rowidyearmonthelementdayvalue
String7Int64Int64String7Int64Float64?
1MX1700420102tmax227.3
2MX1700420102tmin214.4
3MX1700420102tmax324.1
4MX1700420102tmin314.4
5MX1700420103tmax532.1
6MX1700420103tmin514.2
-
-
-

Finally, the element column does not hold a variable, rather it stores the names of two variables (tmin and tmax). For this the data is “unstacked” splitting the longer variable value in two:

-
-
w2 = unstack(w1, :element, :value)
-
- -
3×6 DataFrame
Rowidyearmonthdaytmaxtmin
String7Int64Int64Int64Float64?Float64?
1MX1700420102227.314.4
2MX1700420102324.114.4
3MX1700420103532.114.2
-
-
-

As described in the vignette, now each variable is in one column, and each row describes one day.

-
-
-

Joins

-

Consider the three following assessments of some class. The students have somewhat spotty attendance:

-
-
t1 = """
-first,last,points
-Alice, Smith, 100
-Bob, Jones, 200
-"""
-
-t2 = """
-first,last,score
-Carol, Gates, 125
-Chad, Gad, 200
-Bob, Jones, 225
-"""
-
-t3 = """
-first,last,points
-Bob, Jones, 300
-Carol, Gates, 150
-Erin, Dan, 100
-"""
-a1, a2, a3 = [CSV.read(IOBuffer(txt), DataFrame) for txt  (t1, t2, t3)];
-
-

We have several ways to combine these three data frames.

-

We can concatenate them using vcat. Before doing so, we need to align the column names, as the second data set used a different name:

-
-
select!(a2, 1:2, :score => :points);
-
-

The vcat method for data frames takes a named argument source allowing a variable to be appended indicating the source of the data frame. The following will indicate with a 1, 2, or 3 what data frame contributed what row:

-
-
aₛ = vcat(a1, a2, a3; source=:assignment)
-describe(aₛ)
-
- -
4×7 DataFrame
Rowvariablemeanminmedianmaxnmissingeltype
SymbolUnion…AnyUnion…AnyInt64DataType
1firstAliceErin0String7
2last Dan Smith0String7
3points175.0100175.03000Int64
4assignment2.12512.030Int64
-
-
-

Similarly, append! could be used in combination with reduce (as in reduce(append!, (a1, a2, a3))).

-

A more common alternative to the above is to join the data frames using one of several join commands. Joins allow multiple tables to be merged, or mashed. into one. We briefly illustrate the implementations in DataFrames of outer, left, right, inner, semi, and anti joins, leaving the cross join and further details to the documentation.

-

We return to the original data, with the differently named columns:

-
-
a1, a2, a3 = [CSV.read(IOBuffer(txt), DataFrame) for txt  (t1, t2, t3)];
-
-

The first join we introduce is outerjoin. It combines two data frames with rows for keys found in any of the data frames passed in.

-
-
outerjoin(a1, a2, on=["first", "last"])
-
- -
4×4 DataFrame
Rowfirstlastpointsscore
String7String7Int64?Int64?
1Bob Jones200225
2Alice Smith100missing
3Carol Gatesmissing125
4Chad Gadmissing200
-
-
-

The on argument is a column name (or names as above) to join the data frames on. They need not have the same name; the values passed to on may be pairs indicating the mapping. (That is, if we were joining on the last variable, on=:points => :score would be possible.)

-

The variable to join on is often called the “key,” though in Julia that terminology is also used with associative arrays.

-

The joined data frame includes all columns from either data frame not specified in on. The resulting data frame, in this case, is in a wide, not tidy, format.

-

In the above, the columns do not have names in conflict. However, were a1 and a3 joined, they would have been. Passing makeunique=true instructs distinguishing names to be produced:

-
-
outerjoin(a1, a3, on=["first", "last"], makeunique=true)
-
- -
4×4 DataFrame
Rowfirstlastpointspoints_1
String7String7Int64?Int64?
1Bob Jones200300
2Alice Smith100missing
3Carol Gatesmissing150
4Erin Danmissing100
-
-
-

More than two data frames may be passed to outerjoin, as illustrated below where all three are. The result includes a row for each person (key) in any of the data sets.

-
-
outerjoin(a1, a2, a3, on=["first", "last"], makeunique=true)
-
- -
5×5 DataFrame
Rowfirstlastpointsscorepoints_1
String7String7Int64?Int64?Int64?
1Bob Jones200225300
2Carol Gatesmissing125150
3Alice Smith100missingmissing
4Chad Gadmissing200missing
5Erin Danmissingmissing100
-
-
-

From the outerjoin, we easily note that only the first listed student was present for all the assessments.

-

Three other joins related to outerjoin are:

-
    -
  • leftjoin(df1, df2; on, ...) which includes all rows in the left data frame
  • -
  • rightjoin(df1, df2; on, ...) which includes all rows in the right data frame (similar to leftjoin(df2, df1).
  • -
  • innerjoin(df1, df2; on, ...) which includes all rows with keys in both data data frames.
  • -
-

To illustrate:

-
-
leftjoin(a1, a2, on=["first", "last"])
-
- -
2×4 DataFrame
Rowfirstlastpointsscore
String7String7Int64Int64?
1Bob Jones200225
2Alice Smith100missing
-
-
-
-
rightjoin(a1, a2, on=["first", "last"])
-
- -
3×4 DataFrame
Rowfirstlastpointsscore
String7String7Int64?Int64
1Bob Jones200225
2Carol Gatesmissing125
3Chad Gadmissing200
-
-
-

In the following, we must qualify the function below, as SplitApplyCombine has a conflicting use, though this isn’t typically needed:

-
-
DataFrames.innerjoin(a1, a2, on=["first", "last"])
-
- -
1×4 DataFrame
Rowfirstlastpointsscore
String7String7Int64Int64
1Bob Jones200225
-
-
-

Joins have an analog in set operations: the outer join is like a union, the inner join is like an intersection.

-

The next two joins do not join columns, as the ones above. Rather, they use the second data frame to filter out rows of the first data frame.

-

The semijoin returns the rows of the left data frame that match a key of the right data frame:

-
-
semijoin(a1, a2[3:3, :]; on=["first", "last"])
-
- -
1×3 DataFrame
Rowfirstlastpoints
String7String7Int64
1Bob Jones200
-
-
-

An antijoin includes rows in the left data frame that do not match a key in the right data frame. (Like the set difference). Here we see the row in common to a1 and a2 is not included:

-
-
antijoin(a1, a2, on=["first", "last"])
-
- -
1×3 DataFrame
Rowfirstlastpoints
String7String7Int64
1Alice Smith100
-
-
-
-
-Bouchet-Valat, M., and Kamiński, B. (2023), “DataFrames.jl: Flexible and fast tabular data in julia,” Journal of Statistical Software, 107, 1–32. https://doi.org/10.18637/jss.v107.i04. -
-
-Wickham, H. (2011), “The split-apply-combine strategy for data analysis,” Journal of Statistical Software, 40, 1–29. https://doi.org/10.18637/jss.v040.i01. -
-
-Wickham, H. (2014), Tidy data,” The Journal of Statistical Software, 59. -
-
-
-
-
-
-
-
    -
  1. There are other storage formats available within Julia that can contain more detail on the data, e.g. Arrow.↩︎

  2. -
  3. This combination of column selectors is also performed by Cols(1, :v, "p", r"^l").↩︎

  4. -
  5. The DataPipes package inserts a magical _ in the last positional argument, which is common with higher-order functions like filter. The example could become @p tbl |> filter(_.v >= 25) |> filter(_.p >= 350) with the new lines replacing the pipe operation as a convenience with longer chains↩︎

  6. -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/EDA/univariate-julia.html b/EDA/univariate-julia.html deleted file mode 100644 index 0c15ce0..0000000 --- a/EDA/univariate-julia.html +++ /dev/null @@ -1,2062 +0,0 @@ - - - - - - - - - -univariate-julia - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

Univariate data

-

Statistics involves the study of data. A data set can be a single value, or scalar, but more commonly is a collection of values. This chapter covers the basic containers used in Julia to store and manipulate data sets for a single variable in statistics. In addition it shows some well-known summaries of a single variable.

-
-

Data vectors

-

Julia offers several containers for storing ordered data, such as in a data set \(x_1, x_2, \dots, x_n\).

-

We consider this data on the number of whale beachings in a certain area by year during a decade:

-
74 122 235 111 292 111 211 133 156 79
-

As the data in a data set is typically of the same type (integer here, or character, number, …) and may be quite large a vector is a natural choice for storage in Julia.

-

Vectors are created using square brackets with entries separated by commas. Storing the data in a vector and assigning to a variable, whale, is then done with:

-
-
whale = [74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-
-
10-element Vector{Int64}:
-  74
- 122
- 235
- 111
- 292
- 111
- 211
- 133
- 156
-  79
-
-
-

Without using commas in the construction, a matrix is created:

-
-
whale_matrix = [74 122 235 111 292 111 211 133 156 79]
-
-
1×10 Matrix{Int64}:
- 74  122  235  111  292  111  211  133  156  79
-
-
-
-
-
- -
-
-Space-saving measure -
-
-
-

For purposes of printing only, we use show or the trailing |> show (which uses the chaining operation to call the show function) in the following to format how a vector is printed. This is only a vertical-space-saving measure and wouldn’t normally be done when interacting with Julia.1

-
-
show(whale)
-
-
[74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-
-
-
-
-

Internally a vector is a \(1\)-dimensional array, a matrix a \(2\)-dimensional array. Julia has a general \(N\)-dimensional array type that these are specializations of. For a matrix, one uses space to separate row elements and semicolons to separate rows. Vectors are useful for storing data; matrices can be used to store data, as will be seen through example, but are widely used to represent mathematical values, as they have an associated algebra that can compactly represent many operations (we will see a data frame is more idiomatic for storing rectangular data); arrays are not used in our discussion.

-

As seen in the output above, Julia prints the size and element type of a vector. In this case, a vector of integers (Int64). The constructor ([]) will promote values to a common type, which may be Any, a catch all type.

-

Vectors have a length, found by length, which in this case is the number of data points, often labeled \(n\). More generally, arrays have a size:

-
-
length(whale), size(whale)
-
-
(10, (10,))
-
-
-

The size is returned as a tuple, in this case with just 1 element, as vectors are \(1\) dimensional.

-

The length function is a reduction, returning a summary of a vector. Similarly, sum will add the elements, returning a number. Here we see how to compute the mean:

-
-
sum(whale) / length(whale)
-
-
152.4
-
-
-

The mean function is not part of base Julia. It can be found in the Statistics package, which comes bundled with Julia, but we prefer to use the add-on StatsBase package:

-
-
using StatsBase
-mean(whale)
-
-
152.4
-
-
-
-

Missing values

-

Julia uses missing to represent missing values. For example, data on the cost of a hip replacement at various hospitals was found from their websites and is given by:

-
10500, 45000, 74100, unavailable, 83500,
-86000, 38200, unavailable, 44300, 12500,
-55700, 43900, 71900, unavailable, 62000
-

When the cost could not be identified, it was labeled “unavailable.” We replace the colloquial “unavailable” with the special value missing and enter the values into a vector:

-
-
hip_cost = [10500, 45000, 74100, missing, 83500, 86000, 38200, missing,
-            44300, 12500, 55700, 43900, 71900, missing, 62000]
-hip_cost |> permutedims
-
-
1×15 reshape(::Vector{Union{Missing, Int64}}, 1, 15) with eltype Union{Missing, Int64}:
- 10500  45000  74100  missing  83500  …  55700  43900  71900  missing  62000
-
-
-

We highlight that the type of element in hip_cost is Union{Missing, Int64}, a type that allows an element to be either an integer or a missing element.

-

The missing value propagates through computations:

-
-
sum(hip_cost)
-
-
missing
-
-
-

In particular, all of these combinations with missing yield missing, as they should – if data is not available, combinations based on that data are still not available:

-
-
1 + missing, 1 - missing, 1*missing, 1/missing, missing^2, missing == true
-
-
(missing, missing, missing, missing, missing, missing)
-
-
-
-
-
- -
-
-Also nothing and something -
-
-
-

In Julia there is also nothing. The semantics are a bit different, and missing is the designed choice for data. (The nothing value is useful for general programming purposes.) For floating point values, there is also NaN, or not a number. This too is often used as a sentinel value, to indicate missingness, though missing is idiomatic. The something function is used to skip over its arguments until a non-nothing value is found. The coalesce function does a similar thing with missing values.

-
-
-

To work with missing data, it can be removed or skipped over. The skipmissing function provides a wrapper around an object which when iterated over, will skip the missing values. For example:

-
-
sum(skipmissing(hip_cost))
-
-
627600
-
-
-

The sum function iterates over the values in the container it is passed to reduce them to a value, mean is similar and works with skipmissing in an identical manner:

-
-
mean(skipmissing(hip_cost))
-
-
52300.0
-
-
-

(Which is helpful, as length, used above to compute a mean, does not compose with skipmissing.)

-
-
-

Names (named tuples)

-

It may be natural to assign the year of measurement to the values in whale, but vectors, as defined in base Julia, do not allow names as an attribute. (There are external packages that allow this, e.g. NamedArrays, which arise in our discussion of contingency tables.) If names are important, Julia provides a named tuple type, which builds on the basic tuple type.

-

While vectors are collections of homogeneous values, tuples are collections of heterogeneous values. Tuples are constructed with commas, and typically parentheses to delimit the commas:2

-
-
whale = (74, 122, 235, 111, 292, 111, 211, 133, 156, 79)
-
-
(74, 122, 235, 111, 292, 111, 211, 133, 156, 79)
-
-
-

One-element tuples are distinguished by using a trailing command and parentheses:

-
-
a_lone_whale = (101,)
-
-
(101,)
-
-
-

For the many purposes, tuples can be exchanged for vectors, as both are iterable3:

-
-
sum(whale), mean(whale), length(whale)
-
-
(1524, 152.4, 10)
-
-
-

Unlike vectors, but like numbers, tuples can not be modified after construction. This allows tuples to be quite useful – and performant – for programming purposes.

-

Tuples can also have names. The basic construction uses “key=value” pairs:

-
-
test_scores = (Alice = 87, Bob = 72, Shirley = 99)
-
-
(Alice = 87, Bob = 72, Shirley = 99)
-
-
-

The names are not quoted, and are stored internally as a tuple of symbols. Extra effort is necessary to create names with spaces or number. In the following example we show that var"..." is useful to create more complicated symbols4:

-
-
children = (var"X Æ A-Xii" = 1,
-            var"Vivian Jenna Wilson" = 2,
-            var"Exa Dark Sideræl" = 3)
-
-
(var"X Æ A-Xii" = 1, var"Vivian Jenna Wilson" = 2, var"Exa Dark Sideræl" = 3)
-
-
-

The above also shows that Unicode values are easily used within Julia in strings or as identifiers.

-

Named tuples can also have their “names” attached via parsing, through a syntax similar to keyword arguments of functions, as identifiers imply names:

-
-
a = [1,2]; b = [3,4]
-(; a, b)  # same as (a=a, b=b)
-
-
(a = [1, 2], b = [3, 4])
-
-
-

This gives several different ways to construct \(1\)-element named tuples, where something is done to disambiguate the use of enclosing parentheses:

-
-
(; a), (; a=a), (; :a => a), (a=a, )
-
-
((a = [1, 2],), (a = [1, 2],), (a = [1, 2],), (a = [1, 2],))
-
-
-

Named tuples can have their values accessed by name using getproperty, which has a . for convenient syntax:

-
-
test_scores.Alice
-
-
87
-
-
-

The generic function keys will return the names (“keys” are used to look up a value) and values will extract the values.

-

Named tuples being both named and heterogeneous are a natural container for collecting data on several different variables for a single case, as will be seen in the discussion on tabular data.

-
-

Associative arrays

-

Abstractly, an associative array is a container for storing (key,value) pairs. Julia has the notation key => value for representing a pair. A named tuple is an immutable associative array where the keys are symbols. A dictionary is a more general mutable type with the keys being arbitrary (numbers, strings, symbols, …). There are various implementations of an AbstractDict, the Dict constructor being the most commonly used, though the one provided in the Dictionaries package will be seen. As with a named tuple, the keys are returned by keys, the values by values, and pairs of (key/value)s by pairs.

-
-
-
-

Indexing

-

The elements of a vector, like a data set, are indexed. For vectors Julia uses one-based indexing5. The whale data, as defined, has \(10\) elements, we can get the third, fourth, and sixth with:

-
-
whale = [74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-whale[3], whale[4], whale[6]
-
-
(235, 111, 111)
-
-
-

The above uses three function calls, and displays as a tuple does. (Since the commas produce a tuple.)

-

To retrieve these same values in a single call, we can pass a vector of indices:

-
-
whale[ [3,4,6] ]
-
-
3-element Vector{Int64}:
- 235
- 111
- 111
-
-
-

The end keyword refers to the last index of a collection. Similarly, begin refers to the first index inside the square brackets.

-
-
whale[end], whale[begin]
-
-
(79, 74)
-
-
-

These values allow simple arithmetic. Here we see how to trim off the first and last through indexing6:

-
-
whale[begin+1:end-1] |> show
-
-
[122, 235, 111, 292, 111, 211, 133, 156]
-
-
-

The colon, :, refers to all indices in a vector; whale[:] will return a copy of the data in whale.

-
-
-
- -
-
-Indexing with a container of values -
-
-
-

The range 1:1 specifies the value 1, as does just 1, but for indexing the two are different:

-
-
whale[1], whale[1:1]
-
-
(74, [74])
-
-
-

The design is indexing by a scalar – like 1 – can drop dimensions (the vector becomes a scalar), whereas indexing by a container – like 1:1 or, say, [1] – does not drop dimensions. The documentation for indexing of an array has: “If all the indices are scalars, then the result, X, is a single element from the array, A. Otherwise, X is an array with the same number of dimensions as the sum of the dimensionalities of all the indices.”

-
-
-
-

Views

-

The extraction of values from a vector, as above, necessitates the allocation of memory to store the copy of the values. When the data set is large, or is accessed many times, these allocations may be avoided using a “view” of the data. Views create a lazy reference to the underlying data in the array. The view function takes the object as its first argument, and indices for its remaining arguments7:

-
-
view(whale, [3,4,6])
-
-
3-element view(::Vector{Int64}, [3, 4, 6]) with eltype Int64:
- 235
- 111
- 111
-
-
-

The end and begin keywords do not work with view8; the lastindex and firstindex functions do:

-
-
view(whale, firstindex(whale)+1:lastindex(whale)-1) |> show
-
-
[122, 235, 111, 292, 111, 211, 133, 156]
-
-
-
-
-
-

Assignment

-

The assignment

-
-
whale = [74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-show(whale)
-
-
[74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-
-
-

binds the name whale to the container holding the \(10\) numbers.

-

If we make another assignment, as in

-
-
whale_copy = whale
-show(whale_copy)
-
-
[74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-
-
-

the container is copied, but – unlike if we had used copy(whale) – the two variables point to the same container. When a vector is passed into a function, the function works with the container, not a copy.

-
-
-

Modification

-

Data may need to be modified after read in or constructed, such as is done with data cleaning. As such, the values in the data vector may need to be reassigned. This is carried out by using the indexing notation on the left-hand side of an assignment:

-
-
whale[1] = 75
-
-
75
-
-
-

The value 75 is returned, as the right-hand side of an assignment is always the returned value. We can see that whale was modified:

-
-
whale[1], whale_copy[1]
-
-
(75, 75)
-
-
-

We also see that as whale_copy points to the same container, it too was modified.

-
-
-
- -
-
-Mutation -
-
-
-

When a vector is passed to a function, if there is no copy made (as opposed to a simple naming), then changes to the vector in the function will effect the original vector as the container used in the body of the function isn’t changed. Functions which modify an argument that is passed to them (usually the first one) are conventionally named with a trailing !, though this has no semantic implication.

-
-
-

Multiple values can be assigned at once. For example, if the data was mis-arranged chronologically, we might have:

-
-
whale[ [1,2,3] ] = [235, 74, 122]
-show(whale)
-
-
[235, 74, 122, 111, 292, 111, 211, 133, 156, 79]
-
-
-

The above modified the original container, so these changes would also be reflected in whale_copy.

-

Julia does not recycle values to be assigned by default, where recycling involves some rules where the space referenced on the left-hand side of an assignment does not match in size the space needed to store the right-hand side of an assignment. Broadcasted assignment (cf. ?.=) can produce a similar behavior. Broadcasted assignment expands the right-hand side to match the space expected on the left side (when possible) and then does the assignment. For example, if we wanted to use a sentinel value to indicate unknown data for the first 3 values, we might have:

-
-
whale[[1,2,3]] .= 999
-show(whale)
-
-
[999, 999, 999, 111, 292, 111, 211, 133, 156, 79]
-
-
-

Notice, without the dot an error will be thrown

-
-
whale[[4,5,6]] = 999  # errors
-
-
LoadError: ArgumentError: indexed assignment with a single value to possibly many locations is not supported; perhaps use broadcasting `.=` instead?
-
-
-

The “recycling” above uses the left-hand side to identify the size needed. Broadcasted assignment also works with the entire collection. For example, the following command replaces the current data with the original data:

-
-
whale .= [74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-show(whale)
-
-
[74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-
-
-

But whale is not a new object – as it would be without that dot – but rather, these values are placed into the container whale already refers to – which also is the container whale_copy points at:

-
-
show(whale_copy)
-
-
[74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-
-
-

This assignment avoids the needed allocation of more memory.

- -

The use of [:] on the left-hand side also does in-place assignment9. So unlike whale = [...] which replaces the container whale points at, this command reuses the container:

-
-
whale[:] = [74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-show(whale)
-
-
[74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-
-
- - ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Various indexing patterns.
Index styleExplanation
x[1]The first element of x.
x[:]Copy of all elements of x.
x[end]The last element of x.
x[first]The first element of x.
x[[2,3]]The second and third elements of x.
typeof(x)[]\(0\)-length vector of same type as x.
eltype(x)Element type of container x.
x[1] .= 5Assign a value of 5 to first element of x.
x[[2,3]] .= 4Broadcasted assignment to second and third elements
x[[2,3]] = [4,5]Assign values to second and third elements of x.
x[:] = [1, 2, 3]In-place assignment. Size and type of right-hand side must match left-hand side
-
-

Vector size

-

A vector has a length which can not be changed during assignment. Attempting to assign to an index beyond the size will result in a BoundsError. To extend the size of a vector we can use the following generic functions:

-
    -
  • push!(v, x): extend the vector v pushing x to the last value
  • -
  • pushfirst!(v, x): extend the vector v pushing x to the first value
  • -
  • append!(v, v1): append the entries in v1 to the end of v
  • -
  • insert(v, i, x): insert x into v at index i, shifting as needed
  • -
  • vcat(v, v1): vertically concatenate v and v1. (Unlike append! this returns a new vector, so the type of the output will be recomputed).
  • -
-

The vector can also be shrunk. These generic functions for containers are useful:

-
    -
  • pop!(v): remove last element from a vector; return element
  • -
  • popfirst!(v): remove first element from a vector; return element
  • -
  • deleteat!(v, i): remove ithe element from a vector; returns vector
  • -
  • empty!(v): remove all elements from a vector
  • -
-

Again, the trailing ! in a function name is a convention to indicate to the user that the function will mutate its arguments, conventionally its first one. That is, a function like pop!(v) does two things: it returns the last element and also shortens the vector v that is passed in.

-
-
-

Vectors have an element type

-

The assignment whale = [74, 122, 235, ...] assigns a container of a specific type to the variable whale. The eltype(whale) command will return this element type. Subsequent assignments to this container must contain values that can be promoted to that type, otherwise an error will be thrown.

-

For example, we can’t assign a fractional number of whales:

-
-
whale[1] = 74.5
-
-
LoadError: InexactError: Int64(74.5)
-
-
-

An InexactError is thrown because, whale is a vector of integers, and 74.5 can’t be automatically promoted to an integer (as 74.0 could be).

-

A similar thing happens if we attempt to assign missing to a value, as in whale[1] = missing. In this case, a MethodError is thrown, which comes from the attempt to “convertmissing into the underlying integer type.

-

The way to fix this is to create a new container that allows a wider type. For Float64 values that can be achieved in the cumbersome manner of converting all the values to the wider type:

-
-
whale = convert(Vector{Float64}, whale)
-whale[1] = 74.5
-
-
74.5
-
-
-

This assigns whale to a new container which accepts floating point values, and then reassigns the first one.

-

Though cumbersome, this is not typical usage, as the constructor used to create the data set will promote to a common type, so it would only matter when adjusting the initial values.

-

For the special case of assigning a missing value, the allowmissing function from the DataFrames package10 creates a vector with a type that allows – as well – missing values11. Again, re-assignment is necessary:

-
-
using DataFrames
-whale = allowmissing(whale)
-whale[1] = missing
-
-
missing
-
-
-
-
-

Broadcasting

-

As seen, the functions length and sum are reductions – in this case, returning a single number, a scalar, from a vector of numbers. To compute a sample standard deviation, say, we follow the formula:

-

\[ -s = \sqrt{\frac{\sum_i (x_i - \bar{x})^2 }{n-1}}. -\]

-

To do so we would need to:

-
    -
  • Subtract a scalar value, resulting for a reduction, from from each element of the data vector: (\(x_i - \bar{x}\)).

  • -
  • Apply the squaring function to each element of this difference.

  • -
  • Reduce the resulting data to a number through sum.

  • -
  • Divide a number by another number and take the square root.

  • -
-

Embarking on this punch list with a naive attempt at the first – whale - mean(whale) – will fail.

-

The subtraction of a scalar value from a vector value is not defined, as Julia is not implicitly vectorized. Rather the user must be explicit. For this, the concept of broadcasting is useful. In this context, broadcasting will expand the scalar to match the size of the vector and then use vector subtraction to find the result. Broadcasting is done simply by adding a “.” (the dot) to the function. For infix operations like - this is before the operator:

-
-
whale = [74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-whale .- mean(whale)  |> show
-
-
[-78.4, -30.400000000000006, 82.6, -41.400000000000006, 139.6, -41.400000000000006, 58.599999999999994, -19.400000000000006, 3.5999999999999943, -73.4]
-
-
-

We also would need to square these values. This could also be done by broadcasting ^, as in:

-
-
(whale .- mean(whale)).^2  |> show
-
-
[6146.560000000001, 924.1600000000003, 6822.759999999999, 1713.9600000000005, 19488.16, 1713.9600000000005, 3433.959999999999, 376.36000000000024, 12.959999999999958, 5387.56]
-
-
-

To avoid having to use too many dots, it is typical to define a function for doing a scalar computation and broadcast that12. An example will wait, but to illustrate the syntax, we can broadcast the sqrt function using a “.” after the function name and before the opening parentheses:

-
-
sqrt.(whale)  |> show
-
-
[8.602325267042627, 11.045361017187261, 15.329709716755891, 10.535653752852738, 17.08800749063506, 10.535653752852738, 14.52583904633395, 11.532562594670797, 12.489995996796797, 8.888194417315589]
-
-
-

Broadcasting works with functions of multiple arguments and multiple shapes.

-

The use of multiple shapes allows scalars and vectors to be broadcast over and is used in whale .- mean(whale). With this, the standard deviation could be computed with:

-
-
sqrt( sum((whale .- mean(whale)).^2)/(length(whale) - 1) )
-
-
71.5078861229849
-
-
-

Vector and row vectors can also be broadcast over, So [1,2] .+ [3,4] does vector addition, [1,2] .+ [3 4] pads out the vector to a matrix, the row vector to a matrix, then adds entry-by-entry:

-
-
(v=[1,2], rv=[3 4], va = [1,2] .+ [3,4], ma = [1, 2] .+ [3 4], ck=[1 1; 2 2] + [3 4; 3 4])
-
-
(v = [1, 2], rv = [3 4], va = [4, 6], ma = [4 5; 5 6], ck = [4 5; 5 6])
-
-
-

This behavior may not be desirable. Some objects broadcast as scalars, others as containers. But there may be times where broadcasting as a container may be incorrect. To force a value to broadcast like a scalar, the value can be wrapped in Ref. That is mean(whale) is the same as mean.(Ref(whale)) but mean.(whale) would broadcast mean over each element in whale. As mean for a single number is just that number, mean.(whale) would just be the same values.

-
-
-
-
-

Data types

-

An old taxonomy of levels of measurement include nominal, ordinal, interval, and ratio; where nominal values have no rank; ordinal values have a rank, but no meaning is assigned to the difference between values; interval data has a meaning between differences but \(0\) is arbitrary; and ratio has a meaningful zero, such as most numeric data. As data can easily be coded (explicitly or behind the scenes) with numbers, this keeps the different types distinct. However, for use with the computer, in particular Julia here, we see it also more sense to emphasize different aspects of the data related to the underlying type.

-

Julia has numerous data types. Some are “abstract” types, such as Real or Integer, others are “concrete”, often indicating how the data is stored, such as Float64 or Int64, with the “64” indicating \(64\) bits of memory. In Julia, these data types can be used to direct method dispatch. For statistics a few common types are used to represent data.

-

These are reviewed in the following.

-
-

Numeric data types

-

The Julia parser readily identifies the following values of \(1\) and reads each using a different type:

-
-
1, 1.0, 1//1, 1 + 0im, big(1), BigFloat(1)
-
-
(1, 1.0, 1//1, 1 + 0im, 1, 1.0)
-
-
-

Respectively, these represent integer, floating point, rational, complex, and two types of “big” numbers. Julia uses a promotion machinery when different types are mixed. For example, we have:

-
-
x = 1 + 1.0 + 1//1 + 1 + 0im + big(1) + BigFloat(1)
-
-
6.0 + 0.0im
-
-
-

The type of x must be both complex and be able to store the underlying numbers, which may be “big” numbers:

-
-
typeof(x)
-
-
Complex{BigFloat}
-
-
-

The above illustrates that addition of an integer and a floating point yields a floating point, and adding to a complex number returns a complex number, etc.

-
-
-
- -
-
-Type stability -
-
-
-

Julia programmers try to make functions “type stable,” if possible, as this generally leads to more performant code, once compiled. A consequence is addition of numbers, like 1 + 1.0 will always be the wider type (here a floating point value), even if in the case of these particular values an integer could be the answer. That is the output type of + here is determined by the input types, not the input values.

-
-
-

For data consisting of counts, integers are typically used. If storage is an issue (e.g., lots of data, but not a lot of different values), different forms of integers which use less data may be used.

-

For data on measurements, with a continuous nature, floating point values are the natural choice. Floating point can represent most integer values exactly, and fractional and irrational values either exactly or approximately. Rational numbers can represent fractional data exactly, though it should be expected that operations with rational values are less performant than for floating point values.

-

Complex values in Julia are based on an underlying data type holding the two numbers in a + bi.

-
-
-

Categorical data types

-

There are different options available for the storage of categorical data.

-
-

Character data

-

The String type in Julia is the default type for holding character data. Strings are created with matching single quotes or – for multiline strings – with matching triple quotes:

-
-
s = "The quick brown fox ..."
-t = """
-Four score and seven years ago
-our fathers brought forth...
-"""
-
-
"Four score and seven years ago\nour fathers brought forth...\n"
-
-
-

The string type in Julia can hold Unicode data, such as non-ASCII letters and even emojis.

-
-
-
- -
-
-Entering Unicode -
-
-
-

In the REPL Unicode values may be entered using LaTeX shortcuts, e,g., \alpha[tab] to produce \(\alpha\). Many interfaces also allow this.

-
-
-

Double quotes are used to create a string. Triple, double quotes can be used to create multi-line strings.

-

Single quotes are for Char types. A character represents a Unicode code point. Strings are iterable, and iteration yields back Char values. The collect function iterates over an object and returns the values. Here we see the Chars in a string:

-
-
s = "Zoë"
-collect(s)
-
-
3-element Vector{Char}:
- 'Z': ASCII/Unicode U+005A (category Lu: Letter, uppercase)
- 'o': ASCII/Unicode U+006F (category Ll: Letter, lowercase)
- 'ë': Unicode U+00EB (category Ll: Letter, lowercase)
-
-
-

Strings are also indexable. When indexed by a single value, a Char type is returned, when indexed by a range of a values a string is returned. Be warned, indexing into non-ascii strings may error. Here is an example using a string from Julia’s manual:

-
-
j = "jμΛIα"
-length(j), j[2], j[2:4]  # 2:4 represent the value 2,3,4
-
-
(5, 'μ', "μΛ")
-
-
-

The value returned by j[2] is a character, whereas that returned by j[2:4] is a string13. However, attempting to extract j[3] will be an error.

-
-
-

String operations

-

Strings can be combined many different ways.

-

The * operation is used to combine strings or chars. The basic usage is straight forward:

-
-
out = "a" * "bee" * "see"
-
-
"abeesee"
-
-
-

The ^ operation when used with an integer exponent will repeat the argument:14

-
-
out = "dot "^3
-
-
"dot dot dot "
-
-
-

More generally, join will combine an iterable of strings or characters. If a delimiter is specified, it will be inserted between values (with an option to indicate the last delimiter differently):

-
-
j = "jμΛIα"
-join(collect(j), ","), join(collect(j), ", ", ", and ")
-
-
("j,μ,Λ,I,α", "j, μ, Λ, I, and α")
-
-
-

Julia makes string interpolation easy. Within a string, a value of a variable can be inserted using a dollar sign to reference the variable. This is more general, as computations can also be inserted. Below parentheses are used to delimit the interpolated command:

-
-
x = "Alice"
-out = "$x knows that 2 + 2 is equal to $(2+2)"
-
-
"Alice knows that 2 + 2 is equal to 4"
-
-
-

(To include a dollar sign in the string, it can be escape with a leading slash, “\”, or a raw string can be used, as in raw"$(2+2) is not evaluated".)

-

For formatted values, such as needed when printing floating point values, the built-in Printf package provides support. For more performant solutions, an IOBuffer can be useful.15

-

A common means to generate strings is from reading in delimited files, such as comma-separated files. These may produce strings with leading or trailing spaces. To strip these off, Julia offers strip, lstrip, and rstrip. The lstrip function strips the left side, rstrip the right side, and strip combines the two. An option to pass in other characters to strip besides spaces is available.

-
-
strip("   abc    "),  strip("...abc...", '.')
-
-
("abc", "abc")
-
-
-
-

Discussing data sets for different type of data adds no more complication as vectors in Julia are typed so a data set of character data might simply be stored as a vector of string data, such as:

-
-
job_title = ["Data Scientist", "Machine Learning Scientist", "Big Data Engineer"]
-
-
3-element Vector{String}:
- "Data Scientist"
- "Machine Learning Scientist"
- "Big Data Engineer"
-
-
-
-
-

Symbols

-

Julia as a language can be used to represent the language’s code as a data structure in the language. Symbols are needed to refer to the name, or identifier, of a variable as opposed to the values in the variable. Symbols, being part of the language, are used for other purposes, such as keys for a named tuple of flags for an argument. The access pattern nt.a has been mentioned; this is a convenience for getfield(nt, :a), the symbol being used as a key. When data frames are introduced – essentially a collection of matched data vectors – symbols will be used to reference the individual variables.

-

The simple constructor for a symbol is :, as in :some_symbol. (The : constructor makes expressions, but in this use, these are interpreted as symbols.) The Symbol constructor can also be used to create symbols with spaces, e.g., Symbol("some symbol"); the string macro var"..." is a convenience.

-

The string function (or String constructor) will create a string from a symbol.

-
-
-

Factors

-

While character data is useful for representing data that is unique to each case (like an individual’s address), there are advantages to using a different representation for data with many anticipated repeated values (like an individual’s state). Factors are a data structure that allow the user to see full labels, but internally, the computer only sees an efficient pool of possible levels or values. That is, a factor is essentially a mapping between a label and a corresponding key, with a possible ordering assigned to the labels.

-

Factors are not a built-in data type, but are provided by the CategoricalArrays package, which is loaded as other packages are:

-
-
using CategoricalArrays
-
-

Some main tasks when working with such data are:

-
    -
  • reordering factor levels

  • -
  • changing the labels of factor levels

  • -
  • combining several levels into one

  • -
-

We use an example based on coffee sizes at your neighborhood Starbucks. An order consisted of \(4\) drinks with these sizes, put into a categorical array:

-
-
x = categorical(["Grande", "Venti", "Tall", "Venti"], ordered=true)
-
-
4-element CategoricalArray{String,1,UInt32}:
- "Grande"
- "Venti"
- "Tall"
- "Venti"
-
-
-

This appears to be like a character vector, but the type is different. First, let’s peek to see how values are internally stored. The user-visible values are stored with:

-
-
x.pool
-
-
CategoricalPool{String, UInt32}(["Grande", "Tall", "Venti"]) with ordered levels
-
-
-

Internally, the computer sees:

-
-
x.refs
-
-
4-element Vector{UInt32}:
- 0x00000001
- 0x00000003
- 0x00000002
- 0x00000003
-
-
-

The levels are the labels assigned to the internal values. We can see that the levels are internally kept as 32-bit integers, which in general is more space efficient than storing the labels. The command levelcode.(x) will show the values using the more readable 64-bit integers.

-

Working with levels is the key difference. First levels, may be unordered (nominal) or ordered (ordinal), the latter indicated by specifying ordered=true to the constructor or by calling ordered!(x, true) for an unordered variable.

-

The example data has an odd order (call levels(x) to see), it coming from lexical sorting. So Grande is before Venti, despite the latter being 16 oz. and the former 20 oz (for hot drinks, 24oz for cold)16.

-
-
x[1] < x[2]
-
-
true
-
-
-

To reorder, we call the levels! function with the desired order:

-
-
levels!(x, ["Tall", "Venti", "Grande"])
-x[1] > x[2]
-
-
true
-
-
-

The levels can be extended through assignment. For example, we might prefer to change the label “Tall” for a new label “Tall, 12oz”. We can readily do this for a single value through assignment:

-
-
x[3] = "Tall, 12oz"
-
-
"Tall, 12oz"
-
-
-

After this command, there are 4 levels though only 3 used in the vector. To trim out extra levels, the droplevels! function can be used.

-

In general and for multiple replacements, the replace function is useful and the levels are adjusted accordingly:

-
-
replace!(x, "Grande" => "Grande, 20oz", "Venti" => "Venti, 16oz")
-
-
4-element CategoricalArray{String,1,UInt32}:
- "Grande, 20oz"
- "Venti, 16oz"
- "Tall, 12oz"
- "Venti, 16oz"
-
-
-

The same command can be used to consolidate labels. For example, suppose we wanted to use just “Really big” and “regular sized” where we lump the two smaller ones together. It could be we just use replace with the right hand side using duplicates. Below we use broadcasting to illustrate how this might be done more easily were there many levels to combine. We do this in two lines, to be less confusing, but it could be written with just one:

-
-
replace!(x, (["Tall, 12oz", "Venti, 16oz"] .=> "regular sized")...)
-replace!(x, "Grande, 20oz" => "Really big")
-x
-
-
4-element CategoricalArray{String,1,UInt32}:
- "Really big"
- "regular sized"
- "regular sized"
- "regular sized"
-
-
-

Categorical data, like numeric data, can also be combined with vcat, say. If ordered, as much as possible, an order will attempt to be matched.

-
-
-
-

Logical data and operators

-

The Boolean type in Julia has two values: true and false.

-

Boolean values have their own algebra. The short-circuiting && and || implement “and” and “or:”

-
-
false && false, true && false, false && true, true && true
-
-
(false, false, false, true)
-
-
-
-
false || false, true || false, false || true, true || true
-
-
(false, true, true, true)
-
-
-

These are called “short circuiting,” as the right-hand side is only evaluated if it need be (as in true || false, the statement is known to be true after the left side of || is evaluated, so the right-hand side is not evaluated). This is used frequently for error messages, as the error is not called when the expression is true. These operations may be broadcasted, so the above might have been illustrated by

-
-
[true false] .&& [true, false]  # produces a matrix to accommodate sizes
-
-
2×2 BitMatrix:
- 1  0
- 0  0
-
-
-

The operations have left-to-right associativity, so it is common to see them in a sequence.

-

Many operations promote Boolean values to 1 and 0. For example, true + false is 1, true * false is 0, and sum([true,false, true]) is 2; internally, the complex number \(i\) is internally a pair (false, true) indicating no real part and an imaginary part.

-

Boolean values are returned by the comparison operators <, <=, ==, ===, >=, and >. These have the expected meaning, save == is a test of equality (not =, which is used for assignment) and === is a test of whether two values are identical. (E.g. for a vector x we have x == copy(x) is true, but x === copy(x) is false, as the latter does not point to the exact same container.) The symbol ! is used for negation.

-

Boolean values can be used for indexing. Suppose inds is a vector of trues and falses with the same length as a vector x, then x[inds] will return those values from x where inds is true.

-

To create such Boolean vectors, the comparison operators are typically used combined with broadcasting. For example, the following redefines the whale dataset, then filters out only those values bigger than or equal to \(200\):

-
-
whale = [74, 122, 235, 111, 292, 111, 211, 133, 156, 79]
-whale[ whale .>= 200 ]
-
-
3-element Vector{Int64}:
- 235
- 292
- 211
-
-
-

Or, this example – which shows the mathematically natural chaining of comparison operators – filters out only the values in \([100, 125)\):

-
-
whale[ 100 .<= whale .< 125 ]
-
-
3-element Vector{Int64}:
- 122
- 111
- 111
-
-
-
-

Querying elements

-

There are other helper functions to query the elements of a Boolean vector.

-
    -
  • any(v) will return true if any of the elements of the Boolean vector v are true.
  • -
  • all(v) will return true if all of the elements of the Boolean vector v are true.
  • -
  • findfirst(v) will return the index of the first true value in the Boolean vector v or return nothing if none is found. A predicate function can be used, as in findfirst(f, w) which effectively calls findfirst on f applied to each element of a vector w.
  • -
  • findlast(v) will return the index of the last true value in the Boolean vector v or return nothing if none is found. A predicate function can be used, as in findlast(f, w) which effectively calls findlast on f applied to each element of a vector w.
  • -
  • findnext(v, i) will find the first true value after index i in the Boolean vector v or return nothing if none is found. A predicate function can be used, as in findnext(f, w, i) which effectively calls findnext on f applied to each element of a vector w.
  • -
-

In general, x in v will check if the element x is in the vector v. The Unicode operator (\in[tab]) can replace in.

-
-
-
-

Date and time types

-

Julia provides the built-in Dates module for working with date and time data. This module need not be installed, but is not loaded by default, so loading or importing it is needed to access the functionality.

-
-
using Dates
-
-

Constructing a date is done with the Date constructor which expects a year, followed by an optional month and day. Date and time objects have the DateTime constructor. This example uses the vernal equinox, summer solstice, autumnal equinox, and winter solstice in the year 2022 for illustration:

-
-
ve, ss, ae, ws = Date(2022, 3, 20), Date(2022, 6, 21), Date(2022, 9, 22), Date(2022, 12, 21)
-
-
(Date("2022-03-20"), Date("2022-06-21"), Date("2022-09-22"), Date("2022-12-21"))
-
-
-

Dates can also be parsed from a string. The following uses the default format;17:

-
-
Date.(["2022-03-20", "2022-06-21", "2022-09-22", "2022-12-21"])
-
-
4-element Vector{Date}:
- 2022-03-20
- 2022-06-21
- 2022-09-22
- 2022-12-21
-
-
-

Date objects have the accessors year, month, and day:

-
-
year(ve), month(ve), day(ve)
-
-
(2022, 3, 20)
-
-
-

The objects allow for natural operations, such as comparisons and differences:

-
-
ve < ss < ae < ws, ae - ve, ve + Day(93)
-
-
(true, Day(186), Date("2022-06-21"))
-
-
-

The difference is returned in the number of days. The last command shows the duration of 93 days can be constructed with Day and its value added to a Date object.

-

There are ways to query how a date falls within the calendar

-
-
dayofyear(ve), dayofweek(ve), dayname(ve), dayofweekofmonth(ve)
-
-
(79, 7, "Sunday", 3)
-
-
-

The last command indicating that the vernal equinox in 2022 fell on the third Sunday of the month.

-
-
-

Structured data

-

Structured data may not represent statistical data, but is useful nonetheless, e.g, for specifying the year of the counts in the whale data set.

-

For a vector of all ones or all zeros, the ones and zeros functions are useful. The command ones(n) will return a vector of n zeros using the default Float64 type. To specify a different type, such as Int64, the two-argument form, ones(T, n), is available. Similarly, zeros is used to create a vector of zeros. The singular one(), zero() (one one(T) and zero(T)) are useful for generic programming. For example, we use the idiom one.(x) to create a vector of all 1s with the length and type of the vector x.

-

Arithmetic sequences, \(a, a+h, a+2h, \dots, b\) can be created with the colon operator a:h:b or a:b when h is 1. This operator returns a recipe for generating the sequence, it is lazy – it does not generate the sequence. The precedence is such that simple arithmetic operations do not need parentheses. That is a+1:b-1 represents the sequence \(a+1, a+2, \dots, b-1\). Arithmetic sequences prove useful for indexing into a vector.

-

The colon operator for floating point values may or may not stop at b. Programming this is harder than it seems. The simple example of 1/10:1/10:3/10 should be \(1/10, 2/10, 3/10\), but it turns out that on the computer 1/10 + 2*1/10 is actually just larger than 3/10. See the value of 3/10 - (1/10 + 1/10 + 1/10) to investigate. However, the algorithm of : does produce the result with \(3\) values here.

-
-
1/10:1/10:3/10 |> collect
-
-
3-element Vector{Float64}:
- 0.1
- 0.2
- 0.3
-
-
-

The range function creates sequences. The common usage is range(start, stop, length). That is a:h:b specifies a step size, whereas the positional arguments of range specify the number of values between the starting and stopping values. Keyword arguments allow other combinations of start, stop, step, and length. The range returns a similar expression as the colon operator, it does not realize the entire range of values.

-

Scalar multiplication, scalar division, and addition of like-sized ranges are defined, as they return an arithmetic sequence.

-

For example, if whale holds beaching numbers for the years 2010 through 2019, we can get the odd years though the following:

-
-
oddyrs = 2011:2:2019
-whale[oddyrs .- 2010 .+ 1] # 1-based offset is why we add 1
-
-
5-element Vector{Int64}:
- 122
- 111
- 111
- 133
-  79
-
-
-

The fill function creates an array of a specific size, filling it with a value. For example, to create a vector of all 1s of length 10, fill(1, 10) will do so. A tuple is used for the second positional argument to construct higher-dimensional filled arrays.

-

For more complicated patterns, the repeat function can prove useful. For an array, it creates a new array comprised of repeats of the given array. For a vector, the only use here, two repeating patterns are often desired: repeating the whole vector several times; repeating each entry of a vector several times then combining.

-
-
v = [1, 2, 3]
-repeat(v, 3) |> show
-
-
[1, 2, 3, 1, 2, 3, 1, 2, 3]
-
-
-

In the above, the entire vector is repeated three times. The 3 is passed to the outer argument. To repeat the 1s then the 2s and then the 3s the inner argument is used:

-
-
repeat(v, inner=3) |> show
-
-
[1, 1, 1, 2, 2, 2, 3, 3, 3]
-
-
-
-
-
-

Functions

-

Julia has a simple syntax for user-defined functions.

-

For simple functions, the syntax borrowed from common mathematics is used. For example, here we define a function to find the mad defined by the median of the transformed data \(|x_i - M_x|\).

-
-
MAD(x) = median(abs.(x .- median(x)))
-
-
MAD (generic function with 1 method)
-
-
-

The function is named MAD (to distinguish it from the already defined mad function in StatsBase) and, as written, accepts a vector and returns a summary number:

-
-
MAD(whale)
-
-
38.5
-
-
-

For functions which are not one liners, a pair of function-end keywords will define a block. For example, the following computes the fifth standardized moment (skewness and kurtosis related to the 3rd and 4th). The first line is one way to document a function in Julia.

-
-
"Compute 5th standardized momemt: m_5 / m_2^(5/2)"
-function fifth_sm(x)
-    xbar, n = mean(x), length(x)
-    m5 = sum((xi - xbar)^5 for xi in x) / n
-    m2 = sum((xi - xbar)^2 for xi in x) / n
-    m5 / m2^(5/2)
-end
-fifth_sm(whale)
-
-
3.6325544657722215
-
-
-

(The above uses a generator, created by the use of for and in to loop over the different values of x rather than broadcasting.)

-

The repetition above in m5 and m2 could be avoided if we made a function to compute the sample moments about the mean which accepted both the data and a value for the exponent:

-
-
sample_moment(x, n=2) = sum((xi - x)^n for xi in x) / length(x)
-fifth_sm(x) = sample_moment(x, 5) / sample_moment(x)^(5/2)
-
-
fifth_sm (generic function with 1 method)
-
-
-

The variable n is in the second position and has a default value of 2 which is employed in the denominator of the above, where sample_moment is called with just a single argument.

-

There can be many positional arguments, only the last ones can have default values specified.

-

Functions can have a variable number of arguments. Here is a way to find the proportions of a set of numbers that is not stored in a container, but rather is passed to the function separated by commas:

-
-
proportion(xs...) = collect(xs) / sum(xs)
-
-
proportion (generic function with 1 method)
-
-
-

The splat syntax ... indicates a variable number of arguments in a function definition, and can be used to expand a list of arguments when used inside a function call. The use of collect, above, is needed above to generate a vector, as xs is passed to the body of the function as a tuple and tuples do not have division defined for them.18

-

The mad function from StatsBase has signature mad(x; center=median(x), normalize=true). This shows the use of keyword arguments. These have a default value, which, as illustrated, can depend on the data passed in. To call a function without the default, the keyword is typed, as in:

-
-
mad(whale; center=mean(whale))
-
-
74.13011092528009
-
-
-

(We use a semicolon to separate positional arguments from keyword arguments, as that is needed to define a keyword, but commas can be used to call a keyword argument. What is important is that any keywords come last when a function is called.)

-

Functions, as defined above, are methods of a generic function. That is, there can be more than one method for a given name. (There are over 200 methods for the generic function named + in base Julia – and packages can extend this even more.) To direct or dispatch a call to the appropriate method, Julia considers the number and types of its positional arguments. That is, like +, functions can be defined differently for integers and floating point values.

-

We might like our MAD function to be more graceful than to throw a MethodError if a vector of strings is passed to it. A vector of strings has type Vector{String} so we could make a method just for that type:19 `Julia makes adding methods easy, but the types that are used to extend the function shouldn’t be owned by other packages, as this is considered type-piracy. (Failing to do so may prompt a request for a letter of marque.)

-
-
MAD(x::Vector{String}) = print("Sorry, MAD is not defined for vectors of strings")
-MAD(["one", "three", "four"])
-
-
Sorry, MAD is not defined for vectors of strings
-
-
-

(There are many different types that one might wish to exclude and there are many tricks to efficiently code for this. It is common to define a default method which errors and then a special case for the types that can be worked with. Additionally, as types can be concrete, as the above, or abstract, it is possible to parameterize the type used for dispatch so that subtypes can be identified. For example, some operations return a SubString not a String. A vector of SubString will not match Vector{String} as even though both hold string data. The subtleties of parameterization are necessary to understand to write many special cases, but won’t be necessary in these notes.)

-

A higher order function is one that takes one or more functions as an argument. As example is the calling style of findfirst(predicate, x) where predicate is a function which returns a boolean value. Higher-order functions are widely used with data wrangling. For such uses it is convenient to be able to define an anonymous function. These do not create methods for a generic function, as they have no name (though anonymous functions can be assigned a name).

-

Anonymous functions are easily defined through the pattern: argument(s) -> body, where body, and, when multi-line, can be contained within begin/end blocks.

-

For example, to find the index of the first year that there were 200 or more whale beachings we have:

-
-
findfirst(x -> x >= 200, whale)
-
-
3
-
-
-

For the tasks of logical comparisons, there are partially-applied versions of the operators that are essentially defined like anonymous functions (cf. Base.Fix2). In particular, >=(200) can be used for the anonymous function x -> x >= 200. To illustrate, here we see the index of first year where there were 200 or more beachings and the index of the last year, using ! to negate a call (even though >=(200) would be identical):

-
-
findfirst(>=(200), whale), findlast(!<(200), whale)
-
-
(3, 7)
-
-
-

Another example would be to filter those values less than 100. The filter(predicate, v) function does this:

-
-
filter(<(100), whale)
-
-
2-element Vector{Int64}:
- 74
- 79
-
-
-
-

Broadcasting alternatives; iteration

-

As a quick illustration of a few other concepts, we consider alternatives to broadcasting that can prove useful. Consider the simplest case of broadcasting a function f over a single collection x.

-

For example here is broadcasting:

-
-
x = [1, 4, 9]
-f(x) = sqrt(x)
-f.(x)
-
-
3-element Vector{Float64}:
- 1.0
- 2.0
- 3.0
-
-
-

This use of broadcasting is also called the mapping of f over x. The map(f, x) function is defined:

-
-
map(f, x)
-
-
3-element Vector{Float64}:
- 1.0
- 2.0
- 3.0
-
-
-

Either broadcasting or map do the following: for each element of x apply the function f. This can be represented with the chaining operator, |>, adjusted to broadcast the values of x:

-
-
x .|> f
-
-
3-element Vector{Float64}:
- 1.0
- 2.0
- 3.0
-
-
-

The iteration over x can be made explicit with a for loop. The essential syntax would include:

-
-
out = Any[]
-for xi in x
-   push!(out, f(xi))
-end
-out
-
-
3-element Vector{Any}:
- 1.0
- 2.0
- 3.0
-
-
-

For loops require extra effort for accumulation, which, in the above, required the selection of a container. We used Any[] to create a zero-length container that can hold any object and push values onto this. If would be preferable to have a concrete type, which in this case is just Float64[], but, in general, that requires some peeking into the output of f. This is to point out some background work performed by map and broadcasting that simplify other common tasks.

-

The for xi in x part of the for loop assigns xi to each iterate of x. There are some iterations that return more than one value at once, and it is common to see these destructured in the syntax. For example, enumerate is a helper function which takes an iterable object, like a vector, and iterates over both the index and the value:

-
-
for (i, xi) in enumerate(x)
-  print("element $i is $xi. ")
-end
-
-
element 1 is 1. element 2 is 4. element 3 is 9. 
-
-
-

A comprehension is a good alternative to a for loop when accumulation is required and the computation for each iterate is simple to express. Comprehensions have the syntax [ex for x in xs] and additional syntax for multiple iterations. The expression ex can use the variable x; xs is some iterable, such as a vector. For example, we might have this alternative to f.(x)

-
-
[xi - mean(x) for xi in x]
-
-
3-element Vector{Float64}:
- -3.666666666666667
- -0.666666666666667
-  4.333333333333333
-
-
-

Comprehensions use generators, which can also be used for many other functions, such as sum, which was previously illustrated. This form has the advantage of not needing to allocate temporary space to compute. For example sum([xi for xi in x]) would have to find space for the vector created by the comprehension, but the similar – and easier to type – sum(xi for xi in x) would not.

-

In this example, we sum the squared differences, passing an optional function to sum:

-
-
f(x) = x^2
-sum(f, xi - mean(whale) for xi in whale)
-
-
46020.399999999994
-
-
- - ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Various convenient iterators in Julia.
IteratorDescription
eachindexiterate over each index
valuesiterate over value in a container
enumerateiterate over index and value in a container
keysiterate over keys for a container
pairsiterate over (key,value) pairs in a container
zipiterate over multiple iterators; the value is a tuple with an element from each
eachcolfor tabular data, iterate over the columns
eachrowfor tabular data, iterate over the rows
-
-
-
-

Numeric summaries

-

The StatsBase package provides numerous functions to compute numeric summaries of a univariate data set.

-

For measures of center we have the mean (average), median (middle value), and mode (most common value):

-
-
mean(whale), median(whale), mode(whale), mean(trim(whale, prop=0.2))
-
-
(152.4, 127.5, 111, 140.66666666666666)
-
-
-

The trimmed mean is computed by composing mean with the trim function which returns a generator producing the trimmed values (with proportion to trim specified to prop).

-

For measures of spread we have the standard deviation, the median absolute deviation, and the inter-quartile range (\(Q_3 - Q_1\)):

-
-
std(whale), mad(whale), iqr(whale)
-
-
(71.5078861229849, 57.08018541246567, 86.25)
-
-
-

The quantile(x, p) function returns measures of position. Keywords alpha and beta can be used to adjust the algorithm employed. The 0 quantile is the minimum, the 1 quantile is the maximum, and 0.5 the median, or middle value. The \(p\)th quqntile is an interpolated value that aspires to split the data with \(p\cdot 100\)% of the values to the left and \((1-p)\cdot 100\)% of the values to the right. The p specified to quantile may be an iterable, useful to computing more than one at a time. Here we see that the type of p is used to compute the output type:

-
-
quantile(whale, (0, 1//2, 1.0))
-
-
(74, 255//2, 292.0)
-
-
-

The quantile function is used internally by StatsBase: the iqr is defined by the difference of quantile(x, [1/4, 3/4]); the summarystats function returns a summary of a data set, with the 5-number summary of the quantiles (p=0:1/4:1), the mean, the length, and the number of missing data values.

-
-
summarystats(whale)
-
-
Summary Stats:
-Length:         10
-Missing Count:  0
-Mean:           152.400000
-Std. Deviation: 71.507886
-Minimum:        74.000000
-1st Quartile:   111.000000
-Median:         127.500000
-3rd Quartile:   197.250000
-Maximum:        292.000000
-
-
-

A measure of position gives a sense of how large a value is relative to the data set. Knowing a value is the 20th percentile, say, says it is larger than 20 percent of the data and smaller than 80 percent. The percent of data less or equal a value can be computed by sum(data .<= value) or sum(x <= value for x in data).

-

The \(z\)-score is a different measure of position. It measures differences from the mean on a scale of standard deviations. The \(z\) score is computed by (value - mean(data))/std(data) or for all values at once with:

-
-
zs = (whale .- mean(whale)) / std(whale)
-
-
10-element Vector{Float64}:
- -1.0963825705204249
- -0.4251279355079199
-  1.155117351084019
- -0.5789571226982856
-  1.9522322301613686
- -0.5789571226982856
-  0.8194900335777664
- -0.2712987483175542
-  0.05034409762593779
- -1.0264602127066222
-
-
-

A rule of thumb, based on a certain bell-shaped distribution, is that values larger than \(3\) in absolute value are unusual, none of which are seen in this data.

-

The \(z\)-score is the typical form of standardization and is also supported directly through either of the following:

-
-
zs1 = zscore(whale)
-zs2 = standardize(ZScoreTransform, Float64.(whale))
-
-zs == zs1 == zs2
-
-
true
-
-
-
-
-
- -
-
-Why so many measures? -
-
-
-

There are two main measures: those based on differences and those based on position.

-

The mean is defined by

-

\[ -\bar{x} = \frac{1}{n} \sum_{i=1}^n x_i. -\]

-

A property of the mean is when the data is centered by the mean, i.e. take the transformation \(y_i = x_i - \bar{x}\), then the mean of the \(y_i\) is just \(0\). Put another way, the mean is the point where the differences to the left and right of the mean even out when added.

-

The standard deviation, as a sense of scale, has the property that if the data is centered and then scaled by the standard deviation, the center will be \(0\) and the scale will be \(1\). That is, the \(z\)-scores have mean \(0\) (as they data is centered) and standard deviation \(1\) (as the data is scaled) – the \(z\) scores speak to the “shape” or distribution of values of a data set.

-

These measures are sensitive – or not resistant – to one or more outlying values. For example, the average wealth of people in a bar changes dramatically if someone like a pre-crash Elon Musk walks in. The standard deviation is similar. So measures based on position are better when data is skewed, especially if heavily skewed. For these, the median and IQR are not impacted greatly by one large value. That is they are resistant to outliers. The extreme values (the minimum and maximum) are less so. As such, the range of the data is a poor measure of spread, the range of the middle quartiles (the IQR) a much more resistant measure of spread.

-
-
- - ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Various measures of center, spread, and position in a univariate data set.
MeasureTypeDescription
meancenterAverage value
mediancenterMiddle value
mean ∘ trimcenterTrimmed mean
varspread“Average” squared distance from mean
stdspreadThe square of the variance
madspreadThe median absolute deviation from center
IQRspreadRange of middle half of data
extremaspreadSmallest and largest values
quantilepositionValue where data would be split for give proportion
summarystatspositionThe quartiles and extrema
zscorepositionStandardizing transformation
-
-
-

Shape

-

The five-number summary gives some insight into the shape of a distribution, in that it can show skewness. The skewness function numerically quantifies this:

-
-
skewness(whale)
-
-
0.7785957149991207
-
-
-

Perfectly symmetric data has \(0\) skew.

-

The kurtosis function numerically summarizes if a distribution has long or short tails compared to a benchmark distribution, the normal:

-
-
kurtosis(whale)
-
-
-0.5833928499406711
-
-
-

These numeric summaries are helpful, but to best see the shape of a distribution graphical summaries are suggested.

-

A stem or stem-and-leaf plot is a useful summary of a data set that can be computed easily by hand for modest-sized data. In short, numbers are coded in terms of a stem and a leaf. The stems are not repeated, so the data can be more compactly displayed. The data is organized so that the extrema, the median, and the shape of the data can be quickly gleaned.

-

A stem-and-leaf plot function is not part of the StatsBase package.20 This one, below, is modified from a RosettaCode submission. It is simplified by assuming non-negative data. Essentially, it finds the right place to split a number into two values, a stem and a leaf. Then it displays each.

-
-
using Printf
-function stemleaf(a::Array{T,1}, leafsize=1) where {T<:Real}
-    ls = 10^floor(Int, log10(leafsize))
-    aa = floor.(Int, sort(a)/ls)
-    out = divrem.(aa, 10)
-    stem, leaf = first.(out), last.(out)
-    io = IOBuffer()
-    for i in stem[1]:stem[end]
-        i != 0 || println(io, "")
-        print(io, (@sprintf "%10d | " i))
-        println(io, join(map(string, leaf[stem .== i]), ""))
-    end
-    println(io, " Leaf Unit = " * string(convert(T, ls)))
-    String(take!(io))
-end
-
-
stemleaf (generic function with 2 methods)
-
-
-

For the whale data, which is spread out over a wide range, we use a leaf size of \(10\):

-
-
stemleaf(whale, 10) |> print
-
-

-         0 | 77
-         1 | 11235
-         2 | 139
- Leaf Unit = 10
-
-
-

We can identify 0|7 which is 0*100 + 70 or \(70\) as the “smallest” value. The actual smallest value is \(74\), but the data is rounded for this graphic so that just two digits (the leaf always is represented by one, the stem may use more than one) can represent all the values. Similarly \(290\) is identified as the largest values (which is actually \(292\)).

-

For graphical summaries of data, we will introduce the StatsPlots package, which adds to the Plots package recipes for many statistical graphics. In later chapters we will use a different plotting package, but for now, the StatsPlots package provides easy to use and understand methods to produce the desired graphics.

-
-
using StatsPlots
-
-

For a univariate data set, graphics are used to help gauge the center, spread, and shape of a distribution of values. Different graphics have their advantages. In the following, we illustrate the dot plot, the box plot, the histogram, the density plot, and the quantile-normal plot. There are basic recipes for each graphic that require little more than specifying which data to use, though we do adjust a few default values through keyword arguments, such as legend.

-

A dot plot shows the data on a number line, traditionally horizontally, and with circles (dots) representing each data point. For data with ties, like the whale data set, there are many techniques to disamgibuate the data in the graphic: the tied data may be stacked, using the \(y\) direction; the data may be jittered in the \(x\) direction, adding a small random to each value; the data may be jittered in the \(y\) direction. In StatsPlots, with the dotplot recipe, values are jittered when plotted vertically or when plotted with just the \(x\) values. Here we use the dotplot(x, y) method with x representing the data to display. For this, we manually jitter the x values and we make a vector of values, y all with the same value, in this case 1 as the one function makes this convenient:

-
-
jitter(x, scale=1/4) = x + randn(length(x)) * scale
-
-p1 = dotplot(jitter(whale), one.(whale); legend=false);
-
-

In Figure 1 the graph appears on the left side.

-

A dot plot shows all the data from which the reader can gauge the center, spread, and shape easily enough. For a small number of data points, such as with the whale data it is a very good graphic for assessing the distribution of values. However, for a large number of values, the graphic can get too crowded, and alternatives are useful.

-

The box plot or box-and-whisker plot of Tukey is one such excellent alternative.

-

The boxplot summarizes many aspects of a data set with basically just the five-number summary presented as a box with whiskers:

-
    -
  • The center is shown by the middle line of the box set at the median

  • -
  • The spread is seen through the IQR, the length of the box, which is set at \(Q_1\) and \(Q_3\).

  • -
  • The shape (skewed or symmetric) can be gauged by how different the different quartiles are: the first quartile is from the smallest illustrated value to the box; the second the bottom half of the box; the third the top half of the box; and the fourth the top of the box to the largest value.

  • -
  • Outliers are illustrated as dots and identified by a rule: any value more the \(1.5\) IQRs from \(Q_3\) or less than \(1.5\) IQRs from \(Q_1\) are not included in the whisker, but instead are illustrated with a dot. The whisker_range argument can be set to adjust that multiplier \(1.5\).

  • -
  • The range is seen through the whiskers or identified outliers.

  • -
-

Box plots are excellent graphics for comparing several distributions, and usually appear vertically oriented in such usage, the default orientation. Here, with a single data set, we show how to use the orientation argument to specify a horizontal layout. For this boxplot, we also add in a dot plot with each dot colored by its respective quartile.

-
-
p2 = boxplot(whale; orientation=:horizontal, legend=false)
-
-Q1, Q2,Q3 = quantile(whale, (1/4, 1/2, 3/4))
-quantile_color(x) = x < Q1 ? "red" : x < Q2 ? "yellow" : x < Q3 ? "blue" : "pink"
-whale′ = jitter(sort(whale))
-dotplot!(p2, whale′, 1/2 .+ zero.(whale′), color=quantile_color.(whale′));
-
-

The right-hand graphic in Figure 1 shows the boxplot. A careful comparison shows the interpolation used to identify the quartiles. This graphic has no identified outliers. A slight skew is seen, as the right half of the figure is longer than the left half, though this may be an artifact of sampling, and not a fact about the underlying population.

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

Figure 1: The whale data summarized with a dot plot on the left and a box plot and accompanying dot plot on the right.

-
-
-

A histogram is a another graphic well suited for displaying large data sets. It presents an illustration of what values are seen and also how prevalent that size value is. It does this by selecting a number of “bins” over the range of the data and counting the number of values in that bin, similar to what happens in a stem-and-leaf plot. This number is then represented using a bar with the property that that area of the bar is proportional to the frequency of data being in that bin. This proportion may just represent the count (if all the bins have the same length), or may be scaled so the total area is \(1\) (as these graphics suggest the shape of the underlying probability density function, which has total area \(1\)). The histogram function in StatsPlots offers still more means to scale the bars; adjustments specified through the normalize argument.

-
-
p1 = histogram(whale; bins = 5, legend=false)
-
-dotplot!(p1, jitter(whale), 1/5 .+ zero.(whale), markersize=10);
-
-

This graphic is illustrated in Figure 2. To make it, we adjusted the default number of bins through the bins argument, and added a dotplot with somewhat larger dots, to illustrate the data being counted. The height of each bar should reflect the number of data points within the bin associated to the bar.

-

Histograms are great for seeing the center of the data (the balancing point of the graphic), the spread, and the shape. However, when the are many bins, the graphic gets busy. The key information being conveyed is the general shape of the top of each bar, not the vertical lines producing the bar. The stephist function avoids these lines, though the density plot is a more widely used alternative.

-

A density plot also illustrates the distribution of values in a data set along with how they cluster, however, it smooths out the binning process. Density plots are scaled to have area \(1\), as they will be seen to suggest the shape of a probability density function. In Figure 2 we create a density plot overlaid on a histogram, which is normalized to have area \(1\):

-
-
p2 = stephist(whale; bins=5, normalize=:pdf, alpha=0.75, legend=false)
-
-density!(p2, whale; linewidth=5);
-
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

Figure 2: The whale data summarized with a histogram on the left and a density plot on the right.

-
-
-

A quantile-normal plot is a graphic to assess if a data set comes from a normal distribution, a reference bell-shaped distribution. The graphic is a scatter plot of a quantile of the data set with a corresponding quantile of the reference distribution.

-
    -
  • If the data set has a roughly bell-shaped distribution with “typical” tails, then the dots will mostly fall on a straight line;

  • -
  • if the data set is skewed one edge will deviate from the line;

  • -
  • if the data set is symmetric, but has long tails these will show a deviations in both edges of the line.

  • -
-

The qqnorm function produces Figure 3:

-
-
qqnorm(whale; legend=false)
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 3: A quantile-normal plot of the whale data

-
-
-
-
-
-
-
-
    -
  1. For some cases, where show takes too much horizontal space, permutedims is used for a similar purpose, as it may elide some values.↩︎

  2. -
  3. The parentheses for constructing tuples with 2 or more elements are technically optional, a fact that is useful for bundling different outputs into one↩︎

  4. -
  5. The output of the following command is displayed as a tuple, as the commas used create the tuple, even if not enclosed in parentheses.↩︎

  6. -
  7. The var implementation is via a string macro, and is documented through @var_str.↩︎

  8. -
  9. In Julia there are options to have offsets for indexing, e.g. OffsetArrays that allow for other access patterns, such as would be useful for zero-based indexing.↩︎

  10. -
  11. This example uses the colon operator to produce a range of values between it two arguments with increment of \(1\).↩︎

  12. -
  13. Arrays can be \(n\) dimensional, so there may be \(1\) or more index to specify↩︎

  14. -
  15. Though : does, as it is a function, not a keywoard.↩︎

  16. -
  17. The : copies on the right-hand side, but does in-place assignment on the left-hand side↩︎

  18. -
  19. The DataFrames package is almost always utilized, as it provides a fundamental type to work with tabular data, the data frame. With data frames, the allowmissing! function is used, as well, for this task.↩︎

  20. -
  21. The allowmissing function creates a union type with Missing in addition to the original data type. It some printouts, a ? is appended to the type name to indicate this addition.↩︎

  22. -
  23. Also the @. macro can automatically broadcast all operations.↩︎

  24. -
  25. The colon operator a:b returns an iterator to produce “a, a+1, …, b”.↩︎

  26. -
  27. The expression s^i just calls repeat(s, i), for these types.↩︎

  28. -
  29. These are illustrated in the stemplot function defined later.↩︎

  30. -
  31. There are also Demi (3oz), short (8oz), and Trenta (31oz).↩︎

  32. -
  33. Different date formats are possible, see ?DateFormat for details.↩︎

  34. -
  35. Tuples and vectors are at times interchangeable, but tuples do not have the associated algebraic operations defined for them, as they need not be homogeneous. With data frames, they are treated quite differently.↩︎

  36. -
  37. To extend the mad function from StatsBase requires the extra step of importing that function or qualifying it with its module, as in StatsBase.mad(...) = ....↩︎

  38. -
  39. Stem-and-leaf plots are very useful for organizing data by hand, but better graphics can be used to illustrate data and its distribution on the computer.↩︎

  40. -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/Inference/distributions.html b/Inference/distributions.html deleted file mode 100644 index 551c947..0000000 --- a/Inference/distributions.html +++ /dev/null @@ -1,856 +0,0 @@ - - - - - - - - - -distributions - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

Probability distributions

-

Exploratory data analysis differs from statistics. A statistical model for a measurement involves a description of the random nature of the measurement. To describe randomness, the language of probability is used. Much of this language is implemented in the Distributions package of Julia, loaded below with other packages:

-
-
using Distributions
-using StatsBase, DataFrames
-using CairoMakie, AlgebraOfGraphics
-
-
-

Probability

-

This section quickly reviews the basic concepts of probability.

-

Mathematically a probability is an assignment of numbers to a collection of events (sets) of a probability space. These values may be understood from a model or through long term frequencies. For example, consider the tossing of a fair coin. By writing “fair” the assumption is implicitly made that each side (heads or tails) is equally likely to occur on a given toss. That is a mathematical assumption. This can be reaffirmed by tossing the coin many times and counting the frequency of a heads occuring. If the coin is fair, the expectation is that heads will occur in about half the tosses.

-

The mathematical model involves a formalism of sample spaces and events. There are some subtleties due to infinite sets, but we limit our use of events to subsets of finite or countably infinite sets or intervals of the real line. A probability measure is a function \(P\) which assigns each event \(E\) a number with:

-
    -
  • \(0 \leq P(E) \leq 1\)
  • -
  • The probability of the empty event is \(P(\emptyset) = 0\), the probability of the the sample space is \(P(\Omega) = 1\).
  • -
  • If events \(E_1, E_2, \dots\) are disjoint then the probability of their union is the sum of the individual probabilities.
  • -
-

A random variable, \(X\), is a function which takes an outcome (an element of an event) in a sample space and assigns a number. Random variables naturally generate events through sets of the type \(\{X = k\}\) for some \(k\). This event being all outcomes for which \(X\) would be \(k\). Similarly, \(\{X \leq a\}\) for some \(a\) describes an event. These are the typical events of statistics.

-

The rules of probability lead to a few well used formulas: \(P(X \leq a) = 1 - P(X > a)\) and for discrete random variables \(P(X \leq k) = \sum_{j \leq k} P(X = j)\). Both illustrated in Figure 1.

-
-
-
-
-
-

-
-
-
-
-

-
-
-
-

Figure 1: Illustrations of \(P(X \leq a)\) = \(1 - P(X > a)\) for a discrete and continuous distribution.

-
-
-

A distribution of a random variable is a description of the probabilities of events generated by a random variable. For our purposes, it is sufficient to describe events of the type \(\{X \leq a\}\), \(\{X < a\}\), or \(\{X = a\}\), others being formed through intersections and unions. A valid description of the cumulative distribution function, \(F(a) = P(X \leq a)\), (abbreviated cdf) describes the distribution of a random variable.

-

There are 2 types of distributions where a related function describes the distribution, discrete and continuous distributions.

-

A discrete random variable is one which has \(P(X = k) > 0\) for at most a finite or countably infinite set of numbers. For example, if \(X\) is the number of coin tosses producing heads in \(n\) tosses, then the finite \(k\)s are \(0,1,2, \dots, n\). Whereas, if \(X\) is the number of coin tosses needed to toss one head. The \(k\) would be \(1, 2, \dots\) (with no upper bound, as one could get extremely unlucky). For discrete random variables it is enough to describe \(f(k) = P(X=k)\) for the valid \(k\)s. The function \(f(k)\) is called the pdf (probability distribution function). An immediate consequence is \(\sum_k f(k) = 1\) and \(f(k) \geq 0\).

-

A continuous random variable is described by a function \(f(x)\) where \(P(X \leq a)\) is given by the area under \(f(x)\) between \(-\infty\) and \(a\). The function \(f(x)\) is called the pdf (probability density function). An immediate consequence is the total area under \(f(x)\) is \(1\) and \(f(x) \geq 0\).

-

When defined, the pdf is the basic description of the distribution of a random variable. It says what is possible and how likely possible things are. For the two cases above, this is done differently. In the discrete case, the possible values are all \(k\) where \(f(k) =P(X=k) > 0\), but not all values are equally likely unless \(f(k)\) is a constant. For the continuous case there are no values with \(P(X=k) > 0\), as probabilities are assigned to area, and the corresponding area to this event, for any \(k\), is \(0\). Rather, values can only appear in itervals with positive area (\(f(x) > 0\) within this interval) and for equal-length intervals, those with more area above them are more likely to contain values.

-

A data set in statistics, \(x_1, x_2, \dots, x_n\), is typically modeled by a collection of random variables, \(X_1, X_2, \dots, X_n\). That is, the random variables describe the possible values that can be collected, the values (\(x_1, x_2,\dots\)) describe the actual values that were collected. Put differently, random variables describe what can happen before a measurement, the values are the result of the measurement.

-

The joint cumulative distribution is the probability \(P(X_1 \leq a_1, X_2 \leq a_2, \dots X_n \leq a_n)\). A common assumption made for statistics is that each of the random variables is

-
    -
  • identically distributed, meaning \(F(a) = P(X_i \leq a) = P(X_j \leq a)\) for each pair \((i,j)\).
  • -
  • independent, which means that knowing the value of \(X_i\) does not effect the probabilities regarding values for \(X_j\), \(j \neq i\). (If you know a first toss of a fair coin is heads, it doesn’t change the odds that the second toss will be heads.)
  • -
-

With these two assumptions, the random variables \(X_1, X_2, \cdots, X_n\) are termed an iid random sample. For an iid random sample, this simplification applies: \(P(X_1 \leq a_1, X_2 \leq a_2, \dots X_n \leq a_n)=F(a_1)\cdot F(a_2) \cdots F(a_n)\) (independence means “multiply”).

-

In general, the distribution of a random variable may be hard to describe. For example, suppose the random variable is the sample median, \(M\), of \(X_1, X_2, \cdots, X_n\). With the assumption of an iid random sample, a formula for the distribution of \(M\) can be worked out in terms of the underlying pdf, \(f\) and cdf, \(F\). But without that assumption, it becomes intractable, in general.

-

When a distribution can not be fully identified, it can still be described. A few basic summaries of a probability distribution are:

-
    -
  • The mean or average value. For a discrete distribution this is \(\sum_k k P(X=k)\), which is a weighted average of the possible values, weighted by how likely they are. For a continuous distribution, a similar formula using calculus concepts applies. Both can be viewed from a center of mass perspective. The symbol \(\mu\) or \(E(X)\) (expectation) is used to represent the mean.1

  • -
  • The variance of a probability distribution describes the spread; the standard deviation is the square root of the variance. For a random variable, the variance is described by the average value of the centered random variable, squared: \(E( (X - \mu)^2 )\). The symbol \(\sigma^2\) is used to represent the variance, \(\sigma\) then is the standard deviation. \(VAR(X)\) is also used to represent the variance of a random variable, similarly \(SD(X)\) is used for the standard deviation.

  • -
-

The transformation \(X - \mu = X - E(X)\) centers the random variable \(X\), so that \(E(X-\mu) = 0\).

-

The transformation \(Z = (X - \mu)/\sigma\), following the \(z\)-score, centers and scales the random variable \(X\). The random variable \(Z\) has \(E(Z) = 0\) and \(VAR(Z) = 1\).

-

For a random sample \(X_1, X_2, \dots, X_n\) the sum \(S = \sum_k X_k\) has the property that \(E(S) = \sum_k E(X_k)\) (“expectations add”). This is true even if the sample is not iid. For example, finding the average number of heads in \(100\) tosses of a fair coin is easy, it being \(100 \cdot (1/2) = 50\), the \(1/2\) being the expectation of a single heads where \(X_i=1\) if heads, and \(X_i=0\) if tails.

-

While \(E(X_1 + X_2) = E(X_1) + E(X_2)\), as expectations are linear and satisfy \(E(aX+bY) = aE(X)+bE(Y)\), it is not the case that \(E(X_1 \cdot X_2) = E(X_1) \cdot E(X_2)\) in general – though it is true when the two random variables are independent. As such, the variance of \(S= \sum_k X_k\) is:

-

\[ -VAR(\sum_k X_k) = \sum_k VAR(X_k) + 2 \sum_{i < j} COV(X_i, X_j), -\]

-

where the covariance is \(COV(X_i,X_j) = E((X_i - E(X_i)) \cdot (X_j - E(X_j)))\). If, the random variables in the sample are independent, then the covariances are \(0\) and the variance of a sum is the sum of the variances.

-
-

Statistical language

-

In statistics we have seen a random sample is a sequence of random variables \(X_1, X_2, \dots, X_n\). Assume this is an iid random sample.

-

The population is the common distribution of each random variable in the random sample. Populations have a pdf and cdf, often denoted \(f_X(x)\) and \(F_X(x)\). Populations are summarized by parameters, such as the mean (\(\mu\)) or the standard deviation (\(\sigma\)).

-

A statistic is some summary of a random sample. For example, the median, or middle value, or the sample mean \(\bar{X} = (X_1 + X_2 + \cdots X_n)/n\). Statistics are also random variables and so are described by a distribution (when computable) or summarized by values such as the mean or standard deviation.

-

For the sample mean from an iid random sample, the above says:

-

\[\begin{align*} -E(\bar{X}) &= E\left(\frac{X_1 + X_2 + \cdots + X_n}{n}\right) \\ -&= \frac{1}{n} \sum_k E(X_k) \\ -&= \frac{1}{n} \sum_k \mu = \mu;\\ -VAR(\bar{X}) &= \frac{1}{n^2} \cdot \left(\sum_k VAR(X_k) + 2\sum_{i < j} COV(X_i,X_j)\right)\\ -&= \frac{1}{n^2}\sum_k VAR(X_k) \\ -&= \frac{1}{n^2} n \cdot \sigma^2 = \frac{\sigma^2}{n}. -\end{align*}\]

-

The standard deviation then is \(SD(\bar{X}) = \sigma/\sqrt{n}\).

-

In short, the expected value of the mean is the expected value of the population; the variance of the mean (of an iid random sample) is the variance of the population divided by the sample size, \(n\). The latter speaks to variability: there is more variability in a single random value than in an average of the random values. The distribution of \(\bar{X}\) can be expressed through formulas in terms of \(f_X(x)\), but is well approximated as \(n\) gets large by a distribution characterized by its mean and standard deviation, as will be seen.

-

There are parallels between random variables and data sets:

-
    -
  • The random sample \(X_1, X_2, \dots, X_n\) is realized in a data set by values \(x_1, x_2, ... x_n\).
  • -
  • The distribution, \(f_X(x)\), is reflected in the data set.
  • -
  • The distribution, \(f_X(x)\) is typically described by key parameters
  • -
  • The data set is typically summarized by sample statistics.
  • -
-

Statistical inference makes statements using the language of probability about the parameters in terms of various sample statistics.

-

An intuitive example is the tossing of a fair coin modeling heads by a \(1\) and tails by a \(0\) then we can parameterize the distribution by \(f(1) = P(X=1) = p\) and \(f(0) = P(X=0) = 1 - P(X=1) = 1-p\). This distribution is summarized by \(\mu=p\), \(\sigma = \sqrt{p(1-p)}\). A fair coin would have \(p=1/2\). A sequence of coin tosses, say H,T,T,H,H might be modeled by a sequence of iid random variables, each having this distribution. Then we might expect a few things, where \(\hat{p}\) below is the proportion of heads in the \(n\) tosses:

-
    -
  • A given data set is not random, but it may be viewed as the result of a random process and had that process been run again would likely result in a different outcome. These different outcomes may be described probabalistically in terms of a distribution.
  • -
  • If \(n\) is large enough, the sample proportion \(\hat{p}\) should be close to the population proportion \(p\).
  • -
  • Were the sampling repeated, the variation in the values of \(\hat{p}\) should be smaller for larger sample sizes, \(n\).
  • -
-

These imprecise statements can be made more precise, as will be seen. Figure Figure 2 illustrates a population, several different samples from it with the sample means visualized by a density plot. The density plot is centered at the same location as the population, but is narrower, reflecting smaller variability.

-
-
-
-
-

-

Figure 2: The top-left figure shows a population, the lower left parameters associated with the population. The top-right shows one sample from the population in red, other possible samples in blue with sample means summarized in the bottom most row. A density estimate of these sample means appears in the lower right figure showing how it is centered on the population, but more concentrated. Inference uses the language of probability to characterize a population parameter based on a single random sample.

-
-
-
-
-

Inferential statistics is similar: A population is a distribution summarized by parameters. A random sample drawn from a population is summarized by statistics. A statistic summarizes just one sample, but the language of probability is used to infer from that one sample, statements about the parameters which would be apparent were there many different random samples.

-
-
-
-

The Distributions package

-

While populations are in general described by a cdf, populations which are discrete or continuous are more naturally described by their pdf. There are many standard pdfs used to describe different types of randomness and supported in the Distributions package. This section reviews several.

-
-
using Distributions
-
-
-

Bernoulli, Binomial, Geometric

-

The simplest non-trivial distribution is that used to model a coin toss: \(P(X=1)=p\) and \(P(X=0) = 1-p\). We see this is parameterized by \(p\), the probability of heads (say). This also happens to be the mean of this distribution. By the rules of probability, we must have \(0 \leq p \leq 1\).

-

In the Distributions package, named distributions are given a type, in this case Bernoulli which requires a value of \(p\) to be formed. The following code uses the type and confirms the mean of this distribution is indeed \(p\):

-
-
p = 0.25
-Be = Bernoulli(p)
-mean(Be)
-
-
0.25
-
-
-

The Bernoulli(p) distribution is very simple, but combinations of \(Bernoulli(p)\) random variables give rise to many interesting distributions. For example, the number of heads in 10 coin tosses could be modeled by the sum of 10 Bernoulli random variables. The distribution of this random variable is described by the Binomial distribution. It has two parameters: \(p\) for the Bernoulli \(10\) for the number of tosses and is implemented with the Binomial(n,p) type. There are several summary statistics for the types supported by Distributions, including: mean, median, std:

-
-
n, p = 10, 1/2
-B = Binomial(n, p)
-mean(B),  median(B), std(B)
-
-
(5.0, 5, 1.5811388300841898)
-
-
-

The mean and standard deviation for the Binomial can be easily computed when this statistic is viewed as a sample mean of 10 Bernoulli random variables (\(np\) and \(\sqrt{np(1-p)}\)).

-

More is known. For example, the value of this random variable can only be between 0 and 10. The extrema function combines both the minimum and maximum functions to give this:

-
-
extrema(B)
-
-
(0, 10)
-
-
-

The insupport function is similar, returning true or false if a value, x, is in the support of the distribution, that is if x is a possible value:

-
-
insupport(B, 5), insupport(B, 15), insupport(B, 5.5)
-
-
(true, false, false)
-
-
-

We see only 5 is a possible value. Its probability, \(P(X=5)\) for this discrete distribution, is computed by pdf:

-
-
pdf(B, 5)
-
-
0.24609375000000022
-
-
-

Figure 3 shows different examples of the binomial distribution. When both \(np\) and \(n(1-p)\) are \(5\) or more, the shape is roughly symmetric.

-
-
-
-
-
-

-
-
-
-
-

-
-
-
-

Figure 3: The binomial distribution for different \(n\) and \(p\) values. The left graphic fixes \(n=20\) and shows different \(p\) values. One is more “bell-shaped” as both \(np\) and \(n(1-p)\) are bigger than \(5\). The right graphic shows a fixed \(p\) and different \(n\)s. As \(n\) gets bigger, the shape is more bell shaped.

-
-
-

The cumulative distribution function is computed by cdf with the same calling style, cdf(D, x). The complementary cumulative distribution function, 1 - F(x), is computed by ccdf(D, x).

-

The cumulative distribution, \(F(a)\), finds the value \(p\) for a given value of \(a\) in \(p = P(X \leq a)\). It’s inverse operation, finding a value \(a\) in \(p=P(X \leq a)\) for a given \(p\), is returned by quantile.2 Quantiles are computed with the calling style quantile(D, p).

-

A related question is the number of coin tosses needed to get the first heads. If \(X\) is this distribution, the the event \(\{X > k\}\) can be described as the first \(k\) tosses were tails. This probability is directly computable. The distribution of this number depends only on the parameter \(p\) and is called the Geometric distribution.

-

For example, we can see here the mean, and standard deviation, and also that the the number is unbounded:

-
-
p = 1/2
-G = Geometric(p)
-mean(G), std(G), extrema(G) # 1/p, 1/√p, 0,1,...,∞
-
-
(1.0, 1.4142135623730951, (0, Inf))
-
-
-
-
-

Uniform and DiscreteNonParametric distributions

-

A discrete uniform random variable on \(a, a+1, \dots, b\) assigns equal weight to each value. Hence, each possible value is equally likely. The DiscreteUniform(a,b) type models this distribution. For example, to model the roll of a 6-sided die, we might have:

-
-
D = DiscreteUniform(1,6)
-mean(D), std(D)
-
-
(3.5, 1.707825127659933)
-
-
-

More generally, a distribution which assigns weight \(p_k\) to values \(x_1, x_2, \dots, x_n\) is modeled by the DiscreteNonParametric(xs, ps) distribution. For example, Benford’s “law” is an observation that the first non-zero digit of many data sets follows a certain pattern. For this the possible values are \(1\) through \(9\) and their probabilities are \(f(k) = P(X=k) = \log_{10}(k+1) - \log_{10}(k)\). We can model this with:

-
-
xs = 1:9
-ps = log10.(xs .+ 1) - log10.(xs)
-B = DiscreteNonParametric(xs, ps)
-
-
DiscreteNonParametric{Int64, Float64, UnitRange{Int64}, Vector{Float64}}(
-support: 1:9
-p: [0.3010299956639812, 0.17609125905568124, 0.12493873660829996, 0.09691001300805646, 0.07918124604762478, 0.06694678963061318, 0.057991946977686726, 0.05115252244738133, 0.04575749056067513]
-)
-
-
-

We can answer questions like the mean, the standard deviation, and what is the probability the number is 5 or less with:

-
-
mean(B), std(B), cdf(B, 5)
-
-
(3.4402369671232065, 2.4609982997506656, 0.7781512503836436)
-
-
-
-
a = 5
-
-ks = support(B)
-ps = pdf.(B, ks)
-as = ifelse.(ks .<= a, "X ≤ $a", "X > $a")
-
-layers = visual(Rangebars) + visual(Scatter)
-p = data((; ks, ps, as)) * layers * mapping(:ks, :ps, :ps => zero, color=:as)
-
-draw(p)
-
-
-
-

-

Figure 4: Benford’s law illustrated with values \(5\) or less illustrated

-
-
-
-
-

In Distributions the Categorical type can alse have been used to construct this distribution, it being a special case of DiscreteNonParametric with the xs being \(1, \dots, k\).

-

The multinomial distribution is the distribution of counts for a sequence of \(n\) iid random variables from a Categorical distribution. This generalizes the binomial distribution. Let \(X_i\) be the number of type \(i\) in \(n\) samples. Then \(X_1 + X_2 + \cdots + X_k = n\), so these are not independent. They have mean \(E(X_i)=np_i\), variance \(VAR(X_i) = np_i (1-p_i)\), like the binomial, but covariance \(COV(X_i, X_j) = -np_i p_j, i \neq j\). (Negative, as large values for \(X_i\) correlate with smaller values for \(X_j\) when \(i \neq j\).)

-
-
-

The continuous uniform distribution

-

The continuous uniform distribution models equally likely outcomes over an interval \([a,b]\). (The endpoints possibly open, as mathematically they have no chance of being selected.) For example, the built-in rand function for Float64 values returns random numbers which are modeled by being uniform over \([0,1)\). (That they can be 0 but not 1 is a reality of how computers and mathematical models aren’t always exactly the same, but rather the model is an abstraction of the implementation.)

-

The Uniform(a,b) type models these numbers. Here we see rand being used to randomly sample \(3\) uniform numbers using a more general interface than the use of rand in base Julia:

-
-
U = Uniform(0, 1)
-rand(U, 3)
-
-
3-element Vector{Float64}:
- 0.07840854312239087
- 0.49280532827183554
- 0.5805794615043689
-
-
-

If \(U\) is Uniform on \([0,1]\) then \(Y = aU + b\) is uniform on \([b, b+a]\). The difference in means is shifted by \(b + E(U)\), the difference in standard deviations is scaled by \(a\). We can do the algebra of linear transformations using overloaded operations:

-
-
a, b = 2, 3
-Y = a * U + b
-q1 = mean(U) == mean(Y) - (b + mean(U)) == (1 + 0)/2
-q2 = std(U) == std(Y)/a  == sqrt(1/12)
-q1, q2
-
-
(true, true)
-
-
-
-
-

The normal distribution

-

The most important distribution in statistics is called the normal distribution. This is a bell-shaped distribution completely described by its mean and standard deviation, Normal(mu, sigma) (though some math books use the variance, \(\sigma^2\) for the second parameter). The standard normal has \(\mu=0\) and \(\sigma=1\). Standard normal random variables are generically denoted by \(Z\):

-
-
Z = Normal(0, 1)
-mean(Z), std(Z)
-
-
(0.0, 1.0)
-
-
-

There are many facts about standard normals that are useful to know. First we have the three rules of thumb – \(68\), \(95\), \(99.7\) – describing the amount of area above \([-1,1]\), \([-2,2]\), and \([-3,3]\). We can see these from the cdf with:

-
-
between(Z, a, b) = cdf(Z,b) - cdf(Z,a)
-between(Z, -1, 1), between(Z, -2, 2), between(Z, -3, 3)
-
-
(0.6826894921370861, 0.9544997361036416, 0.9973002039367398)
-
-
-

These values are important as the \(z\) scores of different data sets are often assumed to be normal or approximately normal, so, for example, about 95% of such a data set should have \(z\) scores within \(-2\) and \(2\).

-

The transformation \(Y = \mu + \sigma \cdot Z\) will produce a normal with mean \(\mu\) and standard deviation \(\sigma\), modeled more directly by Normal(mu, sigma).

-

A common question in introductory statistics is what values \(a\) and \(b\) correspond to \((1-\alpha)\cdot 100\)% of the area under the pdf of \(Y\)? There are infinitely many such answers, but only one pair that is symmetric about the mean, \(\mu\). This can be found with the help of quantile and the observation that since the pdf of \(Y\) is symmetric, there will be area \(1 - \alpha\) plus \(\alpha/2\) to the left of \(b\), a question tailor-made for quantile. From \(b\), \(a\) can be found from symmetry, it being equidistant from the mean as \(b\) is.

-

For example:

-
-
alpha = 0.05
-mu, sigma = 10, 20
-Y = Normal(mu, sigma)
-b = quantile(Y, 1 - alpha + alpha/2)
-a = mu - (b - mu)
-a, b, between(Y, a, b)
-
-
(-29.19927969080115, 49.19927969080115, 0.9500000000000004)
-
-
-

An observation of De Moivre in 1733 about the binomial proved foundational. Suppose we consider the binomial distribution with \(n\) and \(p\) such that \(np\) and \(n\cdot(1-p)\) are both \(10\) or more. Then \(\mu = np\) and \(\sigma = \sqrt{np(1-p)}\). The cdf of this distribution was observed to be very well approximated by the cdf of the \(Normal(\mu, \sigma)\) distribution. Furthermore, asymptotically they are equal.

-

For example, the following searches for the largest discrepancy in the cdfs:

-
-
n, p = 100, 1/3
-mu, sigma = n*p, sqrt(n*p*(1-p))
-B, Y = Binomial(n,p), Normal(mu, sigma)
-ks = 0:100
-findmax(abs(cdf(B, k) - cdf(Y, k)) for k in ks)
-
-
(0.046989293624614625, 34)
-
-
-

This shows an error of not more than \(0.0469...\) in approximating the binomial with the normal. The continuity correction adjusts for the discrete nature of the binomial by comparing \(k\) to \(k+1/2\) for the normal. The following is more accurate, as can be seen:

-
-
findmax(abs(cdf(B, k) - cdf(Y, k+1/2)) for k in ks)
-
-
(0.004701503073942237, 34)
-
-
-

Of course, computationally it is often just as easy to call cdf(B, k) as it is to call cdf(Y, k + 1/2), so the advantage here is theoretical for computationally tractable values of \(n\) and \(k\). This particular approximation is generalized in the central limit theorem.

-
-
-

The Chi-squared distribution

-

Let \(Z\) be a standard normal random variable. As seen, a linear transform of \(Z\): \(Y=\sigma Z + \mu\) will have distribution of \(Normal(\mu,\sigma)\), a different transform \(Y=Z^2\) can have its distribution computed using some tricks, e.g. (\(P(Z^2 < a) = P(-\sqrt{a} < Z < \sqrt{a})\).) If we have an iid random sample, \(Z_1, Z_2, \dots, Z_n\) the distribution of \(\chi^2 = Z_1^2 + Z_2^2 + \cdots + Z_n^2\) is of interest. (It may be viewed as the distance squared of a randomly chosen point in space, say.) The distribution of \(\chi^2\) is the Chi-squared distribution with \(n\) degrees of freedom and is implemented in the Chisq(n) type.

-
-
-

The T and F distributions

-

There are two main distributions which arise in the distribution of many sample statistics related to the linear regression model, these are the \(T\)-distribution of Student and the \(F\)-distribution of Fischer. Both are parameterized by “degrees of freedom,” which are more naturally discussed with sampling distributions.

-

Consider two independent random variables, a standard normal \(Z\) and a Chi-squared random variable, \(Y\), with \(\nu\) degrees of freedom. The \(T\)-distribution with \(\nu\) degrees of freedom is the distribution of the ratio:

-

\[ -T = \frac{Z}{\sqrt{Y/\nu}}. -\]

-

The \(T\)-distribution is a symmetric, bell-shaped distribution like the normal distribution, but has wider, fatter tails hence typically produces larger values in a sample. This can be seen heuristically with a relatively small degrees of freedom as follows:

-
-
T5 = TDist(5)
-maximum(abs, rand(T5, 100)), maximum(abs, rand(Z, 100))
-
-
(3.6621884154534414, 2.4194746510021092)
-
-
-

Figure 5 shows a quantile-normal plot of \(T(3)\) in the lower-left graphic. The long tails cause deviations in the pattern of points from a straight line.

-

The skewness method measures asymmetry. For both the \(T\) and the normal distributions – both bell shaped – this is \(0\). The kurtosis function measures excess kurtosis a measure of the size of the tails as compared to the normal. We can see:

-
-
skewness(T5), skewness(Z), kurtosis(T5), kurtosis(Z)
-
-
(0.0, 0.0, 6.0, 0.0)
-
-
-

A distribution with excess kurtosis exceeding the normal is called leptokurtic. Such distributions more frequently produce values with \(z\) scores larger than expected by the standard normal distribution. A platykurtic distribution, on the other hand, would have in more frequently fewer values in the tails and fewer values close to the mean of a standard normal. The uniform distribution is a good example of a playtkurtic distribution, both conditions have a test in Distributions:

-
-
isleptokurtic(T5), isplatykurtic(U)
-
-
(true, true)
-
-
-

For the \(T\)-distribution, when the degrees of freedom gets large, say bigger than \(100\), the distribution is well approximated by the normal. In the following code, we see a similar comparison as above to compare the difference. For a quantile-normal plot see the upper-right graphic in Figure 5.

-
-
T100 = TDist(100)
-xs = range(-3, 3, length=100)
-findmax(abs(cdf(T100, x) - cdf(Z, x)) for x in xs)
-
-
(0.0015794482887208222, 25)
-
-
-

In Figure 5 the lower-right graphic is of the uniform distribution, the short tails, lead to a deviation from a straight line in this graphic; the lower-right graphic is of the skewed exponential distribution; the skew shows up in the lack of symmetry about the fitted line.

-
-
-
-
-

-

Figure 5: Quantile-normal plots for different distributions – \(T_3\) is leptokurtic; \(T_{100}\) is approximately normal; \(U\) is platykurtic; and \(E\) is skewed.

-
-
-
-
-

Consider now two, independent Chi-squared random variables \(Y_1\) and \(Y_2\) with \(\nu_1\) and \(\nu_2\) degrees of freedom. The ratio

-

\[ -F = \frac{Y_1/\nu_1}{Y_2/\nu_2}, -\]

-

has a known distribution called the \(F\)-distribution with \(\nu_1\) and \(\nu_2\) degrees of freedom.

-

This distribution has different shapes for different parameter values (Figure 6), with the distributions becoming more normal as the two parameters get large.

-
-
kurtosis(FDist(5, 10)), kurtosis(FDist(5,100)), kurtosis(FDist(100,100))
-
-
(50.86153846153846, 3.1474470779483217, 0.7278883188966445)
-
-
-
-
-
-
-

-

Figure 6: Quantile-normal plots of the \(F\) distribution for different degrees of freedom. As the degrees of freedom values get bigger, the distribution is more normal.

-
-
-
-
-
-
-
-

Sampling distributions

-

The normal distribution, \(T\) distribution, and \(F\) distribution are important in statistics as they arise as sampling distributions of certain statistics.

-
-

The sample mean

-

For an iid random sample, from a population with mean \(\mu\) and standard deviation \(\sigma\) we have seen that the sample mean, \(\bar{X} = (X_1 + \dots + X_n)/n\) is described by having mean \(\mu_{\bar{X}} = \mu\) and standard deviation \(\sigma_{\bar{X}} = \sigma / \sqrt{n}\).

-

What about the distribution itself? This depends on the underlying population.

-

If the underlying population is normal, then \(\bar{X}\) is normal. This is true as the sum of two independent normal random variables is normally distributed. That is \(\bar{X}\) is bell shaped, centered at the same place as the distribution of any of the \(X_i\)s but has a narrower shape, as the standard deviation is smaller by a factor of \(1/\sqrt{n}\).

-
-

Central limit theorem

-

De Moivre’s observation that the binomial distribution is asymptotically normal is even more general: the central limit theorem informs us that for large enough \(n\) the following is true under assumptions::

-

\[ -P(\frac{\bar{X} - \mu}{\sigma/\sqrt{n}} \leq z) \approx P(Z \leq z), -\]

-

where \(Z\) is a standard normal random variable. That is centering \(\bar{X}\) by its mean and then scaling by its standard deviation results in a random variable that is approximately a standard normal if \(n\) is large enough.

-

The central limit theorem has some assumptions on the underlying population. The assumption that a mean and standard deviation exist are sufficient for application. It is true exactly for normal populations, for non-normal populations asymptotically so. For a binomial population, a rule of thumb is \(np\) and \(n(1-p)\) should be larger than \(5\) (\(10\) to be conservative), which avoids a skewed population.

-

In general, how large \(n\) needs to be depends on the underlying shape of the population: if the population has long tails (leptokurtic) or is very skewed, then \(n\) must be much larger than if the population is symmetric and platykurtic.

-

Figure 7 shows simulations of the distribution of \(\bar{X}\) for \(n=10\) for different populations. The key to simulations is the ability to draw random samples from the distribution, which is provided through the rand method for a distribution type, with rand(D,n) returning a random sample of size \(n\).

-
-
xbar(D, n, N=200) = [mean(rand(D, n)) for _ in 1:N]
-sxbar(D, n, N=200) = (xbar(D,n,N) .- mean(D)) / (std(D)/sqrt(n))
-
-Ds = (normal      = Normal(0,1),
-      leptokurtic = TDist(3),
-      skewed      = FDist(2,5),
-      platykurtic = Uniform(0,1))
-df = DataFrame(D=collect(keys(Ds)), zs = [sxbar(D, 10) for D in values(Ds)])
-
-d = data(flatten(df, :zs))
-p = d * visual(QQNorm, qqline=:fit) *
-    mapping(:zs, layout = :D, main=:D, color=:D)
-
-draw(p)
-
-
-
-

-

Figure 7: Quantile-normal plots of different random samples of \(\bar{X}\) for \(n=10\). The underlying popluation is either normal, short tailed (playkurtic), long tailed (leptokurtic), or skewed. For the normal and short tailed populations the distribution of \(\bar{X}\) is approximately normal for this small value of \(n\); for the other two populations, larger samples sizes are needed to see the aymptotic normality guaranteed by the central limit theorem.

-
-
-
-
-
-
-

The \(T\)-statistic

-

The central limit theorem requires standardizing by the population standard deviation. At times this is neither known or assumed to be known. However, the standard error may be known:

-
-

The standard error of a statistic is the standard deviation of the statistic or an estimate of the standard deviation.

-
-

For \(\bar{X}\), the standard deviation is \(\sigma/\sqrt{n}\) the standard error is \(SD(\bar{X})/\sqrt{n}\), where, for a sample, the sample standard deviation, \(s\), is used to estimate the population standard deviation, \(\sigma\).

-

The scaling in the central limit theorem can be redone using the standard error, \(S/\sqrt{n}\):

-

\[ -T = \frac{\bar{X} - \mu}{SE(\bar{X})} = \frac{\bar{X} - \mu}{S / \sqrt{n}}, -\]

-

where \(S\) is the sample standard deviation of the data set \(X_1, X_2, \dots, X_n\) :

-

\[ -S = \sqrt{ \frac{1}{n-1} \sum_i^n (X_i - \bar{X_i})^2}. -\]

-

The variability in the sampling means that the standard error will occasionally be much smaller than the population standard deviation, as this is in the denominator, the random variable \(T\) will have longer tails than a standard normal. That is, the sampling distribution of \(T\) should be leptokurtic, and it is:

-
-

For a normal population, the sampling distribution of \(T\) is the \(T\)-distribution with \(n-1\) degrees of freedom.

-
-

In general, a \(T\)-statistic for a parameter, \(\beta\), is the difference between the estimate for the parameter (\(\hat{\beta}\)) and the parameter, divided by the standard error of the estimator: \(T = (\hat{\beta}-\beta)/SE(\hat{\beta})\). This is of the pattern “(observed - expected) / SE.”

-
-
-
-

The sample variance

-

For an iid random sample from a normal distribution the distribution of the sample variance \(S^2\) can be identified. Consider this algebraic treatment,

-

\[\begin{align*} -S^2 &= \frac{1}{n-1} \sum_i^n (X_i - E(X_i))^2\\ -&= \frac{\sigma^2}{n-1} \sum_i^n \left( \frac{X_i - \bar{X_i}}{\sigma} \right)^2 -\end{align*}\]

-

If the sample is an iid random sample of normals and were \(\bar{X_i}\) replaced by \(\mu=E(X_i)\), the sum would be of \(n\) iid squared standard normal which would be \(Chisq(n)\) distributed. However, that isn’t quite the case. Cochran’s theorem shows for an iid random sample from a normal population the distribution of \(\sum (Z_i - \bar{Z})^2\) is \(Chisq(n-1)\) and \(\bar{X}\) and \(S^2\) are independent, which leads to:

-
-

For an iid random sample from a normal population \((n-1) S^2/\sigma^2\) has a \(Chisq(n-1)\) distribution.

-
-
-

The \(F\) statistic

-

When there are two independent samples, there are two sample means and standard deviations. The ratio of the sample standard deviations gives insight into the question of whether the two populations have the same spread. The \(F\)-statistic is given by:

-

\[ -F = \frac{(S_1^2/\sigma_1^2)}{(S_2^2/\sigma_2^2)} = \frac{(\chi_{1,n_1-1}^2 / (n_1-1))}{(\chi_{2,n_2-1}^2/(n_2-1))}. -\]

-

If the two populations are normal with means \(\mu_i\) and standard deviations \(\sigma_i\), then the distribution of the \(F\) statistic is the \(F\) distribution with \(\nu_1=n_1-1\) and \(\nu_2=n_2-1\) degrees of freedom.

-

A special case is the \(T\) statistic for a normal population which has a \(F(1, n-1)\) distribution owing to:

-

\[\begin{align*} -T^2 &= (\frac{\bar{X} - \mu}{S/\sqrt{n}})^2\\ -&= \frac{(\bar{X}-\mu)^2/\sigma^2}{S^2/(n\sigma^2)}\\ -&= \frac{(\bar{X}-\mu)^2/(\sigma^2/n)}{S^2/\sigma^2}\\ -&= \frac{Z^2/1}{\chi^2_{n-1}/(n-1)}\\ -&\sim F(1, n-1). -\end{align*}\]

-

That \(\bar{X}\) and \(S^2\) are independent under these assumptions is necessary to identify the distribution.

-
-

The distribution of the \(T\) and \(F\) statistics for an iid random sample is known under the assumption of a normal population. What if the population is not normal, how robust is the distribution of these statistics to differences in the population? This was studied in (BOX 1953). Here we show some examples through simulation.

-

In the following we use a long-tailed population (\(T_3\)) and a skewed exponential population to look:

-
-
T_n(D, n) = (xs = rand(D,n); (mean(xs) - mean(D)) / (std(xs) / sqrt(n)))
-F_n(D, n1, n2) = (xs = rand(D,n1); ys = rand(D, n2); (std(xs)/(n1-1)) / (std(ys)/(n2-1)))
-
-n = 10
-n1, n2 = 10, 5
-N = 100
-
-
-ps = range(0.01, 0.99, length=N)
-SDs = (T=TDist(n-1), F=FDist(n1-1, n2-1))
-Pops = (var"long-tailed"=TDist(3), skewed=Exponential(1))
-
-# use scatter to produce qqplot
-df = DataFrame([(D="$SDk $Popk", x=quantile(Popv, ps), y = quantile(rand(SDv, N), ps))
-                for (SDk,SDv) in pairs(SDs) for (Popk, Popv) in pairs(Pops)])
-
-p = data(flatten(df, [:x,:y])) * (visual(Scatter) + linear(;interval=nothing)) *
-    mapping(:x, :y, main=:D, layout=:D, color=:D)
-
-draw(p, facet=(; linkxaxes=:none, linkyaxes=:none))
-
-
-
-

-

Figure 8: Small sample simulations of \(T\) and \(F\) statistic for non-normal populations. None are well described by the \(T\) or \(F\) distributions.

-
-
-
-
-
-
-
-

The sample median

-

The sample median is the “middle” of an iid random sample. Like the sample mean, it is a measure of center, that, unlike the sample mean, is resistant to outliers. The central limit theorem instructs us that for most populations the distribution of the sample mean is normally distributed. The distribution of the sample median can be computed. It is asymptotically normal with mean \(\mu\) and variance given by \((4nf(m)^2)^{-1}\), where \(n\) is the sample size, \(m\) the median of the population, and \(f\) the pdf of the population.

-

Figure 9 shows through boxplots the variability of the sample median and sample mean depends on the population. For the standard normal population the ratio of the variances of \(\bar{X}\) and \(M\) tends to \(2/\pi\). This is seen in the smaller IQR for \(\bar{X}\) compared to \(M\) in the figure for the normal population. This ratio isn’t always smaller than 1 for other populations.

-
-
-
-
-

-

Figure 9: Comparison of spread of the sample median versus the sample mean for various populations. For skewed and long-tailed distributions, the sample median has smaller variability.

-
-
-
-
-
-
-

The Chi-squared statistic

-

For the multinomial distribution for a fixed \(n\) with probabilities \(p_1, p_2, \dots, p_k\) and counts \(X_1, X_2, \dots, X_k\), the expected values are \(E_i = E(X_i) = n p_i\). The difference between the observed, \(X_i\), and the expected, \(E_i\), are often summarized in this statistic:

-

\[ -\chi^2 = \sum_k \frac{(X_i - E_i)^2}{E_i}. -\]

-

For this scenario, the asymptotic distribution of \(\chi^2\) is the Chi-squared distribution with \(k-1\) degrees of freedom. This number comes from the one relationship amongst the \(k\) variables (that they all add up to \(1\).) That is, \(k-1\) \(p_i\)s are freely chosen, the other a dependency.

-

Suppose there are \(s\) values, \(\theta = (\theta_1, \theta_2, \dots, \theta_s)\) with some identification \(f(\theta) = (p_1, p_2, \dots, p_k)\). If these \(s\) values are estimated from the data (using a best asymptotically normal estimator), then there are \(k - s - 1\) degrees of freedom in the asymptotic Chi-squared distribution.

-

The Chi-squared sampling distribution is asymptotic meaning for \(n\) large enough. A rule of thumb is each count should be expected to be \(5\) or more.

-

A related statistic is the Cressie-Read power-divergence statistic, (Cressie and Read 1984), parameterized by real \(\lambda\) and given by:

-

\[ -2nI^\lambda = \frac{2}{\lambda(\lambda +1)} \sum_{i=1}^k X_i \left( \left(\frac{X_i}{E_i}\right)^\lambda - 1)\right). -\]

-

When \(\lambda = 1\), after some algebraic manipulations, this can be identified with the \(\chi^2\) statistic above. Other values for \(\lambda\) recover other named statistics, for example \(\lambda=0\), for which this computes the maximum-likelihood estimate from the data. The Cressie-Read statistic also has an asymptotic \(Chisq(k-s-1)\) distribution for a fixed \(\lambda\).

-
-
-BOX, G. E. P. (1953), NON-NORMALITY AND TESTS ON VARIANCES,” Biometrika, 40, 318–335. https://doi.org/10.1093/biomet/40.3-4.318. -
-
-Cressie, N., and Read, T. R. C. (1984), Multinomial goodness-of-fit tests,” Journal of the Royal Statistical Society. Series B (Methodological), [Royal Statistical Society, Wiley], 46, 440–464. -
-
-
-
-
-
-
-
    -
  1. Notationally, \(E(X)\) is used for the mean of a random variable, \(\mu\) is used for the mean of a probability distribution, though we use them interchangeably.↩︎

  2. -
  3. This definition of a quantile is for a continuous distribution, when the cumulative distribution is not monotonic, then definition may be the infimum over all \(a\) for which \(p \leq P(X \leq a)\).↩︎

  4. -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/Inference/inference.html b/Inference/inference.html deleted file mode 100644 index 233ff5e..0000000 --- a/Inference/inference.html +++ /dev/null @@ -1,1626 +0,0 @@ - - - - - - - - - -inference - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

Inference

-

A goal of statistics is to use data to characterize the process that generated the data.

-

To discuss, we first load some useful packages:

-
-
using StatsBase, Distributions
-using DataFrames
-using AlgebraOfGraphics, CairoMakie
-
-
-

Larger data sets

-

Suppose \(x_1, x_2, \dots, x_n\) is sample. If we assume these are realizations from some iid random sample \(X_1, X_2, \dots, X_n\) from some population then when \(n\) is large we expect the shape of the population to be well described.

-

The density plots in Figure 1 show a population, a random sample of a certain size from that population, and an density plot found from the random sample. As the sample size gets larger, the density plot resembles more the underlying population.

-
-
-
-
-
-

-
-
-
-
-

-
-
-
-
-
-
-

-
-
-
-
-

-
-
-
-

Figure 1: A population pdf drawn in red, a sample of size n marked with dots, and a sample density. As n increases, the sample density is an improved estimate for the population pdf.

-
-
-

To quantify this, we use the empirical cdf defined as

-

\[ -F_n(a) = \frac{\#\{i: x_i \leq a\}}{n}. -\]

-

That is, the proportion of the sample data less than or equal to \(a\). This estimates the cdf of the population. The ecdf function from StatsBase returns a function. For example, we have for this population given as a mixture of normal distributions:

-
-
Y = MixtureModel(Normal[
-    Normal(0, 1),
-    Normal(1, 2),
-    Normal(2.0, 3)], [0.5, 0.2, 0.3])
-
-xs = rand(Y, 1000)
-Fₙ = ecdf(xs)
-
-findmax(abs(cdf(Y,x) - Fₙ(x)) for x in range(-5, 5, 1000))
-
-
(0.02180623231041745, 495)
-
-
-

The maximum distance between cumulative distributions functions is a useful statistic in itself, which we don’t pursue. Rather, we show in Figure 2 the empirical cdf and the theoretical cdf for this simulation.

-
-
Fₙ = ecdf(rand(Y, 1000))
-xs = range(-3, 6, 1000)
-d = data((x=xs, y=cdf.(Y,xs), y′=Fₙ.(xs)))
-p = d * visual(Lines; color=:black) * mapping(:x, :y)
-p = p + d * visual(Lines; color=:red) * mapping(:x, :y′)
-draw(p)
-
-
-
-

-

Figure 2: A cumulative distribution of a mixture model with an empirical cdf generated by a random sample of size 1000.

-
-
-
-
-
-
-

Confidence intervals

-

When there is a much smaller sample size, one can still infer things about the underlying population if additional assumptions on the population are made. In general, the stronger the assumptions, the more that can be said.

-
-

Confidence interval for a population mean

-

For example, suppose you have a data set with \(n\) elements, \(x_1, x_2, \dots, x_n\), and you assume that:

-
    -
  • the data can be modeled as realizations of some iid collection of random variables, \(X_1, X_2, \dots, X_n\) and

  • -
  • the population of the random variables is \(Normal(\mu, \sigma)\).

  • -
-

With these assumptions the basic facts of probability allow statements about \(\mu\) based on the data set.

-

We know the statistic:

-

\[ -Z = \frac{\bar{X} - \mu}{\sigma/\sqrt{n}} = \frac{\bar{X} - \mu}{SD(\bar{X})} -\]

-

has a \(Normal(0,1)\) distribution. For any \(\alpha\) with \(0 < \alpha < 1/2\), we can solve for values \(z_{\alpha/2}\) and \(z_{1-\alpha/2}\) satisfying:

-

\[ -P(z_{\alpha/2} < Z < z_{1 - \alpha/2}) = 1 - \alpha. -\]

-

(That is between the \(z\) values lies \((1-\alpha)\cdot 100\)% of the area under the pdf for \(Normal(0,1)\).)

-

By the symmetry of the normal distribution, we have \(z_{\alpha/2} = - z_{1-\alpha/2}\).

-

Rearranging this, we have with probability \(1 - \alpha\) the following inequality occurs:

-

\[ -\bar{X} - z_{1-\alpha/2}\cdot SD(\bar{X}) < \mu < \bar{X} + z_{1-\alpha/2}\cdot SD(\bar{X}) -\]

-

Now, the data set is just one possible realization of these random variables, which may or may not be unusual, we can’t say. However, we can say the process that produced this data set will produce values where \((1-\alpha)\cdot 100\)% of the time

-

\[ -\bar{x} - z_{1-\alpha/2}\cdot SD(\bar{x}) < \mu < \bar{x} + z_{1-\alpha/2}\cdot SD(\bar{x}). -\]

-

Since a data set is a single realization, and probability speaks to the frequency of many realizations, we can’t say for our lone data set that there is a \((1-\alpha)\cdot 100\)% chance this occurs, rather the language adopted is to say the interval \((\bar{x} - z_{1-\alpha/2}\cdot \sigma/\sqrt{n}, \bar{x} + z_{1-\alpha/2}\cdot \sigma/\sqrt{n})\) is a \((1-\alpha)\cdot 100\)% confidence interval for an unknown parameter \(\mu\).

-
-

For a data set drawn from iid random sample with a \(Normal(\mu,\sigma)\) population a \((1-\alpha)\cdot 100\)% confidence interval is given by \(\bar{x} \pm z_{1-\alpha/2}\cdot \sigma/\sqrt{n}\), where \(z_{1-\alpha/2}=-z_{\alpha/2}\) satisfies \(P(z_{\alpha/2} < Z < z_{1-\alpha/2})\), \(Z\) being a standard normal random variable.

-
-

Figure 3 illustrates confidence intervals based on several independent random samples. Occasionally – with a probability controlled by \(\alpha\) – the intervals do not cover the true population mean.

-
-
-
-
-

-

Figure 3: 50 simulated confidence intervals. The true mean is \(\mu=0\), the confidence level is \(0.90\). On average, 10 out of 100 of these confidence intervals will not cover the mean. For this simulation, we expect 5 or so to miss, but the exact count may differ, as each is random.

-
-
-
-
-
-

Example 1 (Confidence interval for the mean) Consider the task of the beverage dispenser service person. While they may enjoy great benefits and the opportunity to work out of an office, do they get a chance to practice statistics? Well, possibly. Consider the task of calibrating an automatic coffee dispenser. Suppose the engineers have managed to control the variance of the pour so that \(\sigma = 0.5\) oz. The \(8\) oz. cups should not overflow, but should look full when done. As such, the technician aims for a mean dispense of around \(7\) oz, but not including \(7.5\). To gauge this, they run the machine \(6\) times and collect data using a calibrated glass. Assume this data set is from a \(N(\mu,\sigma=1/2)\) population:

-
7.9  7.2  7.1  7.0  7.0  7.1
-

A \(95\)% confidence interval for \(\mu\) is given by:

-
-
xs = [7.9,  7.2,  7.1,  7.0,  7.0,  7.1]
-n = length(xs)
-σ = 1/2
-α = 0.05
-za = quantile(Normal(0, 1), 1 - α +/2))
-SE = σ / sqrt(n)
-(mean(xs) - za*SE, mean(xs) + za*SE)
-
-
(6.816590693637058, 7.616742639696278)
-
-
-

That \(7.5\) is included may be problematic, as larger pours than \(8\) oz are possible with these assumptions, so the technician calibrates the machine a bit less aggressively.

-
-

The assumption of a normal population is used to say the distribution of \(\bar{X}\) is normal. This would be true if the population weren’t normal but the sample size, \(n\), were sufficiently large. The important part is having assumptions that allows the sampling distribution of a useful statistic to be known.

-
-

The above assumes an unknown mean (\(\mu\)) but a known standard deviation. If that assumption isn’t realistic, something similar can be said. Consider the \(T\)-statistic:

-

\[ -T = \frac{\bar{X} - \mu}{SE(\bar{X})}, -\]

-

Under the assumptions above (iid sample, normal population), the standard error is \(S/\sqrt{n}\) and the distribution of \(T\) is the \(T\)-distribution with \(n-1\) degrees of freedom.

-
-

For a data set of size \(n\) drawn from an iid random sample with a \(Normal(\mu,\sigma)\) population a \((1-\alpha)\cdot 100\)% confidence interval is given by \(\bar{x} \pm t_{1-\alpha/2}\cdot s/\sqrt{n}\), where \(t_{\alpha/2} = -t_{1-\alpha/2}\) satisfies \(1-\alpha = P(t_{\alpha/2} < T_{n-1} < t_{1-\alpha/2})\), \(T_{n-1}\) being \(T\) distributed with \(n-1\) degrees of freedom.

-
-
-

Example 2 (Confidence interval for the mean, no assumption on \(\sigma\)) Returning to the coffee-dispenser technician, a cappuccino dispenser has two sources of variance for the amount poured – the coffee and the foam. This is harder to engineer precisely, so is assumed unknown in the calibration process. Suppose the technician again took \(6\) samples to gauge the value of \(\mu\).

-

With no assumptions on the value of \(\mu\) or \(\sigma\). A \(95\)% confidence interval for \(\mu\) would be computed by:

-
-
xs = [7.9,  7.2,  7.1,  7.0,  7.0,  7.1]
-n = length(xs)
-s = std(xs)
-α = 0.05
-za = quantile(TDist(n-1), 1 - α +/2))
-SE = s / sqrt(n)
-(mean(xs) - za*SE, mean(xs) + za*SE)
-
-
(6.856683216914592, 7.576650116418743)
-
-
-

These computations – and many others – are carried out by functions in the HypothesisTests package. For example, we could have computed the values above through:

-
-
using HypothesisTests
-confint(OneSampleTTest(xs))
-
-
(6.856683216914592, 7.576650116418743)
-
-
-

Of some note, while often the extra assumption of a known standard deviation will lead to smaller confidence intervals, that is not guaranteed, as seen in this data set.

-
-
-

Example 3 (Dependent samples) A method to reduce variability between samples is to match treatments. A classic data set is shoes, which collected shoe wear data from 10 boys, each given two different shoes to wear.

-
-
using RDatasets
-shoes = dataset("MASS", "shoes")
-first(shoes, 2)
-
- -
2×2 DataFrame
RowAB
Float64Float64
113.214.0
28.28.8
-
-
-

Some boys are assumed to be harder on shoes than others, so by matching the two types, it is expected that the difference in the shoe wear per boy could be attributed to the material. That is, if \(X_1, \dots, X_{n}\) models the one material and \(Y_1, \dots, Y_n\) the other, the difference \(Z_i = X_i - Y_i\) should model the difference between the materials. Assuming this data is an iid random sample from a \(Normal(\mu, \sigma)\) population, we can find a \(90\)% confidence interval for the mean difference:

-
-
ds = combine(shoes, [:A,:B] => ByRow(-) => :Z)
-confint(OneSampleTTest(ds.Z); level=0.90)
-
-
(-0.634426399199845, -0.18557360080015525)
-
-
-

That this does not contain \(0\), suggests a difference in the mean wear between shoes.

-
-
-

The above illustrates a pattern: a sampling statistic (a pivotal quantity) which includes an unknown population parameter with a known sampling distribution independent of the parameters allows the formulation of confidence interval.

-
-
-

Confidence interval for a difference of means

-

For two iid random samples, \(X_1, X_2, \dots, X_{n_1}\) and \(Y_1, Y_2, \dots, Y_{n_2}\) from two normal populations \(Normal(\mu_1, \sigma_1)\) and \(Normal(\mu_2, \sigma_2)\) we may have sample data. From that data, the question of the difference between \(\mu_1\) and \(\mu_2\) can be considered.

-

With the assumption that the two samples are themselves independent, the standard deviation for \(\bar{X} - \bar{Y}\) can be computed as:

-

\[ -SD(\bar{X} - \bar{Y}) = \sigma_{\bar{X} - \bar{Y}} = \sqrt{\frac{\sigma_1^2}{n_1} + \frac{\sigma_2^2}{n_2}}. -\]

-

Let the \(T\) statistic be

-

\[ -T = \frac{(\bar{X} - \bar{Y}) - (\mu_1 - \mu_2)}{SE(\bar{X} - \bar{Y})} = \frac{\text{observed}-\text{expected}}{SE}. -\]

-

The distribution of \(T\) and the formula for \(SE\) depends on assumptions made:

-
    -
  • If both \(\sigma_1\) and \(\sigma_2\) are assumed known, \(SE(\bar{X} - \bar{Y}) = SD(\bar{X} - \bar{Y})\), and \(T\) has a normal distribution.
  • -
  • If it is assumed \(\sigma = \sigma_1 = \sigma_2\), but if no value is assumed, then the data can be pooled to estimate \(\sigma\), the common standard deviation, to get \(SE(\bar{X} - \bar{Y}) = s_p\sqrt{1/n_1 + 1/n_2}\) and \(T\) has a \(T\) distribution with \(n_1 + n_2 - 2\) degrees of freedom. The pooled standard deviation is the square root of \((s_1^2(n_1-1) + s_2^2(n_2-1))/(n_1+n_2-2)\).
  • -
  • If it is not assumed \(\sigma = \sigma_1 = \sigma_2\) (though it secretly could be), then the standard error is \(\sqrt{s_1^2/n_1 + s_2^2/n_2}\). The distribution of \(T\) is approximately \(T\)-distributed with an effective degrees of freedom given by the Welch-Satterhwaite equation, which is always between than the smaller of \(n_1-1\) and \(n_2-1\) and \(n_1 + n_2 - 2\).
  • -
-
-

Example 4 (Confidence interval for the difference of means) Suppose the coffee-dispenser technician is tasked with calibrating two machines. Again they take samples from the two machines, in this case:

-
Machine 1: 7.0, 7.8, 7.7, 7.6, 8.3
-Machine 2: 6.2, 8.0, 6.8, 7.0, 7.3, 7.9, 7.1
-

Do the machines output the same amount on average? To answer this, we consider a confidence interval for \(\mu_1 - \mu_2\).

-

If the two machines are assumed to have the same variance, we can compute a 90% confidence interval with:

-
-
xs = [7.0, 7.8, 7.7, 7.6, 8.3]
-ys = [6.2, 8.0, 6.8, 7.0, 7.3, 7.9, 7.1]
-confint(EqualVarianceTTest(xs, ys); level=0.90)
-
-
(-0.10761090026945175, 1.0961823288408845)
-
-
-

That \(0\) is in this interval gives evidence that the two means are equal. The sample means do differ:

-
-
mean(xs), mean(ys)
-
-
(7.6800000000000015, 7.185714285714285)
-
-
-

But the variability is such, the confidence interval makes it conceivable that the population means are the same. Perhaps were more data available, a difference would be seen, as the variablity is generally smaller for larger sample sizes.

-

If the machines are from different vendors, or dispense different beverages, perhaps the assumption of equal variances is not appropriate. The UnequalVarianceTTest method is available for this comparison. The calling pattern is identical:

-
-
confint(UnequalVarianceTTest(xs, ys); level=0.90)
-
-
(-0.07723849812971084, 1.0658099267011436)
-
-
-

The default for such tests in other languages, such as R, is to not assume equal variances.

-
-
-
- - ---- - - - - - - - - - - - - - - - - - - - - - - - - -
Table 1: Different constructors for \(T\)-statistic based confidence intervals
MethodDescription
OneSampleZTestInference on population mean for one sample, known population standard deviation
OneSampleTTestInference on population mean for one sample, unknown population standard deviation
EqualVarianceTTestInference on difference of population means for independent samples assuming equal population standard deviations
UnequalVarianceTTestInference on difference of population means for independent samples not assuming equal population standard deviations
-
-
-
-

Confidence interval for a proportion

-

A very common use of confidence intervals is found in the reporting of polls, particularly political polls, where it may be typical to see a statement such as “Candidate A is ahead of her opponent in the polls by 5 percentatge points with a margin of error of 5 percent” or “The favorability rating of President B is 54% with a 5 percent margin of error.” These are descriptions of confidence intervals, though both leave the confidence level unspecified. When unspecified, it can usually be roughly inferred from the margin of error, as will be seen.

-

A model for a poll where a person chose one of two options is to use a Bernoulli random variable, \(X_i\), to describe the response. The number of \(1\)s in a fixed number of \(n\) respondents can give a proportion. If one can assume the \(X_i\) are iid random samples from a \(Bernoulli(p)\) population, then the number of \(1\)s can be viewed as a realization of a \(Binomial(n,p)\) distribution. That is, this statistic will have a known distribution.

-

Before pursuing, let’s note that the assumption implies a few things about the sampling process carried out by the pollster:

-
    -
  • The population \(p\) is the proportion of the entire voting population (supposedly of size \(N\)) that would respond with a code of \(1\). A census could find \(p\), but random sampling is used as censuses are typically much more work to carry out.

  • -
  • The independent assumption technically requires sampling with replacement, where a person could be asked \(0\), \(1\), or more times the question. But practically if \(N\) is much greater than \(n\) this isn’t necessary and sampling without replacement can be used.

  • -
  • The identically distributed assumption requires the sampling to be representative. For example, if it is a state-wide population, a sample concentrated on one, possibly partisan, district would not be expected to be identically distributed from the population. Such sampling would likely introduce a bias.

  • -
-

Assuming, a survey is reasonably described by a \(Binomial(n,p)\) distribution, the BinomialTest can be used to identify a confidence interval for a given confidence level.

-

The BinomialTest function can take either two numbers x and n representing the number of \(1\)s in \(n\) sample, or a vector of trues and falses with true being a “\(1\)”.

-
-

Example 5 (A political poll) A poll was taken of 1429 likely voters, with 707 indicating support for candidate A and 722 for candidate B. Let \(p\) be the population proportion of voters for candidate A. Find a 95% confidence interval for \(p\).

-

Assuming the poll was collected in such a way that a binomial model would apply to the data, we have:

-
-
A = 707
-B = 722
-n = A + B
-ci = confint(BinomialTest(A, n); level = 0.95)
-
-
(0.4685125690214495, 0.521012196604416)
-
-
-

As \(0.50\) is in this interval, there is no suggestion candidate A can’t win. (And indeed, they did in this case). The report on this poll would be the sample proportion and a margin of error. The margin of error is the width of the interval divided by two:

-
-
(last(ci) - first(ci)) * 1/2
-
-
0.02624981379148325
-
-
-

A bit under 3 percentage points. A rough guide is 3 percentage points is around 1,000 people polled, a larger margin of error (MOE) is fewer people polled, a smaller one is more than 1,000 polled. Why this is so is expanded on in a bit.

-

The BinomialTest has several different ways to compute the margin of error, or the confidence interval. These are passed to the method argument of confint as symbols. The default is :clopper_pearson which is based on the binomial distribution. The value :wald uses a normal approximation to the binomial, which may be very inaccurate when \(p\) is close to \(0\) or \(1\). There are others (cf. ?confint(:BinomialTest)).

-

The default one can be understood through the quantile function, and is essentially given through:

-
-
alpha = 0.05
-quantile.(Binomial(n, A/n), [alpha/2, 1 - alpha/2]),  ci .* n
-
-
([670, 744], (669.5044611316513, 744.5264289477104))
-
-
-

For confidence intervals calculated through the normal approximation, an explicit formula for the margin of error, based on the standard deviation of the binomial distribution, is available:

-

\[ -MOE = z_{1-\alpha/2} \cdot \sqrt{p(1-p)/n} = z_{1-\alpha/2} SE(\hat{p}). -\]

-

As \(p\) is unknown, the standard error is used with \(\hat{p}\) approximating \(p\).

-
-
ci = confint(BinomialTest(A, n); level = 0.95, method=:wald)
-z_a = quantile(Normal(0,1), 1-alpha/2)
-phat = A/n
-SE = sqrt(phat * (1-phat)/n)
-z_a * SE, (last(ci) - first(ci))/2  # compare to that computed with :wald
-
-
(0.025922569857879177, 0.025922569857879163)
-
-
-

This formula allows one to estimate the sample size, \(n\), needed to achieve a certain margin of error, though an estimate for \(p\) can be used, taking \(p=1/2\) gives a conservative number as \(p(1-p)\) is largest for that value. For example, to see what a 3% margin of error with a 95% confidence level would need in terms of a sample, we have, solving for \(n\):

-

\[ -n = p(1-p)\cdot \left(\frac{z_{1-\alpha/2}}{MOE}\right)^2 \leq \frac{1}{4}\left(\frac{z_{1-\alpha/2}}{MOE}\right)^2. -\]

-
-
MOE = 0.03
-alpha = 0.05
-z_a = quantile(Normal(0,1), 1-alpha/2)
-1/2 * (1- 1/2) * (z_a / MOE)^2
-
-
1067.071894637261
-
-
-

A bit more than 1000 in this case ensures that the MOE will be no more than \(0.03\); it could be much less if it was expected that \(p\) is far from \(1/2\).

-
-
-
-

Confidence interval for a difference of proportions

-

For two sample proportions, \(\hat{p}_1 = x_1/n_1\) and \(\hat{p}_2=x_2/n_2\), the \(T\) statistic:

-

\[ -T = \frac{(\hat{p}_1 - \hat{p}_2) - (p_1 - p_2)}{SE(\hat{p}_1 - \hat{p}_2)} -\]

-

has a standard normal distribution if the \(n\) values are both large enough. This allows a confidence interval for \(p_1 - p_2\) to be given by:

-

\[ -(\hat{p}_1 - \hat{p}_2) - z_{1-\alpha/2} \cdot SE(\hat{p}_1 - \hat{p}_2) < p_1 - p_2 < (\hat{p}_1 - \hat{p}_2) + z_{1-\alpha/2} \cdot SE(\hat{p}_1 - \hat{p}_2), -\]

-

where \(SE(\hat{p}_1 - \hat{p}_2) = \sqrt{\hat{p}_1(1-\hat{p}_1)/n_1 + \hat{p}_2(1-\hat{p_2})/n_2}\).

-
-

Example 6 (Difference of population proportions) Did some external event cause people to reexamine their choice for a political race? Suppose polls taken several weeks apart yielded:

- - - - - - - - - - - - - - - - - - - - -
Candidate ACandidate B
Oct289259
Nov513493
-

Compute a \(90\)% confidence interval.

-
-
x1, x2 = 289, 513
-n1, n2 = x1 + 259, x2 + 493
-phat1, phat2 = x1/n1, x2/n2
-SE = sqrt(phat1*(1-phat1)/n1 + phat2*(1-phat2)/n2)
-alpha = 0.10
-za = quantile(Normal(0,1), 1 - alpha/2)
-(phat1 - phat2) .+ [-1,1] * za * SE
-
-
2-element Vector{Float64}:
- -0.026187674280803125
-  0.06105148412248293
-
-
-

That \(0\) is in this, suggests the possibility that there was no change in the polls.

-
-
-
-

Confidence interval for a population standard deviation

-

Under a normal population assumption for an iid random sample, \((n-1)S^2/\sigma^2\) has a \(Chisq(n-1)\) distribution. Solving \(\chi^2_{\alpha/2} < (n-1)S^2/\sigma^2 < \chi^2_{1-\alpha/2}\) for \(\sigma\) gives a formula for a \((1-\alpha)\cdot 100\)% confidence interval for \(\sigma^2\):

-

\[ -\frac{(n-1)S^2}{\chi^2_{1-\alpha/2}} < \sigma^2 < \frac{(n-1)S^2}{\chi^2_{\alpha/2}}. -\]

-

To use this, suppose our data is

-
1.2, -5.2, -8.4, 3.1, -2.1, 3.8
-

We can give a \(90\)% CI by:

-
-
xs = [1.2, -5.2, -8.4, 3.1, -2.1, 3.8]
-n, s² = length(xs), var(xs)
-alpha = 0.10
-cl, cr = quantile.(Chisq(n-1), [alpha/2, 1 - alpha/2])
-(sqrt((n-1)*/cr), sqrt((n-1)*/cl))
-
-
(3.2630536146068865, 10.144128513617867)
-
-
-
-
-

Confidence interval for comparing population standard deviations

-

The comparison of two sample standard deviations is of interest, as seen in the two-sample \(T\) confidence interval, where different formulas are available should there be an assumption of equality. For two independent normally distributed iid random samples, the \(F\)-statistic can be used to construct a confidence interval, as a scaled ratio of the sample standard deviations will have a certain \(F\) distribution. That is, for a given \(\alpha\), the following happens with probability \(1-\alpha\):

-

\[ -F_{\alpha/2; n_1-1, n_2-1} < (S_1^2/\sigma_1^2) / (S_2^2/\sigma_2^2) < F_{1 -\alpha/2; n_1-1, n_2-1} -\]

-

or, after rearrangement:

-

\[ -\frac{S_1^2/S_2^2}{F_{1 -\alpha/2; n_1-1, n_2-1}} < \frac{\sigma^2_1}{\sigma^2_2} < -\frac{S_1^2/S_2^2}{F_{\alpha/2; n_1-1, n_2-1}}. -\]

-

For a single sample, this is used to construct a confidence interval for the ratio of variances (or standard deviations). The VarianceFTest computes the necessary pieces, but the VarianceFTest type does not have a confint method. In the following we extend the confint generic using the above for objects created by VarianceFTest:

-
-
function HypothesisTests.confint(F::VarianceFTest; level=0.95)
-    alpha = 1 - level
-    dof1, dof2 = F.df_x, F.df_y
-    fl, fr = quantile.(FDist(dof1, dof2), (alpha/2, 1-alpha/2))
-    F.F ./ (fr, fl)
-end
-
-

To use this, for example, suppose we have two samples:

-
xs:  1.2, -5.2, -8.4,  3.1, -2.1,  3.8
-ys: -1.7, -1.8, -3.3, -6.3,  8.4, -0.1, 2.5, -3.8
-

Then a \(90\)% confidence interval for \(\sigma^2_1/\sigma^2_2\) is given by:

-
-
xs =  [1.2, -5.2, -8.4, 3.1, -2.1, 3.8]
-ys =  [-1.7, -1.8, -3.3, -6.3, 8.4, -0.1, 2.5, -3.8]
-confint(VarianceFTest(xs, ys); level=0.90)
-
-
(0.28992354789264896, 5.614264355299279)
-
-
-

That this includes 1 suggest no reason to think the two population variances differ.

-
-
-

Likelihood ratio confidence intervals

-

The confidence intervals so far are based on pivotal quantities with known sampling distributions. A more generally applicable alternative is to use the likelihood function, \(L(\theta | x) = f(x | \theta)\), where the latter is the joint pdf of the data for a given parameter or parameters. The idea of maximizing the likelihood is to choose the value(s) for \(\theta\) which maximize the probability of seeing the observed data using the choice(s) to estimate \(\theta\).

-

The maximum likelihood estimate is an alternative manner to estimate a parameter. Here we apply the method to data that would otherwise be amenable to a a \(T\)-test. First we generate some random data:

-
-
μ, σ = 10, 3
-n = 8
-D = Normal(μ, σ)
-ys = rand(D, n)
-ys |> show
-
-
[7.918166275192764, 10.539158051648759, 11.400538417058709, 7.555601875369558, 1.547706898349972, 8.41392335411949, 7.538364470309029, 8.921851370911972]
-
-
-

The likelihood function below uses an assumption on on the data, in particular that it is normally distributed with unknown mean and standard deviation (\(y_i \sim Normal(\mu, \sigma)\)). This just happens to match how the random data was generated, but typically is an assumption about the data. We write the log-likelihood function so it takes two values the unknown parameters and the data:

-
-
function loglik(θ, data)
-    # yᵢ ~ N(μ, σ) is model
-    μ, σ = θ
-    D = Normal(μ, σ)
-    y = first(data)
-    ll = 0.0
-    for yᵢ  y
-        ll += logpdf(D, yᵢ)
-    end
-    ll
-end
-
-
loglik (generic function with 1 method)
-
-
-

Above, we use logpdf to compute the logarithm of the probability density function, as an alternative to log(pdf(D, yᵢ)).

-

To maximize the data, we will use the ProfileLikelihood package, which relies on the Optim package to perform the optimization:

-
-
using ProfileLikelihood, Optim, OptimizationOptimJL
-
-

The optimization needs an initial guess, \(\theta_0\), defined below. We also specify names to the parameters, when we set up a “problem” below:

-
-
θ₀ = [5.0, 2.0]
-nms = [:μ, :σ]
-dat = (ys,) # data is a container
-prob = LikelihoodProblem(loglik, θ₀;
-                         data = dat,
-                         syms = nms)
-
-
-
LikelihoodProblem. In-place: true
-θ₀: 2-element Vector{Float64}
-     μ: 5.0
-     σ: 2.0
-
-
-
-

This problem is solved by mle, which needs an optimization algorithm. We use the NelderMead one, which is easier to specify, as we don’t need to set up means to take a gradient:

-
-
sol = mle(prob, Optim.NelderMead())
-
-
-
LikelihoodSolution. retcode: Success
-Maximum likelihood: -19.482356731739785
-Maximum likelihood estimates: 2-element Vector{Float64}
-     μ: 7.979378609758597
-     σ: 2.763131216804945
-
-
-
-

As seen above, the sol object outputs the estimate for \(\mu\). We can compare to mean(ys) and std(ys); the mean agrees (the standard deviation has a different divisor, so is systematically different, but could be aligned given the sample size):

-
-
mean(ys), std(ys)
-
-
(7.979413839120031, 2.953886480885067)
-
-
-

The reason to use the ProfileLikelihood package to do the optimization, is we can then find \(95\)% confidence levels for the parameters using a method implemented therein. The profile generation requires a specification of upper and lower bounds for the parameters:

-
-
lb = [-100.0, 0.0]
-ub = [ 100.0, 100.0]  # -100 < μ < 100; 0 < σ < 100
-resolutions = [200, 200]
-param_ranges = construct_profile_ranges(sol, lb, ub, resolutions)
-prof = profile(prob, sol; param_ranges=param_ranges)
-
-
-
ProfileLikelihoodSolution. MLE retcode: Success
-Confidence intervals: 
-     95.0% CI for μ: (5.8101390944467886, 10.14846273551349)
-     95.0% CI for σ: (1.8128950062714275, 4.959125486619269)
-
-
-
-

We can compare the confidence interval identified for \(\mu\) to that identified through the \(T\) statistic:

-
-
confint(OneSampleTTest(ys), level = 0.95)
-
-
(5.509902940980954, 10.448924737259109)
-
-
-

The two differ – they use different sampling distributions and methods – though simulations will show both manners create CIs capturing the true mean at the rate of the confidence level.

-

The above example does not showcase the advantage of the maximimum likelihood methods, but hints at a systematic way to find confidence intervals, which for some cases is optimal, and is more systematic then finding some pivotal quantity (e.g. the \(T\)-statistic under a normal population assumption).

-
-
-
-

Hypothesis tests

-

A confidence interval is a means to estimate a population parameter with an appreciation for the variability involved in random sampling. However, sometimes a different language is sought. For example, we might hear a newer product is better than an old one, or amongst two different treatments there is a difference. These aren’t comments about the specific value. The language of hypothesis tests affords the flexibility to incorporate this language.

-

The basic setup is similar to a courtroom trial in the United States – as seen on TV:

-
    -
  • a defendant is judged by a jury with an assumption of innocence
  • -
  • presentation of evidence is given
  • -
  • the jury weighs the evidence assuming the defendant is innocent.
  • -
  • If it is a civil trial a preponderence of evidence is enough for the jury to say the defendent is guilty (not innocent); if a criminal trial the standard is if the evidence is “beyond a reasonable doubt” then the defendent is deemed not innocent. Otherwise the defendant is said to be “not guilty,” though really it should be that they weren’t “proven” to be guilty.
  • -
-

In a hypothesis or significance test for parameters, the setup is similar:

-
    -
  • a null hypothesis of “no difference” is assumed; an alternative hypothesis is specified.
  • -
  • data is collected
  • -
  • a test statistic is used to summarize the data. Then a probability is computed that accounts for the assumption of the null hypothesis and the data, computing the probability that the random values would be as or more extreme than observed in the data assuming the null hypothesis is true
  • -
  • if that probability is small, then one might conclude the null hypothesis is incorrect; otherwise there is no evidence to say it isn’t correct, small would depend on the context of application.
  • -
-

To illustrate hypothesis tests, suppose a company claims their new product is more effective than the current standard. Further, suppose this effectiveness can be measured by a single number, though there is randomness to any single measurement. Over time the old product is known to have a mean of \(70\) in this measurement.

-

Then the null and alternative hypotheses would be:

-

\[ -H_0: \mu = 70 \quad H_A: \mu > 70, -\]

-

where a bigger number is considered better. The null is an assumption of “no change,” the alternative points in the direction of the claim (better, in this case).

-

To test this, data would be collected. Suppose in this case 8 measurements of the new product were taken with these values:

-
73.0, 66.1, 76.7, 68.0, 60.8, 81.8, 73.5, 75.2
-

Assume these are realizations of an iid random sample from a normal population, then the sample mean would be an estimate for the population mean of the new product:

-
-
xs = [73.0, 66.1, 76.7, 68.0, 60.8, 81.8, 73.5, 75.2]
-mean(xs)
-
-
71.8875
-
-
-

This is more than 70, but the skeptic says half the time a sample mean would be more than 70 if the null hypothesis were true. Is it really indicative of “better?” For that question, a sense of how extreme this observed value is under an assumption of the null hypotheses is used.

-

To test this, we characterize the probability a sample mean would be even more than our observed one, were we to repeat this sampling many times:

-
-

The \(p\)-value is the probability the test statistic is as or more extreme than the observed value under the null hypothesis.

-
-

For this problem the \(p\)-value is the probability the sample mean is mean(xs) or more assuming the null hypothesis. Centering by \(\mu\) and scaling by the standard error, this is:

-

\[ -P(\frac{\bar{X} - \mu}{SE(\bar{X})} > \frac{\bar{x} - \mu}{s/\sqrt{n}} \mid H_0). -\]

-

That is, large values of \(\bar{X}\) are not enough to be considered statistically significant, rather large values measured in terms of the number of standard errors are.

-

This scaling and an assumption that the data is from a normal population makes this a statement about a \(T\)-distributed random variable, namely:

-
-
n = length(xs)
-SE = std(xs) / sqrt(n)
-obs = (mean(xs) - 70) / SE
-p_value = 1 - cdf(TDist(n-1), obs)  # 1 - P(T ≤ obs) = P(T > obs)
-
-
0.22361115388017372
-
-
-

Is this \(p\)-value suggestive that the null hypothesis is not a valid explanation of the variation seen in the data?

-

The jury has guidance, depending on the type of trial, here there is also guidance in terms of a stated significance level. This is done in advance, but is typically just \(\alpha = 0.05\). So if the \(p\)-value is less than \(\alpha\) the null hypothesis is suggested to be incorrect; if not there is no evidence to conclude the null hypothesis is incorrect. Since here the \(p\)-value is much greater than \(\alpha = 0.05\), we would say there is no evidence to reject the null hypothesis.

-

The mechanics here are standard and encapsulated in the OneSampleTTest constructor. The pvalue method allows the specification of the type of “tail”. In this case, as the alternative hypothesis is \(H_A: \mu > 70\), the tail is “:right”:

-
-
pvalue(OneSampleTTest(xs, 70); tail=:right)
-
-
0.22361115388017377
-
-
-

Had the alternative been \(H_A: \mu \neq 70\), the \(p\)-value would be computed differently as then very big or very small values of the observed test statistic would be evidence against the null hypothesis. In this case, the tail is :both, which is the default:

-
-
pvalue(OneSampleTTest(xs, 70))
-
-
0.44722230776034755
-
-
-
-

Design of structures in HypothesisTests

-

The HypothesisTests package was first published in 2015 and uses a calling style where the main functions match the underlying types. In particular, the OneSampleTTest method creates a structure, also called OneSampleTTest. It is illustrative of the other tests in the HypothesisTests package. This structure does not keep the data, only sufficient statistics of the data to generate the various summaries. In this case, these are n, xbar (\(\bar{x}\)), df (\(n-1)\), stderr (\(SE\)), t ((mean(xs)-mu)/stderr), and μ0 (the null hypothesis). The design is the default show method summarizes most of what may be of interest, specialized methods, like confint and pvalue allow customization and access to the specific values. The default show method for this test is:

-
-
OneSampleTTest(xs)
-
-
One sample t-test
------------------
-Population details:
-    parameter of interest:   Mean
-    value under h_0:         0
-    point estimate:          71.8875
-    95% confidence interval: (66.34, 77.43)
-
-Test summary:
-    outcome with 95% confidence: reject h_0
-    two-sided p-value:           <1e-07
-
-Details:
-    number of observations:   8
-    t-statistic:              30.6644467681273
-    degrees of freedom:       7
-    empirical standard error: 2.3443273098512263
-
-
-

A default value for the null was chosen (h_0), a default level for the confidence interval (0.95), and a default choice of tail was made for the computed \(p\)-value.

-
-
-

Equivalence between hypothesis tests and confidence intervals

-

For many tests, such as the one-sample \(T\)-test, there is an equivalence between a two-sided significance test with significance level \(\alpha\) and a \((1-\alpha)\cdot 100\)% confidence interval – no surprise given the same \(T\)-statistic is employed by both.

-

For example, consider a two-sided significance test of \(H_0: \mu=\mu_0\). Let \(\alpha\) be the level of significance: we “reject” the null hypothesis if the \(p\)-value is less than \(\alpha\). An iid random sample is summarized by the observed value of the \(T\)-statistic, \((\bar{x} - \mu_0)/(s/\sqrt{n})\). Let \(t^* = t_{1-\alpha/2}\) be the quantile, then if the \(p\)-value is less than \(\alpha\), we must have the observed value in absolute value is greater than \(t^*\). That is

-

\[ -| \frac{\bar{x} - \mu_0}{s/\sqrt{n}}| > t^*. -\]

-

Algebraically, this implies that either \(\mu_0 < \bar{x} - t^* s/\sqrt{n}\) or \(\mu_0 > \bar{x} + t^* s/\sqrt{n}\). That is, the value of \(\mu_0\) is not in the \((1-\alpha)\cdot 100\)% CI based on the sample.

-
-

Rejecting the null hypothesis \(H_0: \mu=\mu_0\) is equivalent to \(\mu_0\) not being in the corresponding confidence interval.

-
-

The reverse is also true: any \(\mu_0\) not in the \((1-\alpha)\cdot 100\)% CI would have a two-tailed test of \(H_0: \mu=\mu_)\) rejected at the \(\alpha\) significance level.

-
-
-

Alternative hypotheses

-

The null hypothesis is also described as one of “no effect,” the alternative hypothesis the “research hypothesis.” The \(p\)-value is computed under the null hypothesis. The alternative hypothesis may take different forms: it could be a specification of a point, as will be seen in the next section on power; a one-tailed directional hypothesis, useful to capture expressions like “better;” two-tailed directional, useful to capture expressions like “different;” and non-directional, useful for indicating anything save the null hypothesis. The distinction between the last two is more clear when the null hypothesis speaks to more than one variable, such as is the case when describing, say, different probabilities for a multinomial distribution.

-

In HypothesisTests, for the \(T\) tests and many others, the alternative is specified to pvalue through the tail argument with a value of :both, :right, or :left. Some tests have non-drectional alternatives and no argument. In the case of a \(T\)-test, with a symmetric sampling distribution, symmetry relates the values, so knowing one can be used to figure out the other two.

-
-
-

Type-I error

-

The computation of a \(p\)-value is the result of a significance test. Often this value is compared to a significance level, \(\alpha\): if the \(p\)-value is less than \(\alpha\) the difference of the test statistic from the null hypothesis is statistically significant. In other language, we may “reject” the null hypothesis when the \(p\)-value is less than \(\alpha\) and “accept” the null if not.

-

The \(p\)-value is computed under the null hypothesis being true. The \(p\)-value depends on a random sample. How often, on average, would this \(p\)-value be less than \(\alpha\)? Why, \(\alpha\cdot 100\) percent of the time, as \(\alpha = P(\text{reject} | H_0)\).

-

We can see this through a simulation. Let’s look at the \(T\) statistic for a sample of size \(10\) from a \(Normal(0,1)\) population:

-
-
Z = Normal(0,1)
-n = 10
-N = 5000
-ps = [pvalue(OneSampleTTest(rand(Z, n))) for _ in 1:N]
-sum(ps .< 0.01)/N, sum(ps .< 0.05)/N, sum(ps .< 0.10)/N
-
-
(0.0106, 0.0508, 0.0972)
-
-
-

The proportions in the 5000 samples roughly match the threshold specified (\(\alpha = 0.01\), \(0.05\), \(0.10\)).

-

There are some cases where a conservative sampling distribution is used. (For example, with a two sample test of means with no assumption of equal variances, a conservative degrees of freedom is sometimes suggested.) Conservative means that “rejecting” the null (or finding \(p\) value less than \(\alpha\)) will occur on average less than \(\alpha\cdot 100\) percent of the time.

-
-
-

Power

-

Consider a significance test for the population mean with this null and alternative hypothesis:

-

\[ -H_0: \mu=0, \quad H_A: \mu=1. -\]

-

That is the alternative is fully specified, unlike, say, \(H_A: \mu > 0\).

-

When a small \(p\) value is found, the language of “rejecting” the null hypothesis is used, whereas for large \(p\) values, “accept” is used. If we “reject” when the \(p\)-value is less than \(\alpha\), then the probability of making a mistake when the null hypothesis is actually true is \(\alpha\). This is called a Type-I error. In the above, as the alternative is a fixed value of the parameter, we could also compute the probability of “accepting” when the alternative hypothesis is actually true. When this error occurs, a Type-II error happens.

-

For example, a sample of size \(n=4\) from a \(Normal(\mu, 1)\) distribution would have a standard error of \(1/\sqrt{4} = 1/2\). Without thinking too much, were the sample mean close to \(0\) we would accept the null, were it bigger than \(1\) we would reject the null. Precisely, if we set \(\alpha = 0.05\), then we can compute under the null hypothesis a critical value for which if \(\bar{x}\) is less we “accept” and if more we “reject”:

-
-
alpha = 0.05
-sigma = 1
-n = 4
-SD = sigma / sqrt(n)
-zc = quantile(Normal(0, SD), 1 - alpha)
-
-
0.8224268134757359
-
-
-

This is computed under the null hypothesis. In this case, we can compute under the alternative hypothesis, as it is fully specified, the probability of “accepting” when the alternative is true. This probability is traditional called \(\beta\) and depends on the exact specification of the alternative. For this specific one we have:

-
-
mu_a = 1
-cdf(Normal(mu_a,  SD), zc)
-
-
0.36123996868766456
-
-
-

The left graphic in Figure 4 shows the sampling distribution under the null hypothesis centered at \(0,\) and the sampling distribution under the alternative hypothesis, centered at \(1\). The shaded area representing \(\beta\) is much bigger than \(\alpha\).

-

The value for \(\beta\) is pretty large, over a third, so with such a small sample, it is difficult to detect a difference of \(1 = \mu_A - \mu_0\). The basic expectation is: the smaller the difference, the larger the sample is needed to have “\(\beta\)” be small.

-

The power is \(1-\beta\). It may be specified ahead of time, as in: what size \(n\) is needed to have power \(0.80\) with a difference of means being \(1\).

-

To solve this, we would need to solve

-
1 - beta = 1 - cdf(Normal(mu_a, sigma/sqrt(n)),
-    quantile(Normal(0, sigma/sqrt(n)), 1 - alpha))
-

For this problem we could do this with the following approach, where we first define a function to get the \(\beta\) value for a given \(n\) and set of assumptions:

-
-
function get_beta(n)
-    H0 = Normal(0, sigma/sqrt(n))
-    HA = Normal(mu_a, sigma/sqrt(n))
-    critical_point = quantile(H0, 1 - alpha)
-    beta = cdf(HA, critical_point)
-end
-
-
get_beta (generic function with 1 method)
-
-
-

This is then solved for a value of n where the power for a given n matches the desired power. To do so, we equate the values and, here, note a sign change between \(2\) and \(100\):

-
-
beta = 0.2
-power = 1 - beta
-f(n) = (1 - get_beta(n)) - power
-f(2), f(100)
-
-
(-0.3912027802061293, 0.19999999999999996)
-
-
-

Somewhere between \(2\) and \(100\) lies the answer, and using a zero finding algorithm, a value of \(7\) can be seen to produce a power greater than or equal to \(0.80\).

-
-
using Roots
-find_zero(f, (2, 100))
-
-
6.182557232019764
-
-
-

(We round up in calculations involving sample size.)

-
-
-
-
-
-

-
-
-
-
-

-
-
-
-

Figure 4: Illustration of power computation. A significance level \(\alpha\) is chosen; under \(H_0\) this allows the identification of a critical point. Under \(H_A\), that same critical point is used to find the area in the the “acceptance” region. This probability is \(\beta\). The power is \(1-\beta\). Adjusting the sample size, \(n\), will narrow the two bell curves allowing \(\beta\) to be set to a specified value. The left-hand figure uses \(n=4\), the right hand one uses \(n=7\).

-
-
-

The computation above was made easier as we used the standard deviation in our test statistic with \(\sigma = 1\). To find a sample size for a \(T\)-test, we turn to a package, as the underlying sampling distributions are a bit different, especially under \(H_A\).

-

Let the effect size, or standardized mean difference, be the absolute effect size (the difference between the mean specified under \(H_0\) and that under \(H_A\)) divided by the standard deviation of the population. This value is termed “small” when around \(0.20\), medium when around \(0.50\), and large when \(0.80\) or more. In the above computation, it would be \(1 = (1 - 0)/1\).

-

The power, \(1-\beta\), depends on the value of \(\alpha\), the sample size, and the effect size. Specifying 3 of these 4 values allows the fourth to be numerically identified.

-

The PowerAnalyses package carries out several different power computations. For the one test we are illustrating, the OneSampleTTest, the name conflicts with a related, but different method in the HypothesisTests package, as PowerAnalyses does not extend HypothesisTests. We use import instead of using, and then qualify with the module name each function used.

-
-
import PowerAnalyses
-
-

For example, to identify the sample size needed for a right-tailed test with \(\alpha = 0.05,\) \(\beta = 0.20,\) and the effect size is small (\(0.30\)) we have:

-
-
effect_size = 0.3
-alpha = 0.05
-power = 0.80
-
-T = PowerAnalyses.OneSampleTTest(PowerAnalyses.one_tail)
-PowerAnalyses.get_n(T; alpha=alpha, es=effect_size, power=power)
-
-
70.06790520005853
-
-
-

We see a smaller \(n\) is needed to have the same power with a larger effect size.

-
-
effect_size = 0.8
-PowerAnalyses.get_n(T; alpha=alpha, es=effect_size, power=power)
-
-
11.144239681013328
-
-
-

There are other power computations provided in the package, but this one illustrates a key point: with large enough sample sizes, any effect size can be discerned. That is the difference is statistically significant. However, that does not mean it is practically significant.

-

An example from the NIH describes a “Physicians Health Study of aspirin to prevent myocardial infarction. In more than \(22,000\) subjects over an average of \(5\) years, aspirin was associated with a reduction in MI (although not in overall cardiovascular mortality) that was highly statistically significant: \(p < .00001\). The study was terminated early due to the conclusive evidence, and aspirin was recommended for general prevention. However, the effect size was … extremely small. As a result of that study, many people were advised to take aspirin who would not experience benefit yet were also at risk for adverse effects. Further studies found even smaller effects, and the recommendation to use aspirin has since been modified.

-
-
-

One sample Z test

-

The one-sample \(Z\) test is similar to the one-sample \(T\) test though the known population standard deviation is used in the test statistic, so the standard normal distribution is the sample distribution.

-

For example, consider again the coffee-dispenser technician. They wish to test the hypothesis

-

\[ -H_0: \mu = 7.5, \quad H_A: \mu < 7.5 -\]

-

assuming the distribution is \(Normal(\mu, 1/2)\). The test statistic used would be \((\bar{x}-\mu)/(\sigma/\sqrt{n})\). The data collected is

-
7.9  7.2  7.1  7.0  7.0  7.1
-

The test can be carried out through the following, which shows evidence to reject the null in favor of the alternative.

-
-
xs = [7.9,  7.2,  7.1,  7.0,  7.0,  7.1]
-Z = OneSampleZTest(xs, 7.5)
-pvalue(Z, tail=:left)
-
-
0.02152440439927433
-
-
-

(The statistician may say, if the mean is \(7.5\) and the standard deviation is \(0.5\), then anything more than 1 standard deviation from the mean will overflow an \(8\)oz cup. But the technicians know that the normal distribution is only a generalization and that the values are always within \(0.5\) oz of the calibration mean.)

-
-
-

The sign test

-

The Wilcoxon signed-rank test is an alternative to the \(Z\) and \(T\) tests. Unlike those, there is no distribution assumption on the population except that it be symmetric. For symmetric distributions with reasonable tails, the median and mean are identical, so this is often termed a test of the median. The test is implemented in the SignedRankTest function. Unlike other tests, we use the null \(H_0: M = 0\), so we subtract off the tested median before proceeding.

-

Returning to the coffee-dispenser technician who took 6 samples and is testing if the center of the data is centered at \(7.5\) or something different, we have:

-
-
xs = [7.9,  7.2,  7.1,  7.0,  7.0,  7.1]
-M = SignedRankTest(xs .- 7.5)
-pvalue(M, tail=:both)
-
-
0.15625
-
-
-

This \(p\)-value is large, there is no suggestion that \(H_0\) does not hold.

-
-
-

One sample test of proportions

-

A test of survey data where one of two answers are possible can be carried out using the binomial model, assuming the sample data is representative.

-

For example, suppose it is known that in any given semester, 50% of matriculating students will have a lower semester GPA than their cumulative GPA. A researcher tests if college transfer students are different with \(p\) representing the population proportion whose lower semester GPA will be worse than their cumulative one. The test is:

-

\[ -H_0: p = 0.50, \quad H_A: p > 0.50 -\]

-

They collect data and find of 500 transfer students identified, 290 had lower GPAs than their accumulated GPA. Is this data statistically significant?

-
-
x, n = 290, 500
-p = 0.5
-B = BinomialTest(x, n, p)
-pvalue(B, tail=:right)
-
-
0.00020020776841988704
-
-
-

The notion of “transfer shock” anticipates first semester fall off exceeding typical rates; it is known that on average this difference in performance goes away after transfer students become more familiar with their new environment.

-
-
-

Two-sample test of center

-

A two-sample test of center can test if the population means or medians are equal. The latter could be done with the signed-rank test. Here we illustrate a test of the population means based on the \(T\)-statistic:

-

\[ -T = \frac{(\bar{X}_1 - \bar{X}_2) - (\mu_1 - \mu_2)}{SE(\bar{X}_1 - \bar{X}_2)}. -\]

-

The null hypothesis will be \(H_0: \mu_1 = \mu_2\), the alternative is either direction or both. The \(SE\) will depend on an assumption of equal population variances or not (EqualVarianceTTest or UnequalVarianceTTest). The basic pattern to find the corresponding \(p\)-value is identical.

-

For example, consider the coffee-dispenser technician seeing if two machines have an equal calibration. The hypotheses would be:

-

\[ -H_0: \mu_1 = \mu_2, \quad H_A: \mu_1 \neq \mu_2 -\]

-

The collected data is:

-
Machine 1: 7.0, 7.8, 7.7, 7.6, 8.3
-Machine 2: 6.2, 8.0, 6.8, 7.0, 7.3, 7.9, 7.1
-

Without assuming equal variances, the test could be carried out with:

-
-
xs = [7.0, 7.8, 7.7, 7.6, 8.3]
-ys = [6.2, 8.0, 6.8, 7.0, 7.3, 7.9, 7.1]
-T = UnequalVarianceTTest(xs, ys)
-pvalue(T)  # use default tail=:both
-
-
0.14802700278100428
-
-
-

The data does not suggest a statistically significant difference between the two machines.

-
-
-

Two-sample test of proportions

-

For a comparison of two population proportions, we do the math using the test statistic:

-

\[ -Z = \frac{(\hat{p}_1 - \hat{p}_2) - (p_1 - p_2)}{SE(\hat{p}_1 - \hat{p}_2)}, -\]

-

which for large enough \(n\) will have a standard normal distribution, assuming the sampling is well modeled by independent, binomial random variables.

-

Suppose, we are interested in comparing two randomly chosen groups of transfer students at different institutions. We are testing if the GPA on the first semester after transfer falls off from the accumulated average. The test hypotheses are:

-

\[ -H_0: p_1 = p_2, \quad, H_A: p_1 \neq p_2. -\]

-

The test statistic involves \(SE\) where the \(SD\) is \(\sqrt{p_1(1-p_1)/n_1 + p_2(1-p_2)/n_2}\). The null hypothesis states \(p_1=p_2\), but not what that value is, so the \(SE\) is found by estimating \(p_1=p_2\) by pooling the data.

-

The collected data is:

-
             x      n
-School A    120    200
-School B    100    190
-

We proceed to compute the observed value of the test statistic:

-
-
x1, n1 = 120, 200
-x2, n2 = 100, 190
-phat1, phat2 = x1/n1, x2/n2
-phat = (x1 + x2)/(n1 + n2) # pooled
-SE = sqrt(phat*(1-phat) * (1/n1 + 1/n2))
-Z_obs = (phat1 - phat2)/SE
-
-
1.4667724206855928
-
-
-

This is a two-tailed test, so the \(p\)-value incorporates potential test statistics more than Z_obs or less than -Z_obs. (The minus sign comes, as Z_obs is positive.) Since the sampling distribution is a symmetric (standard normal) we have:

-
-
p_value = 2 * (1 - cdf(Normal(0,1), Z_obs))
-
-
0.14243797452124007
-
-
-

This being larger than \(\alpha = 0.05\), suggests no reason to reject the null hypothesis.

-
-
-

Test of variances

-

The question of two, independent, normally distributed, samples having equal variances can be tested through an \(F\)-test and is implemented in VarianceFTest.

-

For example, consider a test for equal variances over two levels of some treatment with hypotheses:

-

\[ -H_0: \sigma_1 = \sigma_2, \quad H_A: \sigma_1 \neq \sigma_2 -\]

-

The \(F\) test considers the ratio \(\sigma_1^2/\sigma_2^2\), so these hypotheses may also have been specified in terms of ratios.

-

Suppose two independent samples were collected from normally distributed populations, giving:

-
xs: -2.1, -1.8, 1.3,  -0.9, 1.7,  -2.0, -1.6, 3.8, -0.8, 5.5
-ys:  7.6,  6.4, 7.2,  16.1, 6.6,  10.7, 11.0, 9.4
-

We enter the data and then pass this to the test:

-
-
xs = [-2.1, -1.8, 1.3, -0.9, 1.7, -2.0, -1.6, 3.8, -0.8, 5.5]
-ys = [7.6, 6.4, 7.2, 16.1, 6.6, 10.7, 11.0, 9.4]
-F = VarianceFTest(xs, ys)
-pvalue(F, tail=:both)
-
-
0.566171938952384
-
-
-
-
-
- -
-
-Robustness of the F test -
-
-
-

The \(F\)-test of variance, unlike, say, tests for the population mean, are sensitive to departures from the assumption of a normally distributed population. For this case, this was first noticed by Pearson (cf. (BOX 1953)). The more robust LeveneTest constructor implements Levene’s test for comparing two or more variances for equality.

-
-
-
-
-

Test of correlation

-

Suppose \((X_1, Y_1), (X_2, Y_2), \dots, (X_n, Y_n)\) are jointly normally distributed. The Pearson correlation coefficient was given by:

-

\[ -r = \frac{1}{n-1} \sigma \left(\frac{X_i-\bar{X}}{s_X}\right) \cdot \left(\frac{Y_i - \bar{Y}}{s_Y}\right). -\]

-

The exact sampling distribution function of \(r\) is known and involves the parameter \(\rho\), the population correlation. Under the null hypothesis \(\rho=0\), the distribution is a \(T\)-distribution with \(n-2\) degrees of freedom. This allows the testing of \(0\) correlation, which under this assumption is a test of independence.

-

For example, to test if these two normally distributed pairs of numbers are independent:

-
x: -1.41  -0.8  -1.28  0.68   0.16  -1.28
-y: -0.34  -0.24  0.29  0.83  -0.77  -0.66
-

we have:

-
-
x = [-1.41,  -0.8, -1.28, 0.68, 0.16, -1.28]
-y = [-0.34, -0.24, 0.29, 0.83, -0.77, -0.66]
-n = length(x)
-
-r_obs = cor(x, y)
-
-2 * ccdf(TDist(n-2), abs(r_obs))
-
-
0.7244958336991354
-
-
-

For testing other values, the Fischer approximation proves useful, namely that

-

\[ -F(r) = \frac{1}{2}\ln\left(\frac{1 +r}{1-r}\right) = \tanh^{-1}(r) -\]

-

has an approximate normal distribution with mean \(F(\rho)\) and standard error \(1/\sqrt{n-3}\).

-

This can be used to generate confidence intervals, leading to a \((1-\alpha)\cdot 100\)% CI for \(\rho\) expressed through \(\tanh^{-1}(r) - z_{1-\alpha/2} SE < \tanh^{-1}(\rho) < \tanh^{-1}(r) + z_{1-\alpha/2} SE\).

-

For the previous data, a \(95\)% percent confidence interval for \(\rho\) could be constructed with:

-
-
r = cor(x, y)
-n = length(x)
-alpha = 0.05
-za = quantile(Normal(0, 1), 1 - alpha/2)
-SE = 1/sqrt(n-3)
-MOE = za * SE
-lo′, hi′ = atanh(r) .+ (-MOE, MOE)
-lo, hi = tanh(lo′), tanh(hi′)
-
-
(-0.6252789868635364, 0.9103466950735963)
-
-
-

This is very wide, as the sample size is small, leading to an \(SE\) value over \(1/2\).

-
-
-

Goodness of fit test

-

Comparing categorical counting data to a specified multinomial model is done through a Chi-squared test. The expected count in each category is \(E_i=n p_i\), the count modeled by \(X_i\) and the statistic is \(\sum (X_i-E_i)^2/E_i\).

-
-

Example 7 (Benford’s law) Data to study Benford’s law appeared in the linked article. The authors compiled data from COVID websites to get counts on the number of confirmed cases across many data sources. The first digit of each was tallied, and produced this data:

-
  1    2    3    4    5    6    7    8    9
-2863 1342 1055  916  744  673  580  461  377
-

To test if the data follow Benford’s law (\(P(X=k) = \log_{10}(k+1) - \log_{10}(k)\)) we have this as the null for \(p_k\) with an alternative of not so.

-
-
xs = [2863, 1342, 1055,  916,  744,  673,  580,  461,  377]
-
-ks = 1:9
-pks = log.(10, ks .+ 1) - log.(10, ks)
-
-χ² = ChisqTest(xs, pks)
-(pvalue=pvalue(χ²), tstat=χ².stat, df=χ².df)
-
-
(pvalue = 2.6484734636062243e-12, tstat = 71.34745744533376, df = 8)
-
-
-

This small \(p\)-value suggests the law does not exactly apply, though the general form of the law (monotonically decreasing probabilities) is certainly suggested.

-

You may be interested in applying this empirical law to the spending patterns of a certain congressman.

-
-
-

Example 8 In (Cressie and Read 1984), data on time passage and memory recall is provided. Following Haberman, a log-linear time trend is used to estimate the \(p_i\): \(\log(p_i) = \alpha + \beta\cdot i\).

-

That is, we will test:

-

\[ -H_0: \log(p_i) = \alpha + \beta \cdot i, \quad H_A: \text{not so} -\]

-

The values for \(\alpha\) and \(\beta\) are estimated from the data. Haberman used \(\alpha \approx -2.1873, \beta \approx -0.0838\). (Their method is not quite the same, but close to, fitting a linear model to \(\log(\hat{p}_i) = \alpha + \beta i\).)

-

Using this we can get the \(\chi^2\) statistic as follows, with the data read from the article:

-
-
xs = [15, 11, 14, 17, 5, 11, 10, 4, 8, 10, 7, 9, 11, 3, 6, 1, 1, 4]
-alpha, beta = -2.1873, -0.0838
-ps = exp.(alpha .+ beta*(1:18))
-ps /= sum(ps) # make sum to exactly 1
-χ² = ChisqTest(xs, ps)
-χ².stat
-
-
22.716840144539344
-
-
-

We don’t show the \(p\) value – yet – as we need to consider an adjusted degrees of freedom. There are \(s=2\) parameters estimated from the data, so the degrees of freedom of this test statistic are \(18 - 2 - 1 = 15\). The \(p\)-value is found by computing the area to the right of the observed value, giving:

-
-
1 - cdf(Chisq(18 - 2 - 1), χ².stat)
-
-
0.09033970932839053
-
-
-

This data set was used by (Cressie and Read 1984) to illustrate the power-divergence statistic, which has a varying parameter \(\lambda\). The Chi-squared test is \(\lambda = 1\); a maximum-likelihood test is \(\lambda=0\); a value of \(\lambda=2/3\) is recommended by Cressie and Read as a robust general purpose value. We can test these, as follows:

-
-
Dict(l => 1 - cdf(Chisq(18 - 2 - 1), PowerDivergenceTest(xs; lambda=l, theta0=ps).stat)
-     for l in [0, 2/3, 1, 5])
-
-
Dict{Float64, Float64} with 4 entries:
-  0.0      => 0.0560191
-  0.666667 => 0.0824898
-  5.0      => 0.00204456
-  1.0      => 0.0903397
-
-
-

The value of \(\lambda=5\) was used to show that larger values increase the size of the test statistic for this data, making the \(p\)-values smaller under the asymptotic distribution. The value (of \((X_i/E_i)\)) for \(i=13\) is nearly \(2\); powers over \(1\) make this one term dominate the value of the statistic. Something similar happens for powers much less than \(1\).

-
-
-

Example 9 (Two-way tables) Let \(X\) and \(Y\) be two categorical variables summarizing a set of observations. Their counts could be summarized in a two-way table, say, with the \(X\) levels across the top, and the \(Y\) levels down the side. If there is no relationship between \(X\) and \(Y\) we would expect the proportions in each row to be roughly the same for each level of \(X\). If \(X\) depends on \(Y\), this would be expected.

-

Put differently, if \(X\) and \(Y\) are not related, the expected count in cell row \(i\) column \(j\) would be \(n \cdot p_{ij}\) or \((n \cdot P(Y=i)) \cdot P(X=j)\), the latter first finds the expected number in the \(i\)th level of \(Y\) times the probability of the \(j\) level of \(X\). That is, under an assumption of no association, the marginal probabilities should determine the cell probabilities.

-

Let there be \(c\) levels of \(X\) and \(r\) levels of \(Y\). Viewing the table in a flat manner, there are \(k=rc\) different cells. If a multinomial model described the table, then the Chi-squared distribution would asymptotically describe the \(\chi^2\) statistic’s sampling distribution. However, as there are \(s = (r-1) + (c-1)\) parameters that would need estimating (\(r-1\) for the \(Y\) probabilities as all \(r\) must add to \(1\), for example), the degrees of freedom would be \(k-s-1 = rc - (r-1 + c-1) - 1 = (r-1)(c-1)\).

-

Suppose, a survey of many individuals was taken including questions on COVID-19 contraction and primary form of mitigation with the following data:

-
Mitigation/Contracted
-                 Yes    No
-Vaccination       10   500
-Isolation          8   250
-Face mask         15   600
-Ivermectin        20    40
-none              20   300
-

We enter this two-way table in as a matrix, and then call ChisqTest to do the analysis:

-
-
cnts = [10 500;
-        8  250;
-        15 600
-        20  40
-        20 300] # r=5, c=2 -> 5 df.
-χ² = ChisqTest(cnts)
-(pvalue=pvalue(χ²), tstat=χ².stat, df = χ².df)
-
-
(pvalue = 4.5421259053037854e-30, tstat = 143.7051915805636, df = 4)
-
-
-

The small \(p\)-value for this made-up data suggests an association between primary mitigation and chance of contraction.

-
-
-

Example 10 (Two-sample test of proportions) The methodology for two-way tables can be used for a two-sample test of proportions. Letting \(X\) count the successes and failures and \(Y\) the two different surveys.

-

Several news articles of the time discussed a divide between “red” and “blue” states and their covid rates based on a politicalization of vaccinations in the United States. Suppose data was collected on whether a person with COVID-19 needed hospitalization was cross-tabulated with their county, classified broadly as “red” or “blue”. The data is

-
Type / Hospitalization
-       Yes    No
-Red    100  1000
-Blue    50   750
-

Test the data under the hypothesis of no association against an alternative of an association. Again, the contingency table is entered as a matrix and ChisqTest called to perform the analysis:

-
-
cnts = [100 1000;
-         50  750]
-χ² = ChisqTest(cnts)
-(pvalue=pvalue(χ²), tstat=χ².stat, df = χ².df)
-
-
(pvalue = 0.023371321878153804, tstat = 5.140692640692601, df = 1)
-
-
-

We compare this to the following direct computation:

-
-
n1, n2 = ns = map(sum, eachrow(cnts))
-phat1, phat2 = cnts[:, 1] ./  ns
-phat = sum(cnts[:,1]) / sum(cnts)
-SE = sqrt(phat*(1-phat) * (1/n1 + 1/n2))
-
-Zobs = abs(phat1 - phat2)/SE  # use abs and double right tail
-
-p_value = 2 * (1 - cdf(Normal(0, 1), Zobs))
-
-
0.023371321878153273
-
-
-

One other way to analyze this question is with Fisher’s exact test. For a \(2\times 2\) table, the bottom row divides into the top and the ratio of the two resulting numbers is considered (\((a/c) / (b/d)\)). If there is no association, this ratio should be close to \(1\). For this data, the Fisher exact test yields a \(p\)-value in the same range:

-
-
pvalue(FisherExactTest(100, 1000, 50, 750)) # a,b,c,d
-
-
0.027803693593425226
-
-
-

Either way, all produced a \(p\)-value smaller than the nominal \(\alpha=0.05\) level for this manufactured data.

-
-
-
-

Likelihood ratio tests

-

Consider again a hypothesis test with a concrete alternative:

-

\[ -H_0: \mu = \mu_0, \quad H_A: \mu = \mu_1 -\]

-

Suppose the population is \(Normal(\mu, \sigma)\). We had a similar setup in the discussion on power, where for a \(T\)-test specifying three of a \(\alpha\), \(\beta\), \(n\), or an effect size allows the solving of the fourth using known facts about the \(T\)-statistic. The Neyman-Pearson lemma speaks to the uniformly most powerful test under this scenario with a single unknown parameter (the mean above, but it could also have been the standard devation, etc.).

-

This test can be realized as a likelihood ratio test, which also covers tests of more generality. Suppose the parameters being tested are called \(\theta\) which sit in some subset \(\Theta_0 \subset \Theta\). The non-directional alternative would be \(\theta\) is in \(\Theta \setminus \Theta_0\).

-

A test for this can be based on the likelihood ratio statistic:

-

\[ -\lambda = -2 \ln \frac{\sup_{\theta \in \Theta_0} L(\theta, x)}{\sup_{\theta \in \Theta} L(\theta, x)} = --2(\ln \sup_{\theta \in \Theta_0} L(\theta, x) - \ln \sup_{\theta \in \Theta} L(\theta, x)). -\]

-

The \(\sup\) is essentially the maximum value of the function over the set, which above is either the specific set of values in the null or all possible values. The ratio here is in \([0,1]\), the logarithm is then negative, the factor \(-2\) means \(\lambda\) is in \([0, \infty)\). Under some assumptions, \(\lambda\) will have an asymptotically Chi-square distribution with the degrees of freedom given by the dimensionality of \(\Theta\).

-

While only in the simple case is the likelihood ratio test guaranteed to be the most powerful, the approach is much more general than the ad hoc tests described previously and mostly agrees with them.

-

Let’s consider the case of a survey modeled by a binomial. We test \(H_0: p=1/2\) against a two-sided alternative.

-

The log-likelihood function, \(l(p,x)\), for the data is simply:

-
-
function loglik(θ, data) # both θ, data are containers
-    p = first(θ)
-    x, n = data
-    logpdf(Binomial(n, p), x)
-end
-
-
loglik (generic function with 1 method)
-
-
-

The test statistic is found by computing \(-2 (l(1/2) - l(\hat{p}))\), where \(\hat{p}\) maximizes \(l(p,x)\).

-

We use findmax to identify the maximum.

-

Suppose our data is 60 of \(100\).

-
-
dat = (x=60, n=100)
-ps = range(0, 1, length=201)
-ls = loglik.(ps, Ref(dat))  # don't broadcast over data
-l̂, i = findmax(ls)
-= ps[i]
-
-
0.6
-
-
-

We see \(\hat{p} = x/n\) (which could have been shown mathematically). The value of the log-likelihood ratio statistic can be found through:

-
-
p₀ = 1/2
-ll_obs = -2*(loglik(p₀, dat) - l̂)
-
-
4.027102710137768
-
-
-

The \(Chisq(1)\) distribution describes this statistic asymptotically. Supposing that to be valid, the \(p\)-value is computed by looking at the probability of a more extreme value under the null hypothesis, which are values larger:

-
-
1 - cdf(Chisq(1), ll_obs)
-
-
0.04477477232924443
-
-
-
-

Example 11 (The linear-regression model) The simple linear regression model was given by \(y_i = \beta_0 + \beta_1 x_i + e_i\), where for purposes of modeling, we will take \(e_i\) to have a \(Normal(0, \sigma)\) distribution. We discuss this model more formally in the subsequent chapter, but here we see we can approach the model using maximum likelihood.

-

For a given data set, we test whether \(\beta_1=0\) against a two-sided alternative.

-

Suppose the data is measured dosages of ivermectin with recovery time for COVID-19 infected patients.

-
dose (μg/kg): 100 100 200 200 400 400 600 800
-time (days) :   5   5   6   4   5   8   6   6
-

The model has \(3\) parameters \(\beta_0, \beta_1\) and \(\sigma\), the standard deviation. We have the log-likelihood function given by:

-
-
function loglik(θ, data)
-    # yᵢ ~ N(β₀ + β₁⋅x, σ) is model
-    β₀, β₁, σ = θ
-    y, x = data
-
-    ll = 0.0
-    for (yᵢ,xᵢ)  zip(y, x)
-        μᵢ = β₀ + β₁ * xᵢ
-        D = Normal(μᵢ, σ)
-        ll += logpdf(D, yᵢ)
-    end
-    ll
-end
-
-
loglik (generic function with 1 method)
-
-
-

We have the data entered as:

-
-
x = [100, 100, 200, 200, 400, 400, 600, 800]
-y = [5, 5, 6, 4, 5, 8, 6, 6];
-
-

We use the ProfileLikelihood package to model this. First we set up the model using an initial guess at the maximum likelihood estimators:

-
-
dat = (y, x)
-θ₀ = [5.0, 0.0, 1.0]
-nms = [:β₀, :β₁, :σ]
-prob = LikelihoodProblem(loglik, θ₀;
-                         data = dat,
-                         syms = nms)
-
-
-
LikelihoodProblem. In-place: true
-θ₀: 3-element Vector{Float64}
-     β₀: 5.0
-     β₁: 0.0
-     σ: 1.0
-
-
-
-

We solve the problem through:

-
-
sol = mle(prob, Optim.NelderMead())
-
-
-
LikelihoodSolution. retcode: Success
-Maximum likelihood: -11.466321890403758
-Maximum likelihood estimates: 3-element Vector{Float64}
-     β₀: 4.948881523162539
-     β₁: 0.0019318326444597892
-     σ: 1.0144493565817676
-
-
-
-

We see a small estimated value for \(\beta_1\). Is it statistically significant? For this, we choose to profile the value, and rely on the relationship between confidence intervals and significance tests: if the 95% CI for \(\beta_1\) includes \(0\), then the significance test would not reject the null.

-
-
lb = [-100.0, -100.0, 0.0]
-ub = [ 100.0,  100.0, 100.0]  # -100 < β₀, β₁ < 100; 0 < σ < 100
-resolutions = [200, 200, 200]
-param_ranges = construct_profile_ranges(sol, lb, ub, resolutions)
-prof = profile(prob, sol; param_ranges=param_ranges)
-prof[:β₁]
-
-
-
Profile likelihood for parameter β₁. MLE retcode: Success
-MLE: 0.0019318326444597892
-95.0% CI for β₁: (-0.009936997524490673, 0.01380030329166489)
-
-
-
-

We can visualize the log-likelihood over the value in the \(95\)% confidence interval with the following:

-
-
xs = range(-0.02, 0.02, length=100); ys = prof[:β₁].(xs)
-p = data((x=xs, y=ys)) * visual(Lines) * mapping(:x, :y)
-p += mapping([sol[:β₁]]) * visual(VLines)
-ci = [extrema(prof.confidence_intervals[2])...]
-p += data((x=ci, y = prof[:β₁].(ci))) * visual(Lines) * mapping(:x, :y)
-draw(p)
-
-

-
-
-

Were a significance test desired, the test statistic requires one more optimization calculuation, this time the maximum log likelihood under \(H_0\), which assumes a fixed value of \(\beta_1\):

-
-
function loglik₀(θ, data)
-    # yᵢ ~ N(β₀ + β₁⋅x, σ) is model
-    β₀, σ = θ
-    β₁ = 0.0     # H₀: β₁ = 0
-    y, x = data
-
-    ll = 0.0
-    for (yᵢ,xᵢ)  zip(y, x)
-        μᵢ = β₀ + β₁ * xᵢ
-        D = Normal(μᵢ, σ)
-        ll += logpdf(D, yᵢ)
-    end
-    ll
-end
-
-
loglik₀ (generic function with 1 method)
-
-
-
-
prob₀ = LikelihoodProblem(loglik₀, [5.0, 1.0];
-                         data = (y, x),
-                         syms = [:β₀, :σ])
-sol₀ = mle(prob₀, Optim.NelderMead())
-
-
-
LikelihoodSolution. retcode: Success
-Maximum likelihood: -12.193767351012552
-Maximum likelihood estimates: 2-element Vector{Float64}
-     β₀: 5.62501607088573
-     σ: 1.1110596750702828
-
-
-
-

The likelihood ratio statistic is computed with the difference of the respective maximums, available through the maximum property of the solution:

-
-
l = -2 * (sol₀.maximum - sol.maximum)
-
-
1.4548909212175865
-
-
-

This observed value can be turned into a \(p\)-value using the asymptotically correct \(Chisq(1)\) distribution:

-
-
1 - cdf(Chisq(1), l)
-
-
0.22774478131827325
-
-
-

There is no evidence in the data to reject the null hypothesis of no effect.

-
-
-
-BOX, G. E. P. (1953), NON-NORMALITY AND TESTS ON VARIANCES,” Biometrika, 40, 318–335. https://doi.org/10.1093/biomet/40.3-4.318. -
-
-Cressie, N., and Read, T. R. C. (1984), Multinomial goodness-of-fit tests,” Journal of the Royal Statistical Society. Series B (Methodological), [Royal Statistical Society, Wiley], 46, 440–464. -
-
-
-
-
- -
- - -
- - - - \ No newline at end of file diff --git a/LinearModels/linear-regression.html b/LinearModels/linear-regression.html deleted file mode 100644 index 96e9da2..0000000 --- a/LinearModels/linear-regression.html +++ /dev/null @@ -1,1184 +0,0 @@ - - - - - - - - - -linear-regression - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

The linear regression model

-

We discuss the linear regression model in the following using linear algebra to quickly formulate the main results. For those unfamiliar with linear algebra, its use is only to illustrate various aspects that have pre-defined methods in GLM; no user-level linear algebra is necessary for computation.

-

For this, we utilize the following packages:

-
-
using StatsBase, Distributions, GLM, HypothesisTests
-using CSV, DataFrames, CategoricalArrays, RDatasets
-using CairoMakie, AlgebraOfGraphics
-using LinearAlgebra
-
-
-

Multiple linear regression

-

The simple linear regression model related a covariate variable, \(x\), to a response variable \(Y\) through a formulation:

-

\[ -Y_i = \beta_0 + \beta_1 \cdot x_i + e_i, -\]

-

where \(\beta_0, \beta_1\) are the parameters for the model that describe the average value of the \(Y\) variable for a given \(x\) value and the random errors, \(e_i\), are assumed to be described to be a random sample from some distribution, usually \(Normal(0, \sigma)\). Some inferential results require this sample to be iid.

-

The model for multiple regression is similar, though there are \(k = 0, 1\), or more covariates accounted for in the notation:

-

\[ -Y_i = \beta_0 + \beta_1 \cdot x_{1i} + \beta_2 \cdot x_{2i} + \cdots + \beta_k \cdot x_{ki} + e_i. -\]

-

Following (Wackerly et al. 2008) we formulate the regression model using matrix algebra and quickly review their main results.

-

If there are \(n\) groups of data, then the main model matrix is the \(n\times (k+1)\) matrix:1

-

\[ -X = -\begin{bmatrix} -1 & x_{11} & x_{12} & \cdots & x_{1k}\\ -1 & x_{21} & x_{22} & \cdots & x_{2k}\\ -\vdots & \vdots & \vdots & \vdots & \vdots \\ -1 & x_{n1} & x_{n2} & \cdots & x_{nk} -\end{bmatrix}. -\]

-

The response, parameters, and errors are recorded in column vectors:

-

\[ -Y = [y_1, y_2, \cdots, y_n], \quad \beta = [\beta_0, \beta_1, \dots, \beta_k], \quad e= [e_1, e_2, \dots, e_n]. -\]

-

The collection of equations can then be written as \(Y = X\beta + e\).2

-

The notation \(\hat{Y} = X\beta\) is common, as the values \(X\beta\) are used to predict the average values of the response. The least squares problem to estimate the parameters is then to find \(\beta\) that minimizes:

-

\[ -\lVert Y - \hat{Y} \rVert^2 = \lVert Y - X\beta \rVert^2. -\]

-

Using calculus, it can be seen that any minimizer, \(\hat{\beta}\), will satisfy \(X'X\hat{\beta}=X'Y\), with \(X'\) denoting the transpose3. When written out these are called the normal equations, which, when the matrix \(X\) is non-degenerate (rank \(k+1\) in this case) can be solved algebraically by

-

\[ -\hat{\beta} = (X'X)^{-1}X' Y. -\]

-

We now assume the errors are an iid random sample from a distribution with mean \(0\) and variance \(\sigma^2\). The matrix of covariances, \(COV(\beta_i, \beta_j)\), is called the covariance matrix and denoted \(\Sigma_{\hat{\beta}\hat{\beta}}\). Under these assumptions, it can be shown to satisfy:

-

\[ -\Sigma_{\hat{\beta}\hat{\beta}} = \sigma^2 (X'X)^{-1}. -\]

-

The parameter \(\sigma^2\) can be estimated. We have the residuals are given in vector form by \(\hat{e} = Y - \hat{Y}\). Here \(\hat{Y} = \hat{H}Y\). The product \(\hat{H} = X(X'X)^{-1}X'\) is called the “hat” matrix and comes from \(\hat{Y} = X\hat{\beta} = X((X'X)^{-1}X'Y) = (X(X'X)^{-1}X')Y\).

-

The hat matrix has the property of a projection matrix taking values in an \(n\) dimensional space and projecting onto a subspace described by the columns of the \(X\) matrix. This allows a geometric interpretation of the least-squares formulation.4

-

The sum of squared residuals is:

-

\[ -\sum (Y_i - \hat{Y_i})^2 = \lVert Y - \hat{Y} \rVert^2 = \lVert (1-\hat{H})Y\rVert^2 = Y'(I-\hat{H})Y, -\]

-

where \(I\) is the diagonal matrix of all ones that acts like an identity under multiplication.

-

The expected value can be computed to get \(E(\lVert Y - \hat{Y} \rVert^2) = (n - 1 - k)\sigma^2\), which is used to estimate \(\sigma^2\):

-

\[ -s^2 = \frac{\lVert Y - \hat{Y} \rVert^2}{n-1-k} = \frac{\sum (Y_i - \hat{Y_i})^2}{n-1-k}. -\]

-

(When \(k=0\), this is the same as the sample standard deviation with \(\hat{Y_i}\) simply \(\bar{Y}\).)

-

More is known of the \(\hat{\beta}\), in particular the distribution of:

-

\[ -\frac{(\hat{\beta}-\beta) \cdot (X'X) \cdot (\hat{\beta} - \beta)}{\sigma^2}, -\]

-

is \(Chisq(k+1)\) and, if the errors are from an iid random sample with population \(Normal(0, \sigma)\), is independent of \(s^2\). In which case, the ratio is \(F\) distributed, leading to the following: a \((1-\alpha)\cdot 100\)% joint confidence interval for \(\beta\) is found with:

-

\[ -(\hat{\beta}-\beta) \cdot (X'X) \cdot (\hat{\beta}-\beta) \leq (1+k)s^2 F_{1-\alpha; 1 + k, n-1-k}. -\]

-

While the error terms, \(e\), are assumed to be independent, the residuals, \(\hat{e}\), are not so, as one large residual must be offset by other smaller ones due to the minimization of the squared residuals. The matrix of their covariances can be expressed as \(\Sigma_{\hat{e}\hat{e}} = \sigma^2 (I-\hat{H})\). The standardized residuals account for the \((I-\hat{H})\) and are given by: \(e_i/(s\sqrt{1 - \hat{H}_{ii}})\).

-

When the errors are an iid sample then the fitted values, \(\hat{Y}\), are uncorrelated with the residuals.

-

If it is assumed the error population is normal, then the least-square estimates for \(\beta\), given by \(\hat{\beta} = (X'X)^{-1}X Y\), are linear combinations of independent normal random variables, and consequently are normally distributed. (This is assuming the covariates are not random, or the \(Y\) values are conditionally independent.) For each \(i\), we have \(E(\hat{\beta}_i) = \beta_i\) and \(SE(\hat{\beta}_i) = s_{\hat{\beta}_i} = s \sqrt{c_{ii}}\), where \(c_{ii}\) is the diagonal entry of \((X'X)^{-1}\). Moreover, the \(T\)-statistic:

-

\[ -T = \frac{\hat{\beta}_i - \beta_i}{SE(\hat{\beta}_{ii})}, -\]

-

will have a \(T\)-distribution with \(n-k-1\) degrees of freedom, when the errors are iid and normally distributed. For a single estimate, \(\hat{\beta}_i \pm t_{1 - \alpha/2, n-1-k} s \sqrt{((X'X)^{-1})_{ii}}\) forms a \((1 - \alpha)\cdot 100\)% confidence interval for \(\beta_i\).

-

When the regression model is used for predicting the mean response for a given set of covariates, \(x_0\), the predictor would be \(\hat{\mu}_0 = x_0 \cdot \hat{\beta}\) (with the first value for \(x_0\) being \(1\)). The variance can be computed to give \(VAR(\hat{\mu}_0) = \sigma^2 x_0'(X'X)^{-1}x_0\), which depends on the value of \(x_0\). Confidence bands drawn by the linear() visualization for a scatterplot of data use this formula and a related one to estimate a single value, not an average value. The dependence on \(x_0\) gives the curve away from the center, \(\bar{x}\).

-

A measure of how much variation in the response is explained by the dependence on the respective covariates (the coefficient of determination) is given by \(R^2\) which is computed by

-

\[ -R^2 = 1 - \frac{SSR}{SST}, -\]

-

where \(SSR = \sum \hat{e}_i^2 = \sum (y_i - \hat{y}_i)^2\) is the sum of the squared residuals and \(SST = \sum (y_i - \bar{y})^2\) is the total sum of the squares. When the ratio \(SSR/SST\) is close to \(1\), then the model (\(\hat{y}\)) doesn’t explain much of the variation compared to the null model with all the \(\beta_i\)’s, \(i \geq 1\) being \(0\) and the sample mean of the \(y\), \(\bar{y}\), used for prediction. Conversely, when the ratio is close to \(0\), then the model explains much of the variation. By subtracting this from \(1\), as is customary, we have the interpretation that \(R^2\) explains \(R^2 \cdot 100\)% of the variation in the \(y\) values.

-

The value of \(R^2\) can be made equal to \(1\) with enough variables; the adjusted \(R^2\) value is a modification that weights \(SSR/SST\) by \((n-1)/(n-k-1)\) so using more variables (bigger \(k\)) is penalized.

-
-

Generic methods for statistical models

-

The StatsBase package defines methods for the above calculations and more. These are generic methods with similar usage for other models than the linear regression model discussed here. Table 1 lists several.

-
- - ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Table 1: Generic methods for statistical models defined in StatsBase. Those marked with a \(*\) are defined on the model property of the lm output.
MethodDescription
coefLeast squares estimates for \(\hat{\beta}_0, \hat{\beta}_1, \dots, \hat{\beta}_k\)
coefnamesNames of coefficients
stderrorStandard errors of coefficients
residualsResiduals, \(y_i - \hat{y}_i\)
fitted\(\hat{y}_i\) values
predictPredict future values using \(\hat{\beta}\)s
confintConfindence interval for estimated parameters
modelmatrixMatrix (\(X\)) used in computations
nobs\(n\)
dofConsumed degrees of freedom (matrix rank plus \(1\))
dof_residualresidual degrees of freedom. \(n-k-1\)
r2Coefficient of determination, \(1 - SSR/SST\)
adjr2Adjusted \(R^2\), \(1 - SSR/SST \cdot (n-1)/(n-1-k)\)
vcovVariance/Covariance matrix for the \(\hat{\beta}\)s
devianceResidual sum of squares, \(SSR\)
dispersion\(^*\)Estimate for \(\sigma\), \(\hat{\sigma} = \sqrt{SSR/(n-1-k)}\) or dispersion(res)/dof_residual(res)
nulldevianceTotal sum of squares \(SST = \sum(y_i - \bar{y})^2\).
loglikelihoodLog-likelihood of the model
nullloglikelihoodLog-likelihood of null model
ftest\(^*\)Compute \(F\)-test of two or more nested models
aicAkaike’s Information Criterion, \(-2\log(L) + 2(k+2)\)
bicBayesian Information Criterion, \(-2\log(L) + (k+2)\log(n)\)
-
-
-

Example 1 (Example of simple linear regression) Consider some fabricated data on dosage amount of Ivermectin and days to recovery of COVID-19 fit by a simple linear model:

-
-
x = [100, 100, 200, 200, 400, 400, 600, 800]
-y = [5, 5, 6, 4, 5, 8, 6, 6];
-res = lm(@formula(y ~ x), (; x, y))  # uses named tuple to specify data
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-y ~ 1 + x
-
-Coefficients:
-────────────────────────────────────────────────────────────────────────────
-                  Coef.  Std. Error     t  Pr(>|t|)    Lower 95%   Upper 95%
-────────────────────────────────────────────────────────────────────────────
-(Intercept)  4.94886     0.744004    6.65    0.0006   3.12835     6.76938
-x            0.00193182  0.00176594  1.09    0.3159  -0.00238928  0.00625292
-────────────────────────────────────────────────────────────────────────────
-
-
-

The output shows the estimated coefficients, \(\hat{\beta}_0\) and \(\hat{\beta}_1\). These are computed by \((X'X)^{-1}XY\):

-
-
X = modelmatrix(res)
-inv(X' * X) * X' * y
-
-
2-element Vector{Float64}:
- 4.948863636363637
- 0.0019318181818181804
-
-
-

These are also returned by the coef method, as in coef(res).

-

The default output also computes confidence intervals and performs two-sided tests of whether the parameter is \(0\). Focusing on \(\hat{\beta}_1\), we can find its standard error from \(s \sqrt{c_{ii}}\). First we compute \(s\):

-
-
n, k = length(x), 1
-= sum(eᵢ^2 for eᵢ in residuals(res)) / (n - k - 1)
-s = sqrt(s²)  # also dispersion(res.model)
-C = inv(X'*X)
-sbetas = s * sqrt.(diag(C))
-
-
2-element Vector{Float64}:
- 0.7440036635973516
- 0.0017659398031727332
-
-
-

More conveniently, these are returned by the stederror method:

-
-
stderror(res)
-
-
2-element Vector{Float64}:
- 0.7440036635973516
- 0.0017659398031727338
-
-
-

These are also the square root of the diagonal of the covariance matrix, \(\Sigma_{\hat{\beta}\hat{\beta}}\), computed by the vcov method:

-
-
Σᵦᵦ = vcov(res)
-
-
2×2 Matrix{Float64}:
-  0.553541    -0.00109149
- -0.00109149   3.11854e-6
-
-
-
-
sqrt.(diag(Σᵦᵦ))
-
-
2-element Vector{Float64}:
- 0.7440036635973516
- 0.0017659398031727338
-
-
-

The \(T\)-statistic for \(H_0: \beta_1 = 0\) is then

-
-
β̂₁ = coef(res)[2]
-SE₁ = stderror(res)[2]
-T_obs = (β̂₁ - 0) / SE₁
-
-
1.0939320685492355
-
-
-

The \(p\)-value is then found directly with:

-
-
2 * ccdf(TDist(n-k-1), T_obs)
-
-
0.315945937293384
-
-
-

This computation is needed were there different assumed values than \(\beta_1 = 0\) for the null, though this particular \(p\)-value is included in the default display of res.

-

The confidence intervals are of the form \(\hat{\beta}_i \pm t_{1-\alpha/2; n-k-1} \cdot SE(\hat{\beta}_i)\). We find one for the intercept term, \(\beta_0\):

-
-
alpha = 0.05
-ta = quantile(TDist(n-k-1), 1 - alpha/2)
-β̂₀ = coef(res)[1]
-SE₀ = stderror(res)[1]
-β̂₀ .+ ta * [-SE₀, SE₀]
-
-
2-element Vector{Float64}:
- 3.128352254612003
- 6.769375018115272
-
-
-

The confint method will also compute these, returning the values as rows in a matrix:

-
-
confint(res)
-
-
2×2 Matrix{Float64}:
-  3.12835     6.76938
- -0.00238928  0.00625292
-
-
-

We compute the confidence interval for \(\hat{\mu}\) when \(x=500\) using the variance formula above.

-
-
x0 = [1, 500]
-μ̂ = predict(res, (x=[500],))[1]  # also  dot(inv(X'*X)*X'*y, x0)
-SE = s * sqrt(x0' * inv(X' * X) * x0)
-ci = μ̂ .+ ta * [-SE, SE]
-
-
2-element Vector{Float64}:
- 4.711829667120805
- 7.117715787424649
-
-
-

We can visualize (Figure 1) with the following commands:

-
-
layers = (visual(Scatter) + linear(; interval=:confidence))
-p = data((;x, y)) * layers * mapping(:x, :y)
-p += data((x=[500, 500], y=ci)) * visual(Lines) * mapping(:x, :y)
-draw(p)
-
-
-
-

-

Figure 1: Illlustration of linear regression model with confidence band drawn. The vertical line is computed directly for the value of \(x=500\).

-
-
-
-
-

The value of \(R^2\) can be computed directly:

-
-
1 - sum(eᵢ^2 for eᵢ in residuals(res)) / sum((yᵢ - mean(y))^2 for yᵢ in y)
-
-
0.166283084004603
-
-
-

This can also be computed using several of the methods defined for model outputs by GLM:

-
-
r2(res), 1 - deviance(res)/nulldeviance(res)
-
-
(0.166283084004603, 0.166283084004603)
-
-
-

Whichever way, for this model a low \(R^2\) implies the model does not explain much of the variance in the response.

-
-
-

Example 2 (Multiple regression example) We give an example of multiple linear regression using a data set on various cereal boxes in a US grocery store.

-
-
cereal = dataset("MASS", "UScereal")
-first(cereal, 2)
-
- -
2×12 DataFrame
RowBrandMFRCaloriesProteinFatSodiumFibreCarboSugarsShelfPotassiumVitamins
StringCat…Float64Float64Float64Float64Float64Float64Float64Int32Float64Cat…
1100% BranN212.12112.12123.0303393.93930.30315.151518.18183848.485enriched
2All-BranK212.12112.12123.0303787.87927.272721.212115.15153969.697enriched
-
-
-

The data set collected numerous variables, here we consider numeric ones:

-
-
names(cereal, Real) |> permutedims
-
-
1×9 Matrix{String}:
- "Calories"  "Protein"  "Fat"  "Sodium"  …  "Sugars"  "Shelf"  "Potassium"
-
-
-

The initial model we consider has Calories as a response, and several covariates:

-
-
fm = @formula(Calories ~ Protein + Fat + Sodium + Carbo + Sugars)
-res = lm(fm, cereal)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-Calories ~ 1 + Protein + Fat + Sodium + Carbo + Sugars
-
-Coefficients:
-────────────────────────────────────────────────────────────────────────────────
-                    Coef.  Std. Error      t  Pr(>|t|)    Lower 95%    Upper 95%
-────────────────────────────────────────────────────────────────────────────────
-(Intercept)  -18.9515       3.61524    -5.24    <1e-05  -26.1856     -11.7175
-Protein        3.99092      0.598282    6.67    <1e-08    2.79376      5.18808
-Fat            8.86319      0.803833   11.03    <1e-15    7.25472     10.4717
-Sodium         0.00266192   0.0107093   0.25    0.8046   -0.0187674    0.0240913
-Carbo          4.91706      0.162811   30.20    <1e-36    4.59128      5.24284
-Sugars         4.20214      0.216049   19.45    <1e-26    3.76983      4.63446
-────────────────────────────────────────────────────────────────────────────────
-
-
-

The output shows what might have been anticipated: there appears to be no connection between Sodium and Calories, though were this data on dinner foods that might not be the case. The \(T\)-test displayed for Sodium is a test of whether the slope based on Sodium is \(0\) – holding the other variables constant – and the large \(p\)-value would lead us to accept that hypotheses.

-

We drop this variable from the model and refit:

-
-
res = lm(@formula(Calories ~ Protein + Fat + Carbo + Sugars), cereal)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-Calories ~ 1 + Protein + Fat + Carbo + Sugars
-
-Coefficients:
-─────────────────────────────────────────────────────────────────────────
-                 Coef.  Std. Error      t  Pr(>|t|)  Lower 95%  Upper 95%
-─────────────────────────────────────────────────────────────────────────
-(Intercept)  -18.7698     3.51272   -5.34    <1e-05  -25.7963   -11.7433
-Protein        4.05056    0.543752   7.45    <1e-09    2.9629     5.13823
-Fat            8.85889    0.797339  11.11    <1e-15    7.26398   10.4538
-Carbo          4.92466    0.158656  31.04    <1e-37    4.6073     5.24202
-Sugars         4.21069    0.211619  19.90    <1e-27    3.78739    4.634
-─────────────────────────────────────────────────────────────────────────
-
-
-

How to interpret this? Each coefficient (save the intercept) measures the predicted change in mean number of calories for a \(1\)-unit increase holding the other variables fixed. For example, it is suggested that adding 1 additional unit of protein holding the other variables constant would add nearly 4 calories per serving, on average.

-
-
-

Example 3 (Polynomial regression) Dickey provides an analysis of Galileo’s falling ball data. Galileo rolled a ball down an elevated ramp at certain distances, the ball then jumped down a certain distance that varied depending on the height of the ramp. The collected data is:

-
-
release = [1000, 800, 600, 450, 300, 200, 100]
-horizontal_distance = [573, 534, 495, 451, 395, 337, 253]
-galileo = DataFrame(; release, horizontal_distance)
-first(galileo, 3)
-
- -
3×2 DataFrame
Rowreleasehorizontal_distance
Int64Int64
11000573
2800534
3600495
-
-
-

With an assumption that the horizontal distance was related to \(v_xt\) and \(t\) was found by solving for \(0 = h - (1/2)gt^2\), we might expect \(h\) and \(t\) to be quadratically related. We consider, somewhat artifcially, the release height modeled linearly by the horizontal distance:

-
-
res = lm(@formula(y ~ x), (y=galileo.release, x=galileo.horizontal_distance))
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-y ~ 1 + x
-
-Coefficients:
-─────────────────────────────────────────────────────────────────────────────
-                  Coef.  Std. Error      t  Pr(>|t|)    Lower 95%   Upper 95%
-─────────────────────────────────────────────────────────────────────────────
-(Intercept)  -713.262    156.449     -4.56    0.0061  -1115.43     -311.098
-x               2.77908    0.350392   7.93    0.0005      1.87837     3.67979
-─────────────────────────────────────────────────────────────────────────────
-
-
-

Without much effort, the small \(p\)-value would lead one to conclude the linear term is statistically significant. But Galileo might have expected a quadratic relationship and a modern reader might, as well, viewing Figure 2, such as modeled by the following:

-
-
res₂ = lm(@formula(y ~ x + x^2), (y=galileo.release, x=galileo.horizontal_distance))
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-y ~ 1 + x + :(x ^ 2)
-
-Coefficients:
-────────────────────────────────────────────────────────────────────────────────────
-                    Coef.     Std. Error      t  Pr(>|t|)     Lower 95%    Upper 95%
-────────────────────────────────────────────────────────────────────────────────────
-(Intercept)  600.054       110.543         5.43    0.0056  293.136       906.971
-x             -4.00484       0.55582      -7.21    0.0020   -5.54805      -2.46164
-x ^ 2          0.00818074    0.000665952  12.28    0.0003    0.00633176    0.0100297
-────────────────────────────────────────────────────────────────────────────────────
-
-
-

The rules of @formula parse the above as adding a variable x^2 to the model. Alternatively, the data frame could have been transformed to produce that variable. The output shows the test of \(\beta_2=0\) would be rejected for reasonable values of \(\alpha\).

-
-
-
-
-

-

Figure 2: Scatter plot of falling-ball data of Galileo with a linear model fit. The curve suggests a quadratic model.

-
-
-
-
-
-
-
-
-

Categorical covariates

-

The linear regression model is more flexible than may appear on first introduction through simple regression.

-

For example, the regression model when there are no covariates is just a one-sample \(T\)-test, as seen from this example where a two-sided test of \(0\) mean is carried out.

-
-
y = [-0.2, 1.9, 2.7, 2.6, 1.5, 0.6]
-lm(@formula(y ~ 1), (;y)) # using a named tuple for the data
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-y ~ 1
-
-Coefficients:
-──────────────────────────────────────────────────────────────────────
-               Coef.  Std. Error     t  Pr(>|t|)  Lower 95%  Upper 95%
-──────────────────────────────────────────────────────────────────────
-(Intercept)  1.51667    0.465773  3.26    0.0225   0.319359    2.71397
-──────────────────────────────────────────────────────────────────────
-
-
-

The formula usually has an implicit intercept, but here with no covariates listed, it must be made explicit. Compare the values with the following:

-
-
OneSampleTTest(y)
-
-
One sample t-test
------------------
-Population details:
-    parameter of interest:   Mean
-    value under h_0:         0
-    point estimate:          1.51667
-    95% confidence interval: (0.3194, 2.714)
-
-Test summary:
-    outcome with 95% confidence: reject h_0
-    two-sided p-value:           0.0225
-
-Details:
-    number of observations:   6
-    t-statistic:              3.2562360146885347
-    degrees of freedom:       5
-    empirical standard error: 0.46577295374940403
-
-
-

Further, the two-sample \(T\)-test (with equal variances assumed) can be performed through the regression model. After tidying the data, we fit a model:

-
-
y1 = [5, 4, 6, 7]
-y2 = [7, 6, 5, 4, 5, 6, 7]
-df = DataFrame(group=["g1", "g2"], value=[y1, y2])
-d = flatten(df, [:value])
-
-res = lm(@formula(value ~ group), d)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-value ~ 1 + group
-
-Coefficients:
-───────────────────────────────────────────────────────────────────────
-                Coef.  Std. Error     t  Pr(>|t|)  Lower 95%  Upper 95%
-───────────────────────────────────────────────────────────────────────
-(Intercept)  5.5         0.58757   9.36    <1e-05    4.17083    6.82917
-group: g2    0.214286    0.736558  0.29    0.7777   -1.45192    1.88049
-───────────────────────────────────────────────────────────────────────
-
-
-

We can compare the computed values to those computed a different way:

-
-
EqualVarianceTTest(y2, y1)
-
-
Two sample t-test (equal variance)
-----------------------------------
-Population details:
-    parameter of interest:   Mean difference
-    value under h_0:         0
-    point estimate:          0.214286
-    95% confidence interval: (-1.452, 1.88)
-
-Test summary:
-    outcome with 95% confidence: fail to reject h_0
-    two-sided p-value:           0.7777
-
-Details:
-    number of observations:   [7,4]
-    t-statistic:              0.2909286827258563
-    degrees of freedom:       9
-    empirical standard error: 0.7365575380122867
-
-
-

However, some comments are warranted. We would have found a slightly different answer (a different sign) had we done EqualVarianceTTest(y1, y2). This is because a choice is made if we consider \(\bar{y}_1-\bar{y}_2\) or \(\bar{y}_2 - \bar{y}_1\) in the statistic.

-

In the use of the linear model, there is a new subtlety – the group variable is categorical and not numeric. A peek at the model matrix (modelmatrix(res)) will show that the categorical variable was coded with a \(0\) for each g1 and \(1\) for each g2. The details are handled by the underlying StatsModels package which first creates a ModelFrame which takes a formula and the data; ModelMatrix then creates the matrix, \(X\). The call to ModelFrame allows a specification of contrasts. The above uses the DummyCoding, which picks a base level ("g1" in this case) and then creates a variable for each other level, these variables having values either being 0 or 1, and 1 only when the factor has that level. Using the notation \(1_{j}(x_i)\) for this, we have the above call to lm fits the model \(y_i = \beta_0 + \beta_1 \cdot 1_{\text{g2}}(x_i) + e_i\) and the model matrix shows this (2nd row below):

-
-
modelmatrix(res) |> permutedims # turned on side to save page space
-
-
2×11 Matrix{Float64}:
- 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0
- 0.0  0.0  0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0
-
-
-

The model can’t be \(y_i = \beta_0 + \beta_1 \cdot 1_{\text{g2}}(x_i) + \beta_2 \cdot 1_{\text{g1}}(x_i) + e_i\), as there wouldn’t be a unique solution (the model \(y_i = (\beta_0 + \beta_2) + \beta_2\cdot 1_{\text{g2}}(x_i)\) would also fit, so the parameters would not be identifiable). More mathematically, the model matrix, \(X\), would have 3 columns, but one of them could be expressed as a sum of the other 2. This would mean \(X\) would not have full rank and the least-squares formula wouldn’t have the form it does.

-

To fit a model with different contrasts, the lm function has a contrast keyword argument.

-

The above formulation does not require the factor to have just 2 levels; if there are \(k\) levels, then \(k-1\) variables are formed in the model.

-
-

Example 4 (Categorical covariates example) Consider the cereal data set. The Shelf variable is numeric, but really it should be considered categorical for any study using a linear model, as differences between shelf 1 and 2 and shelf 2 and 3 should not be expected to be uniform (as they would were the values treated numerically). The following first ensures shelf is categorical, then fits a model on how the shelf placement impacts the number of calories:

-
-
cereal.shelf = categorical(cereal.Shelf)
-res = lm(@formula(Calories ~ shelf), cereal)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-Calories ~ 1 + shelf
-
-Coefficients:
-───────────────────────────────────────────────────────────────────────
-                Coef.  Std. Error     t  Pr(>|t|)  Lower 95%  Upper 95%
-───────────────────────────────────────────────────────────────────────
-(Intercept)  119.477      13.3488  8.95    <1e-12    92.7936   146.161
-shelf: 2      10.3388     18.878   0.55    0.5859   -27.3979    48.0754
-shelf: 3      60.6692     16.9939  3.57    0.0007    26.6989    94.6394
-───────────────────────────────────────────────────────────────────────
-
-
-

The \(p\)-value for shelf 2 is consistent with there being no difference between shelf 1 and 2, but that of shelf 3 (counting from the floor) is significantly different from shelf 1 and would be interpreted as having 60 additional calories over shelf 1. (Which fits the expectation that the lowest-shelf traditionally holds the least sold cereals, hence the most healthy in 1993 when this data was collected).

-

We can check that the model matrix has \(2\) variables a few ways: directly from the size (with first column being the intercept), and indirectly by the residual degrees of freedom:

-
-
size(modelmatrix(res)), nobs(res) - dof_residual(res) - 1 # dof_residual = n - k - 1
-
-
((65, 3), 2.0)
-
-
-

The omnibus \(F\)-test is a statistical test for a null hypothesis that \(\beta_i=0\) for all \(i\) except \(i=0\). It is implemented in the ftest method of GLM. It requires fitting the null model of just a constant, which we do with:

-
-
res₀ = lm(@formula(Calories ~ 1), cereal)  # null model
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-Calories ~ 1
-
-Coefficients:
-───────────────────────────────────────────────────────────────────────
-               Coef.  Std. Error      t  Pr(>|t|)  Lower 95%  Upper 95%
-───────────────────────────────────────────────────────────────────────
-(Intercept)  149.408     7.74124  19.30    <1e-27    133.943    164.873
-───────────────────────────────────────────────────────────────────────
-
-
-

The test takes the model, which is stored in the model property:

-
-
ftest(res.model, res₀.model)
-
-
F-test: 2 models fitted on 65 observations
-────────────────────────────────────────────────────────────────────────
-     DOF  ΔDOF          SSR        ΔSSR      R²      ΔR²      F*   p(>F)
-────────────────────────────────────────────────────────────────────────
-[1]    4        198860.3072              0.2023                         
-[2]    2    -2  249295.4943  50435.1871  0.0000  -0.2023  7.8623  0.0009
-────────────────────────────────────────────────────────────────────────
-
-
-

Ignoring for now all but the bottom right number which gives the \(p\)-value, we see that this null model would be rejected.

-
-
-
-

Interactions

-

An interaction is when the effect of one explanatory variable depends on the values of a different explanatory variable. We see such a case in the following example.

-
-

Example 5 The ToothGrowth data set is included in base R and summarizes an experiment on the effect of vitamin C on tooth growth in guinea pigs. Each of the 60 animals in the study received one of three dose levels of vitamin C (0.5, 1, and 2 mg/day) by one of two delivery methods, orange juice or ascorbic acid. We load the data set using RDatasets:

-
-
ToothGrowth = dataset("datasets", "ToothGrowth")
-first(ToothGrowth, 2)
-
- -
2×3 DataFrame
RowLenSuppDose
Float64Cat…Float64
14.2VC0.5
211.5VC0.5
-
-
-

Dose is seen to be stored as a numeric variable (Float64), but we treat it as a categorical variable in the following. The table shows that the 6 different treatment pairs were tested on 10 animals.

-
-
ToothGrowth.Dose = categorical(ToothGrowth.Dose)
-combine(groupby(ToothGrowth, 2:3), nrow)
-
- -
6×3 DataFrame
RowSuppDosenrow
Cat…Cat…Int64
1OJ0.510
2OJ1.010
3OJ2.010
4VC0.510
5VC1.010
6VC2.010
-
-
-

Figure 3 shows a violinplot with sides reflecting the distribution of the :Supp variable. A quick glance suggests that there may be some effect due to the dosage amount and a difference between the OJ and VC delivery.

-
-
-
-
-

-

Figure 3: ToothGrowth data set

-
-
-
-
-

We proceed to fit the additive model where Supp introduces one variable, and Dose two:

-
-
res = lm(@formula(Len ~ Supp + Dose), ToothGrowth)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-Len ~ 1 + Supp + Dose
-
-Coefficients:
-──────────────────────────────────────────────────────────────────────
-              Coef.  Std. Error      t  Pr(>|t|)  Lower 95%  Upper 95%
-──────────────────────────────────────────────────────────────────────
-(Intercept)  12.455     0.98828  12.60    <1e-17   10.4752    14.4348
-Supp: VC     -3.7       0.98828  -3.74    0.0004   -5.67976   -1.72024
-Dose: 1.0     9.13      1.21039   7.54    <1e-09    6.7053    11.5547
-Dose: 2.0    15.495     1.21039  12.80    <1e-17   13.0703    17.9197
-──────────────────────────────────────────────────────────────────────
-
-
-

The small \(p\)-values support the visual observation that there are differences. The value for “Supp: VC”, for instance, indicates that holding the dose equal, administering the dosage through citamin C and not ascorbic acid had a negative effect of \(-3.7\) units on the predicted average tooth length.

-

Visually, the distribution of the VC variable seems to depend on the dosage. Perhaps there is an interaction.

-

For this data we can fit a model

-

\[\begin{align*} -y_i & = \beta_0 + \\ - & \beta_1 \cdot 1_{\text{VC}}(\text{Supp}_i) + \\ - & \beta_2 \cdot 1_{1.0}(\text{Dose}_i) + \beta_3 \cdot 1_{2.0}(\text{Dose}_i) + \\ - & \beta_4 \cdot 1_{\text{VC}, 1.0}(\text{Supp}_i, \text{Dose}_i) + \beta_5 \cdot 1_{\text{VC}, 2.0}(\text{Supp}_i, \text{Dose}_i) + e_i -\end{align*}\]

-

The additional terms account for cases where, say, Supp = VC and Dose = 1.0.

-

Interactions are specified in the modeling formula through *. (Which when used also includes the additive terms without interactions. Plain interactions are specified with &.). The model is:

-
-
resᵢ = lm(@formula(Len ~ Supp * Dose), ToothGrowth)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-Len ~ 1 + Supp + Dose + Supp & Dose
-
-Coefficients:
-──────────────────────────────────────────────────────────────────────────────
-                      Coef.  Std. Error      t  Pr(>|t|)  Lower 95%  Upper 95%
-──────────────────────────────────────────────────────────────────────────────
-(Intercept)           13.23     1.14835  11.52    <1e-15  10.9277     15.5323
-Supp: VC              -5.25     1.62402  -3.23    0.0021  -8.50596    -1.99404
-Dose: 1.0              9.47     1.62402   5.83    <1e-06   6.21404    12.726
-Dose: 2.0             12.83     1.62402   7.90    <1e-09   9.57404    16.086
-Supp: VC & Dose: 1.0  -0.68     2.29671  -0.30    0.7683  -5.28462     3.92462
-Supp: VC & Dose: 2.0   5.33     2.29671   2.32    0.0241   0.725381    9.93462
-──────────────────────────────────────────────────────────────────────────────
-
-
-

As expected from the graph, the \(p\)-value for the “Supp: VC & Dose: 2.0” case is significant.

-

As before, an \(F\) test can test the difference between the model with and without the interaction:

-
-
ftest(res.model, resᵢ.model)
-
-
F-test: 2 models fitted on 60 observations
-───────────────────────────────────────────────────────────────────
-     DOF  ΔDOF       SSR       ΔSSR      R²     ΔR²      F*   p(>F)
-───────────────────────────────────────────────────────────────────
-[1]    5        820.4250             0.7623                        
-[2]    7     2  712.1060  -108.3190  0.7937  0.0314  4.1070  0.0219
-───────────────────────────────────────────────────────────────────
-
-
-

The small \(p\)-value suggests the interaction is statistically significant.

-
-
-
-

F test

-

Consider the linear regression model with parameters \(\beta\) and a significance test with some constraint on the parameters (e.g. \(\beta_1 = 0\) or \(\beta_1 = \beta_2\)). Suppose the error terms are an iid random sample from a \(Normal(0, \sigma)\) distribution. The a test of \(H_0\) against an alternative of not \(H_0\) can be carried out by considering the likelihood ratio statistic. The likelihood function for a set of parameters \(\beta\) is:

-

\[\begin{align*} -L(\beta, \sigma, x) &= \prod_{i=1}^n \frac{1}{(2\pi\sigma^2)^{n/2}} e^{-\frac{1}{2\sigma^2}(y_i - \hat{y}_i)^2} -\\ -&=\frac{1}{(2\pi)^{n/2}} \frac{1}{\sigma^n} e^{-\frac{1}{2\sigma^2}\sum_i(y_i - \hat{y}_i)^2}, -\end{align*}\]

-

where \(\hat{y}_i = X\beta\) for some \(X\) related to the data. As \(e^{-x}\) is decreasing, \(L\) is maximized in \(\beta\) when \(\sum_i (y_i - \hat{y}_i)^2\) is minimimized (a least squares estimate), say at \(m(\hat{\beta})\). In terms of \(\sigma\) we take a logarithm and seek to maximize:

-

\[ --\frac{n}{\sigma}\ln(2\pi) - n \ln(\sigma) - \frac{1}{2\sigma^2} m(\hat{\beta}). -\]

-

This occurs at

-

\[ -\hat{\sigma^2} = \frac{m(\hat{\beta})}{n} = \frac{1}{n} SSR, -\]

-

where \(SSR\) indicates the sum of the squared residuals, \(y_i - \hat{y}_i\). (This is a biased estimate, as the divisor does not account for the degrees of freedom.)

-

The log-likelihood ratio statistic considers the two models: the restricted one under \(H_0\) and the unrestricted one. This simplifies to

-

\[ -\lambda = -2 \ln \frac{L_0}{L} = n \ln(\frac{SSR_0}{SSR}), -\]

-

with \(SSR_0\) being the sum of the squared residuals under \(H_0\) and \(SSR\) the sum of the squared residuals under the full model, which necessarily is smaller than \(SSR_0\). The asymptotic distribution is \(Chisq(k-p)\) where \(p\) variables are free in \(H_0\).

-

The above says if \(SSR_0/SSR\) is sufficiently large it is statistically significant. Algebraically, the same thing can be said about

-

\[ -F = \frac{n-k-1}{k-p} \cdot \frac{SSR_0 - SSR}{SSR} = -\frac{(SSR_0 - SSR)/(k-p)}{SSR/(n-k-1)}. -\]

-

The distribution of \(SSR/(n-k-1)\) is \(Chisq(n-k-1)\). However, under these assumptions and under the null hypothesis, by Cochran’s theorem \(SSR_0- SSR\) is independent of \(SSR\) and \((SSR_0-SSR)/(k-p)\) is \(Chisq(k-p)\). That is \(F\) has a \(FDist(n-k-1, k-p)\) distribution. (Not asymptotically.)

-

This statistic is used by ftest to compare two nested models. Nested means the parameters in the reduced model are related to those in the full model; no new ones are introduced.

-
-

Example 6 Consider again the output of the last call to ftest which checked for an interaction between the Supp and Dose variables in the ToothGrowth data:

-
-
res₀ = lm(@formula(Len ~ Supp + Dose), ToothGrowth)
-res  = lm(@formula(Len ~ Supp * Dose), ToothGrowth)
-ftest(res₀.model, res.model)
-
-
F-test: 2 models fitted on 60 observations
-───────────────────────────────────────────────────────────────────
-     DOF  ΔDOF       SSR       ΔSSR      R²     ΔR²      F*   p(>F)
-───────────────────────────────────────────────────────────────────
-[1]    5        820.4250             0.7623                        
-[2]    7     2  712.1060  -108.3190  0.7937  0.0314  4.1070  0.0219
-───────────────────────────────────────────────────────────────────
-
-
-

The null hypothesis is \(H_0: \beta_4 = \beta_5 = 0\). The full model has \(k=5\), the null has \(p=3\). The reported degrees of freedom is the consumed degrees of freedom which is this number of (linearly independent) columns in the model matrix (\(k+1\)) plus \(1\). The sum of squares can be computed directly or through the deviance method:

-
-
SSR₀, SSR = deviance(res₀), deviance(res) # or, say, sum(residuals(res₀).^2)
-
-
(820.4250000000001, 712.1059999999999)
-
-
-

The difference between the two is the numerator of the \(F\) statistic when divided by \(2 = 5-3\) (or \(7-5\)). The denominator should be \(SSR/(n-k-1)\):

-
-
((SSR₀ - SSR)/(5 - 3)) / (SSR / (60 - 1 - 5))
-
-
4.1069910940225265
-
-
-

The degrees of freedom (\(n-1-k\)) is also calculated by

-
-
dof_residual(res)
-
-
54.0
-
-
-

The ftest can test more than two models. For example, suppose we test the null model with just an intercept, as in:

-
-
resᵢ = lm(@formula(Len ~ 1), ToothGrowth)
-ftest(resᵢ.model, res₀.model, res.model)
-
-
F-test: 3 models fitted on 60 observations
-──────────────────────────────────────────────────────────────────────
-     DOF  ΔDOF        SSR        ΔSSR      R²     ΔR²       F*   p(>F)
-──────────────────────────────────────────────────────────────────────
-[1]    2        3452.2093              0.0000                         
-[2]    5     3   820.4250  -2631.7843  0.7623  0.7623  59.8795  <1e-16
-[3]    7     2   712.1060   -108.3190  0.7937  0.0314   4.1070  0.0219
-──────────────────────────────────────────────────────────────────────
-
-
-

The output here has two \(p\)-values, the first testing if the additive model is statistically significant (with a very small \(p\)-value), the second testing, as mentioned, if the model with interaction is statistically significant compared to the additive model.

-
-
-

Example 7 We borrow an example from (Faraway 2004) to illustrate how the \(F\)-test can be used to test a null hypothesis of \(H_0: \beta_i = \beta_j\).

-

The dataset is in the datasets package of R:

-
-
savings = dataset("datasets", "LifeCycleSavings")
-first(savings, 2)
-
- -
2×6 DataFrame
RowCountrySRPop15Pop75DPIDDPI
String15Float64Float64Float64Float64Float64
1Australia11.4329.352.872329.682.87
2Austria12.0723.324.411507.993.93
-
-
-

We fit the full model for SR through:

-
-
res = lm(@formula(SR ~ Pop15 + Pop75 + DPI + DDPI), savings)
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-SR ~ 1 + Pop15 + Pop75 + DPI + DDPI
-
-Coefficients:
-─────────────────────────────────────────────────────────────────────────────────
-                    Coef.   Std. Error      t  Pr(>|t|)    Lower 95%    Upper 95%
-─────────────────────────────────────────────────────────────────────────────────
-(Intercept)  28.5661       7.35452       3.88    0.0003  13.7533      43.3788
-Pop15        -0.461193     0.144642     -3.19    0.0026  -0.752518    -0.169869
-Pop75        -1.6915       1.0836       -1.56    0.1255  -3.87398      0.490983
-DPI          -0.000336902  0.000931107  -0.36    0.7192  -0.00221225   0.00153844
-DDPI          0.409695     0.196197      2.09    0.0425   0.0145336    0.804856
-─────────────────────────────────────────────────────────────────────────────────
-
-
-

A test of \(H_0: \beta_1 = \beta_2\) is done by preparing a variable Pop15 + Pop75 (rather than a modification to the formula):

-
-
res1575 = lm(@formula(SR ~ Pop1575 + DPI + DDPI),
-             transform(savings, [:Pop15, :Pop75] => (+) => :Pop1575))
-
-
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
-
-SR ~ 1 + Pop1575 + DPI + DDPI
-
-Coefficients:
-──────────────────────────────────────────────────────────────────────────────────
-                    Coef.   Std. Error      t  Pr(>|t|)    Lower 95%     Upper 95%
-──────────────────────────────────────────────────────────────────────────────────
-(Intercept)  21.6093       4.88336       4.43    <1e-04  11.7796      31.439
-Pop1575      -0.333633     0.103868     -3.21    0.0024  -0.542708    -0.124558
-DPI          -0.000845101  0.000844351  -1.00    0.3221  -0.00254469   0.000854489
-DDPI          0.390965     0.196871      1.99    0.0530  -0.00531671   0.787247
-──────────────────────────────────────────────────────────────────────────────────
-
-
-

The ftest then can be applied:

-
-
ftest(res.model, res1575.model)
-
-
F-test: 2 models fitted on 50 observations
-──────────────────────────────────────────────────────────────────
-     DOF  ΔDOF       SSR     ΔSSR      R²      ΔR²      F*   p(>F)
-──────────────────────────────────────────────────────────────────
-[1]    6        650.7130           0.3385                         
-[2]    5    -1  673.6275  22.9145  0.3152  -0.0233  1.5847  0.2146
-──────────────────────────────────────────────────────────────────
-
-
-

The large \(p\) value suggests no reason to reject this null.

-
-
-
-Faraway, J. J. (2004), Linear models with r, hapman & Hall/CRC. -
-
-Wackerly, D., Mendenhall, W., and Scheaffer, R. L. (2008), Mathematical statistics with applications, Cengage. -
-
-
-
-
-
-
    -
  1. A more general parameterization is to just use \(n-p\) where \(p\) is the rank of the model matrix. This parameterization covers models without an intercept.↩︎

  2. -
  3. Alternatively, the multivariate normal distribution can be used to write \(Y \sim Normal(X\beta, \sigma^2 I)\), where \(I\) is the identity matrix. Matrices, as used here, compactly represent multiple equations and through matrix algebra allow symbolic rearrangements. Needed for this discussion are the notions of matrix addition, which requires similar shaped matrices; matrix multiplication, which requires a matching of the number of columns of one matrix with the number of rows of the other; matrix inverses, which require square-shaped matrices and allow the solving of \(Ax=b\) formally with \(x=A^{-1}b\), though in practice more computationally efficient means are used; matrix transposes, which interchange rows for columns; and a vector norm, which is a generalization of the distance formula.↩︎

  4. -
  5. In Julia, the ' notation for a matrix denotes the adjoint of the matrix, which differs from the transpose for complex valued matrices.↩︎

  6. -
  7. This geometric interpretation of projection gives insight into the presence of an \(F\) statistic later in this discussion. The normal equations are so named, as the imply the residual vector, \(Y - \hat{Y}\) is perpendicular (also known as normal) to the columns, as vectors, of \(X\).↩︎

  8. -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/_quarto.yml b/_quarto.yml index 47bf2d0..18189b5 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -1,8 +1,9 @@ -version: "0.0.4" -jupyter: julia-1.9 - -Project: +project: type: book + output-dir: _book + +version: "0.0.4" +jupyter: julia-1.10 book: title: "Using Julia for Introductory Statistics" diff --git a/index.html b/index.html deleted file mode 100644 index 57de8cf..0000000 --- a/index.html +++ /dev/null @@ -1,407 +0,0 @@ - - - - - - - - - -index - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

Julia introduction

-

This is a collection of notes for using Julia for introductory statistics.

-

In case you haven’t heard, Julia is an open-source programming language suitable for many tasks, like scientific programming. It is designed for high performance – Julia programs compile on the fly to efficient native code. Julia has a relatively easy to learn syntax for many tasks, certainly no harder to pick up than R and Python, widely used scripting languages for the tasks illustrated herein.

-

Why these notes on introductory statistics? No compelling reason save I had done something similar for R when R was a fledgling S-Plus clone. No more, R is a juggernaut, and it is almost certain Julia will never replace R as the programming langauage of choice for statistics. Besides, Julia users can already interface with R quite easily through RCall. However, there are some reasons that Julia could be a useful language when learning basic inferential statistics, especially if other real strengths of the Julia ecosystem were needed. So these notes show how Julia can be used for these tasks, and, hopefully, shows that it works pretty well.

-

There are some great books published about using Julia (Bezanson et al. 2017) with data science, within which much of this material is covered. For example, (Kamiński 2022) is a quite thorough treatmeant, (Storopoli et al. 2021) is very well done, (Nazarathy and Klok 2021) covers topics here (cf. the JuliaCon Workshop). The quarto book Embrace Uncertainty (Alday et al. 2022) covers the more advanced topic of Mixed-effects Models in Julia. Nothing here couldn’t be found in those resources, these notes are just an introduction.

-
-
-
- -
-
-Contribute -
-
-
-

These notes are a work in progress. Feel free to click the “edit this page” button or report an issue.

-
-
-
-

Installing and running Julia

-

Julia can be downloaded from julialang.org. The language is evolving rapidly. The latest official release is recommended. These notes should work with any version since v"1.6.0". It is recommended to use a version v"1.9.0" or later, as there are significant speedups with external packages that make the user experience even better.

-

Once downloaded and installed the Julia installation will provide a command line for interactive usage and a binary to run scripts. It is envisioned most users will use an alternative interface, though Julia has an excellent REPL for command-line usage.

-

Some alternatives to the REPL for interacting with Julia are:

-
    -
  • IJulia: This is a means to use the Jupyter interactive environment to interact with Julia through notebooks. It is made available by installing the package IJulia (details on package installation follow below). This relies on Julia’s seamless interaction with Python and leverages many technologies developed for that langauge.
  • -
  • Pluto: The Pluto environment provides a notebook interface for Julia written in Julia leveraging many JavaScript technologies for the browser. It has the feature of being reactive, making it well suited for many exploratory tasks and pedagogical demonstrations.
  • -
  • Visual Studio Code: Julia is a supported language for the Visual Studio Code editor of Microsoft, a programmer’s IDE.
  • -
-

These notes use quarto to organize the mix of text, code, and graphics. The quarto publishing system is developed by Posit, the developers of the wildly sucessful RStudio interface for R. The code snippets are run as blocks (within IJulia) and the last command executed is shown. (If code is copy-and-pasted into the REPL, each line’s output will be displayed.) The code display occurs below the cell, as here, where we show that Julia can handle basic addition:

-
-
2 + 2
-
-
4
-
-
-
-
-

Overview of some basics

-

This section gives a quick orientation for using Julia. See this compiled collection of tutorials for more comprehensive introductions.

-

As will be seen, Julia use multiple dispatch (as does R) where different function methods can be called using the same generic name. Different methods are dispatched depending on the type and number of the arguments. The + sign above, is actually a function call to the + function, which in base Julia has over 200 different methods, as there are many different implementations for addition. For a beginner this is great – fewer new function names to remember.

-

Julia is a dynamically typed language, like R and Python, meaning variables can be reassigned to different values and with different types.1 Dynamicness makes interactive usage at the REPL or through a notebook much easier.

-

Julia supports the usual mathematical operations familiar to users of a calculator, such as +, -, *, /, and ^. In addition, there a numerous built in functions such as mathematical ones like sqrt or programming oriented ones, like map.

-

These functions are called with arguments which may be positional (\(0\), \(1,\) or more positional arguments) or specified by keywords. Multiple dispatch considers the positions and types of arguments a function is called with.

-
-
-
- -
-
-Interactive help -
-
-
-

Interacting with Julia primarily involves variables and functions. Most all functions have documentation, which can be called up by prefacing the function name with an immediate question mark, as in ?sqrt to see the documentation for sqrt. More than one method may be documented. A call like ?sqrt(9) will limit the help to the method called by sqrt(9) (the square root function for integers.)

-
-
-

Values in Julia have types. A particular instance will have a concrete type but abstract types help to organize code bases and participate in dispatch. Values can be assigned to variable names, or bindings. The ability to simply create new user-defined types makes generic programming quite accessible and Julia code very composable.

-

This simple example, taking the average of several numbers, shows most of this:

-
-
xs = [1, 2, 3, 7, 9]
-sum(xs) / length(xs)
-
-
4.4
-
-
-

The first line assigns to a variable, xs, a value that is a vector of numbers, integers of type Int64 in this case. For this illustration, a vector is a container of different numbers. The second line calls three functions: sum to add the elements in the vector; length to count the number of elements in the vector; and / to divide these two quantities. All of these functions are generic, with different methods for different types of argument(s). The same pattern would work for different container types, such as a tuple:

-
-
xs = (1, 2, 3, 7, 9) # tuple
-sum(xs) / length(xs)
-
-
4.4
-
-
-

The takeaway – we can focus more on what the computations mean, and less on how to program a particular computation.

-
-
-

Add-on packages

-

Base Julia provides a very useful programming environment which can be extended through packages. Some packages are provided by base Julia, such as Dates, others are external add-on packages, such as IJulia, mentioned previously. Julia has one key package, Pkg, to manage the installation. By default, the installation of a single package will download all dependent packages. On installation, packages are partially compiled. This speeds up the loading of a package when it is used within a session, but can slow down package installation.

-

Packages need be installed just once, but must be loaded each session. Loading a package is done by a command like using Statistics, which will load the built in Statistics package. At the REPL, calling using PKGNAME on an uninstalled package will lead to a prompt to install the package. For other interfaces, packages may need to be installed through the Pkg package, loaded through using Pkg.

-

When a package is loaded its exported functions are made available to use directly. Non-exported functions can be accessed by qualifying the function with the name of a module (conventionally the name of the package). For example, we will see the command CSV.read which calls the read function provided in the CSV package which has a CSV module.

-

Most packages are designed to extend generic functions that may be defined elsewhere. Not all. When there are conflicts, they can be resolved by either just importing the packages and qualifying all uses, or qualifying the uses that conflict.

-

These notes will utilize numerous add-on packages including:

- -

Most of these are maintained by the StatsBase organization, which provides the StatsKit package to load all these with a single command, though we don’t illustrate that.

-
-

Copyright 2023, John Verzani. All rights reserved.

-
-
-Alday, P., Kliegl, R., and Bates, D. (2022), Embrace uncertainty: Fitting mixed-effects models with julia, https://juliamixedmodels.github.io/EmbraceUncertainty/. -
-
-Bezanson, J., Edelman, A., Karpinski, S., and Shah, V. B. (2017), Julia: A fresh approach to numerical computing,” SIAM review, SIAM, 59, 65–98. -
-
-Kamiński, B. (2022), Julia for data analysis, Manning. -
-
-Nazarathy, Y., and Klok, H. (2021), Statistics with julia: Fundamentals for data science, machine learning and artificial intelligence, Springer. -
-
-Storopoli, J., Huijzer, R., and Alonso, L. (2021), Julia data science. -
-
-
-
-
-
-
    -
  1. With the one caveat that generic function names can not be reassigned as variables or vice versa.↩︎

  2. -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/index.qmd b/index.qmd index 0ed87a3..df0daf4 100644 --- a/index.qmd +++ b/index.qmd @@ -12,6 +12,7 @@ science, within which much of this material is covered. For example, [@storopolihuijzeralonso2021juliadatascience] is very well done, [@nazarathy2021statisticsjulia] covers topics here (cf. the [JuliaCon Workshop](https://www.youtube.com/watch?v=IlPoU5Yr2QI)). +[@RomeoAndJulia] covers many of the topics here with a Biologist's viewpoint. The quarto book [Embrace Uncertainty](https://juliamixedmodels.github.io/EmbraceUncertainty/) [@Embrace-Uncertainty-Fitting-Mixed-Effects-Models-with-Julia] covers the more advanced topic of Mixed-effects Models in `Julia`. Nothing here couldn't be found in those resources, these notes are just an introduction. diff --git a/references.bib b/references.bib index 5f550bd..cce369d 100644 --- a/references.bib +++ b/references.bib @@ -117,6 +117,14 @@ @Book{JuliaForDataAnalysis year = 2022, url = {https://www.manning.com/books/julia-for-data-analysis}} +@book{RomeoAndJulia, + title = {Romeo and Julia where Romeo is Basic Statistics}, + author = {B Lukaszuk}, + url = {https://b-lukaszuk.github.io/RJ_BS_eng/}, + year = {2023}, +} + + @article{JSSv107i04, title={DataFrames.jl: Flexible and Fast Tabular Data in Julia}, volume={107}, diff --git a/references.html b/references.html deleted file mode 100644 index 5296317..0000000 --- a/references.html +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - -references - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-

References

-
- -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/site_libs/bootstrap/bootstrap-icons.css b/site_libs/bootstrap/bootstrap-icons.css deleted file mode 100644 index f51d04b..0000000 --- a/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/site_libs/bootstrap/bootstrap-icons.woff b/site_libs/bootstrap/bootstrap-icons.woff deleted file mode 100644 index b26ccd1ac9f9f1fbc980e93531398364f6f03cd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137124 zcma%@WmHsO*tchh85%_d=?89+ekW&jlt>5?8mK%`q5lx_q;Ktj4Z z9P&N;zt;19cs@O@b*{PhZ(sYKea<=z1I*Gx=l*>d90r5oP=AIILy!31eE%Cm<^TSt zs`o?*27?noxioY0||Y(@`+kwH7G5My@3M+~Jw$D;RuR7h1;z9n8o&IKSgF z2Wuz;`;moC(#~BZw)T~iiz^JiQwoD|?ZIHrw~Cjt{5(^wES_6f%vs*GD7CV1etkgr zY_3Crm7f z=n=G8&(x)dwD8Z`oU?O@l*ViWIyOc;v0Jcr|m||MPEPduEz$rMP{kAw6Jj zU`0;PBS!euK=D_zSw{5x)b}DJB=<#H7uxo&hSA6c18lRo0qs?@c?~?TBOpDH^MiR3 zrmkGJShh?yN47#Xud%SPz_0M)^F^6>xp@vq{X0apR%VYZ0r+*z3m#BoaVEXF_hjC4o5ZZ_~@d-KHiiui2yY}Uhm&y<=r zq$6i<-e*Jg!WO1|Yj(U%giu=}c6d<)Ut3*ocvOT`TXSUiaL;s5O?bFZgt%X$Vt7*o z*{|+0{6~bmpHBX-uZTRK0`X6!%Da3@!KjC{T4BTUm3X9?9JVyH8b44*Pa_iYZlY9Z zAMgzKR1y_w6b!FdB8t@QhY6mhjAgpn%0A5y!;sptJ1A$PtR~-x<@BRmCWER!7oqGY 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%0Av_>K5 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:inherit}kbd{padding:.4rem .4rem;font-size:0.875em;color:#fff;background-color:#2d2d2d;border-radius:.2em}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;border-radius:.25rem;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: #6c757d;--bs-table-striped-bg: rgba(0, 0, 0, 0.05);--bs-table-active-color: #6c757d;--bs-table-active-bg: rgba(0, 0, 0, 0.1);--bs-table-hover-color: #6c757d;--bs-table-hover-bg: rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#6c757d;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: #dae2eb;--bs-table-striped-bg: #cfd7df;--bs-table-striped-color: #000;--bs-table-active-bg: #c4cbd4;--bs-table-active-color: #000;--bs-table-hover-bg: #cad1d9;--bs-table-hover-color: #000;color:#000;border-color:#c4cbd4}.table-secondary{--bs-table-bg: #eff0f2;--bs-table-striped-bg: #e3e4e6;--bs-table-striped-color: #000;--bs-table-active-bg: #d7d8da;--bs-table-active-color: #000;--bs-table-hover-bg: #dddee0;--bs-table-hover-color: #000;color:#000;border-color:#d7d8da}.table-success{--bs-table-bg: #d8f0d3;--bs-table-striped-bg: #cde4c8;--bs-table-striped-color: #000;--bs-table-active-bg: #c2d8be;--bs-table-active-color: #000;--bs-table-hover-bg: #c8dec3;--bs-table-hover-color: #000;color:#000;border-color:#c2d8be}.table-info{--bs-table-bg: #d6ebfd;--bs-table-striped-bg: #cbdff0;--bs-table-striped-color: #000;--bs-table-active-bg: #c1d4e4;--bs-table-active-color: #000;--bs-table-hover-bg: #c6d9ea;--bs-table-hover-color: #000;color:#000;border-color:#c1d4e4}.table-warning{--bs-table-bg: #f6e3cc;--bs-table-striped-bg: #ead8c2;--bs-table-striped-color: #000;--bs-table-active-bg: #ddccb8;--bs-table-active-color: #000;--bs-table-hover-bg: #e4d2bd;--bs-table-hover-color: #000;color:#000;border-color:#ddccb8}.table-danger{--bs-table-bg: #f5cccc;--bs-table-striped-bg: #e9c2c2;--bs-table-striped-color: #000;--bs-table-active-bg: #ddb8b8;--bs-table-active-color: #000;--bs-table-hover-bg: #e3bdbd;--bs-table-hover-color: #000;color:#000;border-color:#ddb8b8}.table-light{--bs-table-bg: #eee;--bs-table-striped-bg: #e2e2e2;--bs-table-striped-color: #000;--bs-table-active-bg: #d6d6d6;--bs-table-active-color: #000;--bs-table-hover-bg: gainsboro;--bs-table-hover-color: #000;color:#000;border-color:#d6d6d6}.table-dark{--bs-table-bg: #333;--bs-table-striped-bg: #3d3d3d;--bs-table-striped-color: #fff;--bs-table-active-bg: #474747;--bs-table-active-color: #fff;--bs-table-hover-bg: #424242;--bs-table-hover-color: #fff;color:#fff;border-color:#474747}.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:#6c757d;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:.25rem;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:#6c757d;background-color:#fff;border-color:#a2b7cd;outline:0;box-shadow:0 0 0 .25rem rgba(68,110,155,.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:#eee;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-0.375rem -0.75rem;margin-inline-end:.75rem;color:#6c757d;background-color:#eee;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:#e2e2e2}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-0.375rem -0.75rem;margin-inline-end:.75rem;color:#6c757d;background-color:#eee;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:#e2e2e2}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#6c757d;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;border-radius:.2em}.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;border-radius:.3rem}.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;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.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:#6c757d;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='%23333' 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:.25rem;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:#a2b7cd;outline:0;box-shadow:0 0 0 .25rem rgba(68,110,155,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#eee}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #6c757d}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:0.875rem;border-radius:.2em}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:.3rem}.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=checkbox],.shiny-input-container .checkbox input[type=checkbox],.shiny-input-container .checkbox-inline input[type=checkbox],.shiny-input-container .radio input[type=checkbox],.shiny-input-container .radio-inline input[type=checkbox]{border-radius:.25em}.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:#a2b7cd;outline:0;box-shadow:0 0 0 .25rem rgba(68,110,155,.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:#446e9b;border-color:#446e9b}.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:#446e9b;border-color:#446e9b;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;border-radius:2em;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='%23a2b7cd'/%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(68,110,155,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(68,110,155,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;background-color:#446e9b;border:0;border-radius:1rem;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:#c7d4e1}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#446e9b;border:0;border-radius:1rem;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:#c7d4e1}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.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:#6c757d;text-align:center;white-space:nowrap;background-color:#eee;border:1px solid #ced4da;border-radius:.25rem}.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;border-radius:.3rem}.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;border-radius:.2em}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:#3cb521}.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(60,181,33,.9);border-radius:.25rem}.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:#3cb521;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='%233cb521' 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:#3cb521;box-shadow:0 0 0 .25rem rgba(60,181,33,.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:#3cb521}.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='%23333' 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='%233cb521' 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:#3cb521;box-shadow:0 0 0 .25rem rgba(60,181,33,.25)}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:#3cb521}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:#3cb521}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(60,181,33,.25)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:#3cb521}.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:#cd0200}.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(205,2,0,.9);border-radius:.25rem}.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:#cd0200;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='%23cd0200'%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='%23cd0200' 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:#cd0200;box-shadow:0 0 0 .25rem rgba(205,2,0,.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:#cd0200}.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='%23333' 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='%23cd0200'%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='%23cd0200' 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:#cd0200;box-shadow:0 0 0 .25rem rgba(205,2,0,.25)}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:#cd0200}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:#cd0200}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .25rem rgba(205,2,0,.25)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:#cd0200}.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:#6c757d;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:.25rem;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:#6c757d}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(68,110,155,.25)}.btn:disabled,.btn.disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-default{color:#000;background-color:#adb5bd;border-color:#adb5bd}.btn-default:hover{color:#000;background-color:#b9c0c7;border-color:#b5bcc4}.btn-check:focus+.btn-default,.btn-default:focus{color:#000;background-color:#b9c0c7;border-color:#b5bcc4;box-shadow:0 0 0 .25rem rgba(147,154,161,.5)}.btn-check:checked+.btn-default,.btn-check:active+.btn-default,.btn-default:active,.btn-default.active,.show>.btn-default.dropdown-toggle{color:#000;background-color:#bdc4ca;border-color:#b5bcc4}.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(147,154,161,.5)}.btn-default:disabled,.btn-default.disabled{color:#000;background-color:#adb5bd;border-color:#adb5bd}.btn-primary{color:#fff;background-color:#446e9b;border-color:#446e9b}.btn-primary:hover{color:#fff;background-color:#3a5e84;border-color:#36587c}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#3a5e84;border-color:#36587c;box-shadow:0 0 0 .25rem rgba(96,132,170,.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:#36587c;border-color:#335374}.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(96,132,170,.5)}.btn-primary:disabled,.btn-primary.disabled{color:#fff;background-color:#446e9b;border-color:#446e9b}.btn-secondary{color:#000;background-color:#adb5bd;border-color:#adb5bd}.btn-secondary:hover{color:#000;background-color:#b9c0c7;border-color:#b5bcc4}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#000;background-color:#b9c0c7;border-color:#b5bcc4;box-shadow:0 0 0 .25rem rgba(147,154,161,.5)}.btn-check:checked+.btn-secondary,.btn-check:active+.btn-secondary,.btn-secondary:active,.btn-secondary.active,.show>.btn-secondary.dropdown-toggle{color:#000;background-color:#bdc4ca;border-color:#b5bcc4}.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(147,154,161,.5)}.btn-secondary:disabled,.btn-secondary.disabled{color:#000;background-color:#adb5bd;border-color:#adb5bd}.btn-success{color:#fff;background-color:#3cb521;border-color:#3cb521}.btn-success:hover{color:#fff;background-color:#339a1c;border-color:#30911a}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#339a1c;border-color:#30911a;box-shadow:0 0 0 .25rem rgba(89,192,66,.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:#30911a;border-color:#2d8819}.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(89,192,66,.5)}.btn-success:disabled,.btn-success.disabled{color:#fff;background-color:#3cb521;border-color:#3cb521}.btn-info{color:#fff;background-color:#3399f3;border-color:#3399f3}.btn-info:hover{color:#fff;background-color:#2b82cf;border-color:#297ac2}.btn-check:focus+.btn-info,.btn-info:focus{color:#fff;background-color:#2b82cf;border-color:#297ac2;box-shadow:0 0 0 .25rem rgba(82,168,245,.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:#297ac2;border-color:#2673b6}.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(82,168,245,.5)}.btn-info:disabled,.btn-info.disabled{color:#fff;background-color:#3399f3;border-color:#3399f3}.btn-warning{color:#fff;background-color:#d47500;border-color:#d47500}.btn-warning:hover{color:#fff;background-color:#b46300;border-color:#aa5e00}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#fff;background-color:#b46300;border-color:#aa5e00;box-shadow:0 0 0 .25rem rgba(218,138,38,.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:#aa5e00;border-color:#9f5800}.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(218,138,38,.5)}.btn-warning:disabled,.btn-warning.disabled{color:#fff;background-color:#d47500;border-color:#d47500}.btn-danger{color:#fff;background-color:#cd0200;border-color:#cd0200}.btn-danger:hover{color:#fff;background-color:#ae0200;border-color:#a40200}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#ae0200;border-color:#a40200;box-shadow:0 0 0 .25rem rgba(213,40,38,.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:#a40200;border-color:#9a0200}.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(213,40,38,.5)}.btn-danger:disabled,.btn-danger.disabled{color:#fff;background-color:#cd0200;border-color:#cd0200}.btn-light{color:#000;background-color:#eee;border-color:#eee}.btn-light:hover{color:#000;background-color:#f1f1f1;border-color:#f0f0f0}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f1f1f1;border-color:#f0f0f0;box-shadow:0 0 0 .25rem rgba(202,202,202,.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:#f1f1f1;border-color:#f0f0f0}.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(202,202,202,.5)}.btn-light:disabled,.btn-light.disabled{color:#000;background-color:#eee;border-color:#eee}.btn-dark{color:#fff;background-color:#333;border-color:#333}.btn-dark:hover{color:#fff;background-color:#2b2b2b;border-color:#292929}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#2b2b2b;border-color:#292929;box-shadow:0 0 0 .25rem rgba(82,82,82,.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:#292929;border-color:#262626}.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(82,82,82,.5)}.btn-dark:disabled,.btn-dark.disabled{color:#fff;background-color:#333;border-color:#333}.btn-outline-default{color:#adb5bd;border-color:#adb5bd;background-color:transparent}.btn-outline-default:hover{color:#000;background-color:#adb5bd;border-color:#adb5bd}.btn-check:focus+.btn-outline-default,.btn-outline-default:focus{box-shadow:0 0 0 .25rem rgba(173,181,189,.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:#000;background-color:#adb5bd;border-color:#adb5bd}.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(173,181,189,.5)}.btn-outline-default:disabled,.btn-outline-default.disabled{color:#adb5bd;background-color:transparent}.btn-outline-primary{color:#446e9b;border-color:#446e9b;background-color:transparent}.btn-outline-primary:hover{color:#fff;background-color:#446e9b;border-color:#446e9b}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(68,110,155,.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:#446e9b;border-color:#446e9b}.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(68,110,155,.5)}.btn-outline-primary:disabled,.btn-outline-primary.disabled{color:#446e9b;background-color:transparent}.btn-outline-secondary{color:#adb5bd;border-color:#adb5bd;background-color:transparent}.btn-outline-secondary:hover{color:#000;background-color:#adb5bd;border-color:#adb5bd}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(173,181,189,.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:#000;background-color:#adb5bd;border-color:#adb5bd}.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(173,181,189,.5)}.btn-outline-secondary:disabled,.btn-outline-secondary.disabled{color:#adb5bd;background-color:transparent}.btn-outline-success{color:#3cb521;border-color:#3cb521;background-color:transparent}.btn-outline-success:hover{color:#fff;background-color:#3cb521;border-color:#3cb521}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(60,181,33,.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:#3cb521;border-color:#3cb521}.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(60,181,33,.5)}.btn-outline-success:disabled,.btn-outline-success.disabled{color:#3cb521;background-color:transparent}.btn-outline-info{color:#3399f3;border-color:#3399f3;background-color:transparent}.btn-outline-info:hover{color:#fff;background-color:#3399f3;border-color:#3399f3}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(51,153,243,.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:#3399f3;border-color:#3399f3}.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(51,153,243,.5)}.btn-outline-info:disabled,.btn-outline-info.disabled{color:#3399f3;background-color:transparent}.btn-outline-warning{color:#d47500;border-color:#d47500;background-color:transparent}.btn-outline-warning:hover{color:#fff;background-color:#d47500;border-color:#d47500}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(212,117,0,.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:#d47500;border-color:#d47500}.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(212,117,0,.5)}.btn-outline-warning:disabled,.btn-outline-warning.disabled{color:#d47500;background-color:transparent}.btn-outline-danger{color:#cd0200;border-color:#cd0200;background-color:transparent}.btn-outline-danger:hover{color:#fff;background-color:#cd0200;border-color:#cd0200}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(205,2,0,.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:#cd0200;border-color:#cd0200}.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(205,2,0,.5)}.btn-outline-danger:disabled,.btn-outline-danger.disabled{color:#cd0200;background-color:transparent}.btn-outline-light{color:#eee;border-color:#eee;background-color:transparent}.btn-outline-light:hover{color:#000;background-color:#eee;border-color:#eee}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(238,238,238,.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:#eee;border-color:#eee}.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(238,238,238,.5)}.btn-outline-light:disabled,.btn-outline-light.disabled{color:#eee;background-color:transparent}.btn-outline-dark{color:#333;border-color:#333;background-color:transparent}.btn-outline-dark:hover{color:#fff;background-color:#333;border-color:#333}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(51,51,51,.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:#333;border-color:#333}.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(51,51,51,.5)}.btn-outline-dark:disabled,.btn-outline-dark.disabled{color:#333;background-color:transparent}.btn-link{font-weight:400;color:#3399f3;text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}.btn-link:hover{color:#297ac2}.btn-link:disabled,.btn-link.disabled{color:#6c757d}.btn-lg,.btn-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-sm,.btn-group-sm>.btn{padding:.25rem .5rem;font-size:0.875rem;border-radius:.2em}.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:#6c757d;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.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:#2d2d2d;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:#292929;background-color:#eee}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#446e9b}.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:#2d2d2d}.dropdown-menu-dark{color:#dee2e6;background-color:#333;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:#446e9b}.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}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn,.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.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}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn~.btn,.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}.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:#3399f3;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:#297ac2}.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;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{border-color:#eee #eee #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;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:none;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#446e9b}.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;border-radius:.25rem;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:#eee}.navbar-light .navbar-brand{color:#4f4f4f}.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:focus{color:#3399f3}.navbar-light .navbar-nav .nav-link{color:#4f4f4f}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-link:focus{color:rgba(51,153,243,.8)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(79,79,79,.75)}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .nav-link.active{color:#3399f3}.navbar-light .navbar-toggler{color:#4f4f4f;border-color:rgba(79,79,79,.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='%234f4f4f' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:#4f4f4f}.navbar-light .navbar-text a,.navbar-light .navbar-text a:hover,.navbar-light .navbar-text a:focus{color:#3399f3}.navbar-dark{background-color:#eee}.navbar-dark .navbar-brand{color:#4f4f4f}.navbar-dark .navbar-brand:hover,.navbar-dark .navbar-brand:focus{color:#3399f3}.navbar-dark .navbar-nav .nav-link{color:#4f4f4f}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-link:focus{color:rgba(51,153,243,.8)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(79,79,79,.75)}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active{color:#3399f3}.navbar-dark .navbar-toggler{color:#4f4f4f;border-color:rgba(79,79,79,.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='%234f4f4f' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:#4f4f4f}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:hover,.navbar-dark .navbar-text a:focus{color:#3399f3}.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);border-radius:.25rem}.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;border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.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-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:#adb5bd;border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.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;border-radius:calc(0.25rem - 1px)}.card-img,.card-img-top,.card-img-bottom{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.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}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-img-top,.card-group>.card:not(:last-child) .card-header{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-img-bottom,.card-group>.card:not(:last-child) .card-footer{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-img-top,.card-group>.card:not(:first-child) .card-header{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-img-bottom,.card-group>.card:not(:first-child) .card-footer{border-bottom-left-radius: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:#6c757d;text-align:left;background-color:#fff;border:0;border-radius: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:#3d638c;background-color:#ecf1f5;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='%233d638c'%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='%236c757d'%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:#a2b7cd;outline:0;box-shadow:0 0 0 .25rem rgba(68,110,155,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius: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:#3399f3;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:#297ac2;background-color:#eee;border-color:#dee2e6}.page-link:focus{z-index:3;color:#297ac2;background-color:#eee;outline:0;box-shadow:0 0 0 .25rem rgba(68,110,155,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#446e9b;border-color:#446e9b}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:0.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2em;border-bottom-left-radius:.2em}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2em;border-bottom-right-radius:.2em}.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;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.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:#686d71;background-color:#eff0f2;border-color:#e6e9eb}.alert-default .alert-link{color:#53575a}.alert-primary{color:#29425d;background-color:#dae2eb;border-color:#c7d4e1}.alert-primary .alert-link{color:#21354a}.alert-secondary{color:#686d71;background-color:#eff0f2;border-color:#e6e9eb}.alert-secondary .alert-link{color:#53575a}.alert-success{color:#246d14;background-color:#d8f0d3;border-color:#c5e9bc}.alert-success .alert-link{color:#1d5710}.alert-info{color:#1f5c92;background-color:#d6ebfd;border-color:#c2e0fb}.alert-info .alert-link{color:#194a75}.alert-warning{color:#7f4600;background-color:#f6e3cc;border-color:#f2d6b3}.alert-warning .alert-link{color:#663800}.alert-danger{color:#7b0100;background-color:#f5cccc;border-color:#f0b3b3}.alert-danger .alert-link{color:#620100}.alert-light{color:#8f8f8f;background-color:#fcfcfc;border-color:#fafafa}.alert-light .alert-link{color:#727272}.alert-dark{color:#1f1f1f;background-color:#d6d6d6;border-color:#c2c2c2}.alert-dark .alert-link{color:#191919}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;display:-webkit-flex;height:1rem;overflow:hidden;font-size:0.75rem;background-color:#eee;border-radius:.25rem}.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:#446e9b;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:1rem 1rem}.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;border-radius:.25rem}.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:#6c757d;background-color:#eee}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#2d2d2d;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:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.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:#446e9b;border-color:#446e9b}.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:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.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:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.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:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.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:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.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:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.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:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.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{border-radius:0}.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:#686d71;background-color:#eff0f2}.list-group-item-default.list-group-item-action:hover,.list-group-item-default.list-group-item-action:focus{color:#686d71;background-color:#d7d8da}.list-group-item-default.list-group-item-action.active{color:#fff;background-color:#686d71;border-color:#686d71}.list-group-item-primary{color:#29425d;background-color:#dae2eb}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#29425d;background-color:#c4cbd4}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#29425d;border-color:#29425d}.list-group-item-secondary{color:#686d71;background-color:#eff0f2}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#686d71;background-color:#d7d8da}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#686d71;border-color:#686d71}.list-group-item-success{color:#246d14;background-color:#d8f0d3}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#246d14;background-color:#c2d8be}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#246d14;border-color:#246d14}.list-group-item-info{color:#1f5c92;background-color:#d6ebfd}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#1f5c92;background-color:#c1d4e4}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#1f5c92;border-color:#1f5c92}.list-group-item-warning{color:#7f4600;background-color:#f6e3cc}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#7f4600;background-color:#ddccb8}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#7f4600;border-color:#7f4600}.list-group-item-danger{color:#7b0100;background-color:#f5cccc}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#7b0100;background-color:#ddb8b8}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#7b0100;border-color:#7b0100}.list-group-item-light{color:#8f8f8f;background-color:#fcfcfc}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#8f8f8f;background-color:#e3e3e3}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#8f8f8f;border-color:#8f8f8f}.list-group-item-dark{color:#1f1f1f;background-color:#d6d6d6}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#1f1f1f;background-color:#c1c1c1}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1f1f1f;border-color:#1f1f1f}.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;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(68,110,155,.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);border-radius:.25rem}.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);border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.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);border-radius:.3rem;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;border-top-left-radius:calc(0.3rem - 1px);border-top-right-radius:calc(0.3rem - 1px)}.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;border-bottom-right-radius:calc(0.3rem - 1px);border-bottom-left-radius:calc(0.3rem - 1px)}.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;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@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;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@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;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@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;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@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;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@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;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.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;border-radius:.25rem}.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);border-radius:.3rem}.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;color:#2d2d2d;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(0.3rem - 1px);border-top-right-radius:calc(0.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#6c757d}.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:#adb5bd}.link-default:hover,.link-default:focus{color:#bdc4ca}.link-primary{color:#446e9b}.link-primary:hover,.link-primary:focus{color:#36587c}.link-secondary{color:#adb5bd}.link-secondary:hover,.link-secondary:focus{color:#bdc4ca}.link-success{color:#3cb521}.link-success:hover,.link-success:focus{color:#30911a}.link-info{color:#3399f3}.link-info:hover,.link-info:focus{color:#297ac2}.link-warning{color:#d47500}.link-warning:hover,.link-warning:focus{color:#aa5e00}.link-danger{color:#cd0200}.link-danger:hover,.link-danger:focus{color:#a40200}.link-light{color:#eee}.link-light:hover,.link-light:focus{color:#f1f1f1}.link-dark{color:#333}.link-dark:hover,.link-dark:focus{color:#292929}.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:#adb5bd !important}.border-primary{border-color:#446e9b !important}.border-secondary{border-color:#adb5bd !important}.border-success{border-color:#3cb521 !important}.border-info{border-color:#3399f3 !important}.border-warning{border-color:#d47500 !important}.border-danger{border-color:#cd0200 !important}.border-light{border-color:#eee !important}.border-dark{border-color:#333 !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:#000}.bg-primary{color:#fff}.bg-secondary{color:#000}.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}}.sidebar-item .chapter-number{color:#6c757d}.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{overflow:hidden;text-overflow:ellipsis}.navbar-brand-container{max-width:calc(100% - 85px);min-width:0;display:flex;align-items:center;margin-right:1em}.navbar-brand.navbar-brand-logo{margin-right:4px;display:inline-flex}.navbar-toggler{flex-basis:content;flex-shrink:0}.navbar-logo{max-height:24px;width:auto;padding-right:4px}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:#595959;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:#595959;padding-top:4px}div.sidebar-item-container{color:#595959}div.sidebar-item-container:hover,div.sidebar-item-container:focus{color:rgba(30,90,143,.8)}div.sidebar-item-container.disabled{color:rgba(89,89,89,.75)}div.sidebar-item-container .active,div.sidebar-item-container .show>.nav-link,div.sidebar-item-container .sidebar-link>code{color:#1e5a8f}div.sidebar.sidebar-navigation.rollup.quarto-sidebar-toggle-contents,nav.sidebar.sidebar-navigation:not(.rollup){background-color:#fff}@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:#fff;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:#595959}.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:#297ac2}.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:#297ac2}.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}.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:64px}body:not(.floating) .nav-footer{border-top:1px solid #dee2e6}.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:#4f4f4f;border-radius:3px}.quarto-reader-toggle.reader.sidebar-tool .quarto-reader-toggle-btn{background-color:#595959;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:#4f4f4f;opacity:1}.navbar #quarto-search.type-overlay .aa-Autocomplete svg.aa-SubmitIcon{width:26px;height:26px;color:#4f4f4f;opacity:1}.aa-Autocomplete .aa-Form,.aa-DetachedFormContainer .aa-Form{align-items:center;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;color:#6c757d;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(68,110,155,.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:#6c757d;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:#6c757d;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:#6c757d;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:#6c757d;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:#6c757d;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:#6c757d;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:#6c757d;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:#446e9b}.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:#446e9b}.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:#5482b4}.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:#6c757d}.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:#ccdae9}.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:#6c757d}.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:#6c757d}.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:#4f4f4f}.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:#595959}.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:#6c757d;cursor:pointer;margin:0 0 0 .5em;padding:0 .5em}.aa-DetachedCancelButton:hover,.aa-DetachedCancelButton:focus{box-shadow:rgba(68,110,155,.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(108,117,125,.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:#6c757d;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:#6c757d;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:#a1a8ae;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:#3399f3}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:#a1a8ae;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:#3399f3}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:#a1a8ae;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:#3399f3}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:#a1a8ae;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:#3399f3}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:#a1a8ae;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:#3399f3}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:#6c757d;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}.zindex-bottom{z-index:-1 !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{padding-top:.5rem;padding-bottom:.5rem;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}p code:not(.sourceCode){white-space:pre-wrap}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: #6c757d;--quarto-text-muted: #6c757d;--quarto-border-color: #dee2e6;--quarto-border-width: 1px;--quarto-border-radius: 0.25rem}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:transparent;border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:transparent;border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:transparent}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}.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}.code-copy-button-tooltip{font-size:.75em}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:#eee;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:#afb5ba}.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:#afb5ba}.table-caption,caption{color:#6c757d}.quarto-layout-cell[data-ref-parent] caption{color:#afb5ba}.column-margin figcaption,.margin-caption,div.aside,aside,.column-margin{color:#afb5ba;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(238,238,238,.65);border:1px solid rgba(238,238,238,.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:#afb5ba}.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:#f9f9fa;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 #eee;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 #3399f3;color:#3399f3 !important}.sidebar nav[role=doc-toc] ul>li>a.active{border-left:1px solid #3399f3;color:#3399f3 !important}kbd,.kbd{color:#6c757d;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:#446e9b}div.callout-note.callout-style-default>.callout-header{background-color:#ecf1f5}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:#3cb521}div.callout-tip.callout-style-default>.callout-header{background-color:#ecf8e9}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:#d47500}div.callout-warning.callout-style-default>.callout-header{background-color:#fbf1e6}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:#fd7e14}div.callout-caution.callout-style-default>.callout-header{background-color:#fff2e8}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:#cd0200}div.callout-important.callout-style-default>.callout-header{background-color:#fae6e6}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;align-items:center}@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:#6c757d}.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:#26282a;background-color:#adb5bd;border-color:#adb5bd}.btn.btn-quarto:hover,div.cell-output-display .btn-quarto:hover{color:#26282a;background-color:#b9c0c7;border-color:#b5bcc4}.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:#26282a;background-color:#b9c0c7;border-color:#b5bcc4;box-shadow:0 0 0 .25rem rgba(153,160,167,.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:#000;background-color:#bdc4ca;border-color:#b5bcc4}.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(153,160,167,.5)}.btn.btn-quarto:disabled,.btn.btn-quarto.disabled,div.cell-output-display .btn-quarto:disabled,div.cell-output-display .btn-quarto.disabled{color:#000;background-color:#adb5bd;border-color:#adb5bd}nav.quarto-secondary-nav.color-navbar{background-color:#eee;color:#4f4f4f}nav.quarto-secondary-nav.color-navbar h1,nav.quarto-secondary-nav.color-navbar .h1,nav.quarto-secondary-nav.color-navbar .quarto-btn-toggle{color:#4f4f4f}@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 */div.observablehq table thead tr th{background-color:var(--bs-body-bg)}input,button,select,optgroup,textarea{background-color:var(--bs-body-bg)}@media print{.page-columns .column-screen-inset{grid-column:page-start-inset/page-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:page-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/page-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:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:page-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/page-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:page-start-inset/page-end-inset;padding:1em;background:#eee;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}}.quarto-video{margin-bottom:1em}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}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file,.code-with-filename .code-with-filename-file pre{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file,.quarto-dark .code-with-filename .code-with-filename-file pre{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.quarto-title-banner{margin-bottom:1em;color:#4f4f4f;background:#eee}.quarto-title-banner .code-tools-button{color:#828282}.quarto-title-banner .code-tools-button:hover{color:#4f4f4f}.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:.75em}.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:#6c757d}#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}.navbar .nav-link,.navbar .navbar-brand{text-shadow:-1px -1px 0 rgba(0,0,0,.1);transition:color ease-in-out .2s}.navbar.bg-default{background-image:linear-gradient(#c1c7cd, #adb5bd 50%, #9fa7ae);filter:none;border:1px solid #8a9197}.navbar.bg-primary{background-image:linear-gradient(#7191b3, #446e9b 50%, #3f658f);filter:none;border:1px solid #36587c}.navbar.bg-secondary{background-image:linear-gradient(#c1c7cd, #adb5bd 50%, #9fa7ae);filter:none;border:1px solid #8a9197}.navbar.bg-success{background-image:linear-gradient(#6bc756, #3cb521 50%, #37a71e);filter:none;border:1px solid #30911a}.navbar.bg-info{background-image:linear-gradient(#64b1f6, #3399f3 50%, #2f8de0);filter:none;border:1px solid #297ac2}.navbar.bg-warning{background-image:linear-gradient(#de963d, #d47500 50%, #c36c00);filter:none;border:1px solid #aa5e00}.navbar.bg-danger{background-image:linear-gradient(#d93f3d, #cd0200 50%, #bd0200);filter:none;border:1px solid #a40200}.navbar.bg-light{background-image:linear-gradient(#f2f2f2, #eee 50%, #dbdbdb);filter:none;border:1px solid #bebebe}.navbar.bg-dark{background-image:linear-gradient(#646464, #333 50%, #2f2f2f);filter:none;border:1px solid #292929}.navbar.bg-light .nav-link,.navbar.bg-light .navbar-brand,.navbar.navbar-default .nav-link,.navbar.navbar-default .navbar-brand{text-shadow:1px 1px 0 rgba(255,255,255,.1)}.navbar.bg-light .navbar-brand,.navbar.navbar-default .navbar-brand{color:#4f4f4f}.navbar.bg-light .navbar-brand:hover,.navbar.navbar-default .navbar-brand:hover{color:#3399f3}.btn{text-shadow:-1px -1px 0 rgba(0,0,0,.1)}.btn-link{text-shadow:none}.btn-default{background-image:linear-gradient(#c1c7cd, #adb5bd 50%, #9fa7ae);filter:none;border:1px solid #8a9197}.btn-default:not(.disabled):hover{background-image:linear-gradient(#b6bcc1, #9fa7ae 50%, #929aa0);filter:none;border:1px solid #7f868b}.btn-primary{background-image:linear-gradient(#7191b3, #446e9b 50%, #3f658f);filter:none;border:1px solid #36587c}.btn-primary:not(.disabled):hover{background-image:linear-gradient(#6d8aaa, #3f658f 50%, #3a5d84);filter:none;border:1px solid #325172}.btn-secondary{background-image:linear-gradient(#c1c7cd, #adb5bd 50%, #9fa7ae);filter:none;border:1px solid #8a9197}.btn-secondary:not(.disabled):hover{background-image:linear-gradient(#b6bcc1, #9fa7ae 50%, #929aa0);filter:none;border:1px solid #7f868b}.btn-success{background-image:linear-gradient(#6bc756, #3cb521 50%, #37a71e);filter:none;border:1px solid #30911a}.btn-success:not(.disabled):hover{background-image:linear-gradient(#67bc54, #37a71e 50%, #339a1c);filter:none;border:1px solid #2c8618}.btn-info{background-image:linear-gradient(#64b1f6, #3399f3 50%, #2f8de0);filter:none;border:1px solid #297ac2}.btn-info:not(.disabled):hover{background-image:linear-gradient(#61a8e7, #2f8de0 50%, #2b82ce);filter:none;border:1px solid #2671b3}.btn-warning{background-image:linear-gradient(#de963d, #d47500 50%, #c36c00);filter:none;border:1px solid #aa5e00}.btn-warning:not(.disabled):hover{background-image:linear-gradient(#d18f3d, #c36c00 50%, #b36300);filter:none;border:1px solid #9c5600}.btn-danger{background-image:linear-gradient(#d93f3d, #cd0200 50%, #bd0200);filter:none;border:1px solid #a40200}.btn-danger:not(.disabled):hover{background-image:linear-gradient(#cd3f3d, #bd0200 50%, #ae0200);filter:none;border:1px solid #970200}.btn-light{background-image:linear-gradient(#f2f2f2, #eee 50%, #dbdbdb);filter:none;border:1px solid #bebebe}.btn-light:not(.disabled):hover{background-image:linear-gradient(#e4e4e4, #dbdbdb 50%, #c9c9c9);filter:none;border:1px solid #afafaf}.btn-dark{background-image:linear-gradient(#646464, #333 50%, #2f2f2f);filter:none;border:1px solid #292929}.btn-dark:not(.disabled):hover{background-image:linear-gradient(#616161, #2f2f2f 50%, #2b2b2b);filter:none;border:1px solid #262626}[class*=btn-outline-]{text-shadow:none}.badge.bg-light{color:#333}.card h1,.card .h1,.card h2,.card .h2,.card h3,.card .h3,.card h4,.card .h4,.card h5,.card .h5,.card h6,.card .h6,.list-group-item h1,.list-group-item .h1,.list-group-item h2,.list-group-item .h2,.list-group-item h3,.list-group-item .h3,.list-group-item h4,.list-group-item .h4,.list-group-item h5,.list-group-item .h5,.list-group-item h6,.list-group-item .h6{color:inherit}/*# sourceMappingURL=603954f6f730b7a48ae583e90c07e56e.css.map */ diff --git a/site_libs/bootstrap/bootstrap.min.js b/site_libs/bootstrap/bootstrap.min.js deleted file mode 100644 index cc0a255..0000000 --- a/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/site_libs/clipboard/clipboard.min.js b/site_libs/clipboard/clipboard.min.js deleted file mode 100644 index 41c6a0f..0000000 --- a/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/site_libs/quarto-html/popper.min.js b/site_libs/quarto-html/popper.min.js deleted file mode 100644 index 2269d66..0000000 --- a/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/site_libs/quarto-html/quarto-syntax-highlighting.css b/site_libs/quarto-html/quarto-syntax-highlighting.css deleted file mode 100644 index 36cb328..0000000 --- a/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); - }); - - // fire slideEnter for tabby tab activations (for htmlwidget resize behavior) - document.addEventListener("tabby", fireSlideEnter, false); - - // 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.overflow = "hidden"; - } - - 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; - child.style.overflow = 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 > * " - ); - - nexttick(() => { - let lastBottom = 0; - for (const marginChild of marginChildren) { - const top = marginChild.getBoundingClientRect().top + window.scrollY; - 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()); -}); - -// grouped tabsets -window.addEventListener("pageshow", (_event) => { - function getTabSettings() { - const data = localStorage.getItem("quarto-persistent-tabsets-data"); - if (!data) { - localStorage.setItem("quarto-persistent-tabsets-data", "{}"); - return {}; - } - if (data) { - return JSON.parse(data); - } - } - - function setTabSettings(data) { - localStorage.setItem( - "quarto-persistent-tabsets-data", - JSON.stringify(data) - ); - } - - function setTabState(groupName, groupValue) { - const data = getTabSettings(); - data[groupName] = groupValue; - setTabSettings(data); - } - - function toggleTab(tab, active) { - const tabPanelId = tab.getAttribute("aria-controls"); - const tabPanel = document.getElementById(tabPanelId); - if (active) { - tab.classList.add("active"); - tabPanel.classList.add("active"); - } else { - tab.classList.remove("active"); - tabPanel.classList.remove("active"); - } - } - - function toggleAll(selectedGroup, selectorsToSync) { - for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) { - const active = selectedGroup === thisGroup; - for (const tab of tabs) { - toggleTab(tab, active); - } - } - } - - function findSelectorsToSyncByLanguage() { - const result = {}; - const tabs = Array.from( - document.querySelectorAll(`div[data-group] a[id^='tabset-']`) - ); - for (const item of tabs) { - const div = item.parentElement.parentElement.parentElement; - const group = div.getAttribute("data-group"); - if (!result[group]) { - result[group] = {}; - } - const selectorsToSync = result[group]; - const value = item.innerHTML; - if (!selectorsToSync[value]) { - selectorsToSync[value] = []; - } - selectorsToSync[value].push(item); - } - return result; - } - - function setupSelectorSync() { - const selectorsToSync = findSelectorsToSyncByLanguage(); - Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => { - Object.entries(tabSetsByValue).forEach(([value, items]) => { - items.forEach((item) => { - item.addEventListener("click", (_event) => { - setTabState(group, value); - toggleAll(value, selectorsToSync[group]); - }); - }); - }); - }); - return selectorsToSync; - } - - const selectorsToSync = setupSelectorSync(); - for (const [group, selectedName] of Object.entries(getTabSettings())) { - const selectors = selectorsToSync[group]; - // it's possible that stale state gives us empty selections, so we explicitly check here. - if (selectors) { - toggleAll(selectedName, selectors); - } - } -}); - -function throttle(func, wait) { - let waiting = false; - return function () { - if (!waiting) { - func.apply(this, arguments); - waiting = true; - setTimeout(function () { - waiting = false; - }, wait); - } - }; -} - -function nexttick(func) { - return setTimeout(func, 0); -} diff --git a/site_libs/quarto-html/tippy.css b/site_libs/quarto-html/tippy.css deleted file mode 100644 index e6ae635..0000000 --- a/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/site_libs/quarto-html/tippy.umd.min.js b/site_libs/quarto-html/tippy.umd.min.js deleted file mode 100644 index ca292be..0000000 --- a/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/site_libs/quarto-nav/headroom.min.js b/site_libs/quarto-nav/headroom.min.js deleted file mode 100644 index b08f1df..0000000 --- a/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/site_libs/quarto-nav/quarto-nav.js b/site_libs/quarto-nav/quarto-nav.js deleted file mode 100644 index b41b31e..0000000 --- a/site_libs/quarto-nav/quarto-nav.js +++ /dev/null @@ -1,222 +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"); - linkStyle.setAttribute("id", "quarto-target-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( - updateDocumentOffsetWithoutAnimation - ); - observer.observe(headerEl, { - attributes: true, - childList: true, - characterData: true, - }); - } else { - window.addEventListener( - "resize", - throttle(updateDocumentOffsetWithoutAnimation, 50) - ); - } - setTimeout(updateDocumentOffsetWithoutAnimation, 250); - - // 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/site_libs/quarto-search/autocomplete.umd.js b/site_libs/quarto-search/autocomplete.umd.js deleted file mode 100644 index 3f2dcf0..0000000 --- a/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/site_libs/quarto-search/fuse.min.js b/site_libs/quarto-search/fuse.min.js deleted file mode 100644 index ca37378..0000000 --- a/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/site_libs/quarto-search/quarto-search.js b/site_libs/quarto-search/quarto-search.js deleted file mode 100644 index 6fd4b5b..0000000 --- a/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), - }; - }); -} From 5d436891148cbc7b107efafa5aef14a3d3394a18 Mon Sep 17 00:00:00 2001 From: jverzani Date: Wed, 3 Sep 2025 12:14:55 -0400 Subject: [PATCH 3/4] WIP --- EDA/tabular-data-julia.qmd | 160 ++++++++++++++++++++++++++++++++++-- Inference/distributions.qmd | 50 ++++++----- Inference/inference.qmd | 8 +- Project.toml | 8 ++ README-quarto.md | 7 ++ _quarto.yml | 11 ++- 6 files changed, 208 insertions(+), 36 deletions(-) create mode 100644 README-quarto.md diff --git a/EDA/tabular-data-julia.qmd b/EDA/tabular-data-julia.qmd index 8ff7a24..4fe8b7a 100644 --- a/EDA/tabular-data-julia.qmd +++ b/EDA/tabular-data-julia.qmd @@ -673,17 +673,27 @@ flatten(dxy, :value) The split-apply-combine strategy for wrangling data frames was popularized in the influential paper "*The Split-Apply-Combine Strategy for Data Analysis*: [@JSSv040i01] by H. Wickham which introduces this description: "where you break up a big problem into manageable pieces, operate on each piece independently and then put all the pieces back together." +#### Grouping data The `groupby` function for data frames does the breaking up, the `DataFrames` mini language can operate on each piece, and the `combine` function can put things together again. -The `groupby` function splits a data frame into pieces of type `GroupedDataFrame`. The basic signature is `groupby(df, cols; sort=nothing, skipmissing=false)`. The `cols` are one or more column selectors. The value of `sort` defaults to `nothing` leaving the order of the groups in the result undefined, but the grouping uses the fastest identified algorithm. The `skipmissing` argument can instruct the skipping of rows which have missing values in the selected columns. +Grouping takes one or more *categorical variables* and creates a "grouped" data frame for each different set of *combined* levels. That is one grouped data frame for each possible combination of the levels. +The `groupby` function splits the data frame into pieces of type `GroupedDataFrame`. The basic signature is `groupby(df, cols; sort=nothing, skipmissing=false)`. The `cols` are one or more column selectors. The value of `sort` defaults to `nothing` leaving the order of the groups in the result undefined, but the grouping uses the fastest identified algorithm. The `skipmissing` argument can instruct the skipping of rows which have missing values in the selected columns. -For example, grouping by loading style, returns two sub data frames. +For example, grouping the baggage data by loading style, returns two sub data frames. ```{julia} gdf = groupby(d, :load) ``` +Grouping by more than one variable is achieved by passing in a selector with multiple variables. The following also creates two sub data frames, but only because the unique combination of both load and date are the same as just load: + +```{julia} +gdf = groupby(d, [:load, :d]) +``` + + + Indices may be used to get each sub data frame. Moreover, the `keys` method returns keys, like indices, to efficiently look up the different sub data frames in `gdf`. The levels for the keys are found in the property `.load`, as with `keys(gdf)[1].load`. Grouped data frames may be iterated over; the `pairs` iterator iterates over both keys and values. The `select` and `subset` functions work over `GroupedDataFrame` objects, here we see the returned values are combined: @@ -694,12 +704,18 @@ subset(gdf, :p => ByRow(<=(350))) (The command `[subset(g, :p => ByRow(<=(350))) for g in gdf]` avoids the `combine` step.) +#### Group and combine + We can see that `combine` also works over `GroupedDataFrame` objects. This example shows how to find the mean city mileage of groups formed by the value of `:Origin` in the `cars` data set: ```{julia} combine(groupby(cars, :Origin), :MPGCity => mean) ``` +Typically, the function applied to a column is a reduction, which results in *aggregated data*. + +By default the keys used to group (`:Origin`) are kept by `combine`. The `keepkeys` argument for `combine` when applied to grouped data controls this feature. + More than one column can be used to group the data. This example first groups by the number of cylinders, then groups by origin. For each of the 9 resulting groups, the group mean for city mileage is taken: ```{julia} @@ -857,7 +873,7 @@ end #### The SplitApplyCombine package There is support for the split-apply-combine paradigm outside of the `DataFrames` package in the package `SplitApplyCombine`. This package is much lighter weight than `DataFrames`. -This support targets other representations of tabular data, such as a vector of nested tuples: +This support targets other representations of tabular data, such as a vector of named tuples: ```{julia} @@ -871,13 +887,13 @@ tbl = [ Many of the main verbs are generic functions: `filter`, `map`, `reduce`; `group` does grouping. There are some others. A few examples follow. -For indexing this structure, we can't index by `[row, col]`, rather we can use `[row][col]` notation, the first to extract the row, the second to access the entries in the column. For example, he we index a selected row by position, by name, and then with multiple names: +For indexing this structure, we can't index by `[row, col]`, rather we can use `[row][col]` notation, the first to extract the row, the second to access the entries in the column. For example, here we index a selected column by position, by name, and then with multiple names: ```{julia} tbl[2][2], tbl[2].v, tbl[2][ [:v, :p] ] ``` -The `invert` function reverses the access pattern, allowing in this case, `[col][row]` access, with: +The `invert` function from `SplitApplyCombine` reverses the access pattern, allowing, in this case, `[col][row]` access, with: ```{julia} itbl = invert(tbl) @@ -1059,13 +1075,29 @@ cars_price = select(cars, 1:3, r"Price" => ByRow(_mean) => :price) first(cars_price, 3) ``` -To aggregate over columns, the `groupby` construct can be used to specify the groupings to aggregate over. In the following this is the car type: +To aggregate over columns, the `groupby` construct can be used to specify the groupings to aggregate over. In the following this is the car type. The `Chain` style is used to make the steps more explicit: + +```{julia} +@chain cars_price begin + groupby(:Type) + combine(:price => mean => :avg_price) +end +``` + +As `mean` is a reduction, the result has one row for each unique set of levels in the column selector specified to `groupby`. Again, that these columns appear in the output is due to the chosen default value of`keepkeys`. + +This similar example shows grouping by two variables and then aggregating miles per gallon over each: ```{julia} -combine(groupby(cars_price, :Type), :price => mean => :avg_price) +@chain cars begin + groupby([:Type, :DriveTrain]) + combine(:MPGHighway => mean => :avg_MPG) + sort(:avg_MPG, rev=true) +end ``` + ### Tidy data; stack, unstack The split-apply-combine paradigm suggest that storing data so that it can be split readily is preferable to other ways of storage. The notion of "tidy data," [@tidy-data] as presented in R's `tidyr` package and described in this [vignette](https://cran.r-project.org/web/packages/tidyr/vignettes/tidy-data.html) suggests data sets should follow: @@ -1078,7 +1110,119 @@ These terms are defined by > A dataset is a collection of values, usually either numbers (if quantitative) or strings (if qualitative). Values are organised in two ways. Every value belongs to a variable and an observation. A variable contains all values that measure the same underlying attribute (like height, temperature, duration) across units. An observation contains all values measured on the same unit (like a person, or a day, or a race) across attributes. -In addition to `flatten`, the `DataFrames` package provides the functions `stack` and `unstack` for tidying data. We illustrate with an abbreviated version of an example in the `tidyr` vignette. +In addition to `flatten`, the `DataFrames` package provides the functions `stack` and `unstack` for tidying data. + +::: {.callout-note} + +### Stack and unstack +The `stack` and `unstack` commands are used to move from "wide" format, where related values are stored in multiple columns with column names representing some level, to "long" format, where related values are stored in one column, and their categorical levels in another column. + +For example, this wide format as the attributes are in columns `:A` and `:B`; the values in columns `:C` and `:D`. + +```{julia} +wide = DataFrame(A=["A","B"], B=["a","b"], C=["c1","c2"], D=["d1","d2"]) +``` + +With the `stack` function, the values are placed into a single column (by default `:value`) and the variable names placed into a single column (by default `:variable`): + +```{julia} +long = stack(wide, [:C, :D]) +``` + +The values in the `:variable` column come from the data frame names. + +With `unstack` the data can be spread back out into "wide" format. We pass in the two columns indicating the variable and the values to `unstack`: + +```{julia} +unstack(long, :variable, :value) +``` + + +```{julia} +#| echo: false +#| eval: false + +# output: asis +# Picture of stack and unstack +This figure may help visualize the process. + +using Vizagrams + +using Random +# 376x240 ratio for figure +function to_quarto(plt; height=240, pad=10, caption="", label=randstring(5)) + fname = "XXX-" * "-" * label * ".png" + Vizagrams.savefig(plt; filename=fname, height=height, pad=pad) + str = "![$caption]($fname){#fig-$label}" + print(str) +end + +struct Brace <: Mark + y + center +end +Brace() = Brace(1/8, [0,0]) +Brace(y) = Brace(y, [0,0]) +function Vizagrams.ζ(b::Brace) + x = 1/2 + y = b.y + r = y/2 + bpts = [[0,0],[r,r],[x-r,y-r],[x,y]] .- Ref(b.center) .- Ref([1/2, 0]) + cpts = [ [r/2,r], [1/2,r], [x-r/2,y-r]] .- Ref(b.center) .- Ref([1/2, 0]) + xx = QBezier(bpts, cpts) + xx → (Vizagrams.M(pi/2)*xx) +end + +# ----- +struct TextCell <: Mark + txt + fill +end +TextCell(txt) = TextCell(txt, :red) +TextCell() = TextCell("") +function Vizagrams.ζ(r::TextCell) + sq = S(:fill=>r.fill,:fillOpacity=>0.25)*S(:stroke=>:black,:strokeWidth=>3) * Rectangle(; h=1,w=1) + sq + TextMark(;text = r.txt, fontsize=1/4) +end + +# ------ +# wide format +B = TextCell(".") + +P = TextCell(":A") → TextCell(":B") → ((TextCell(":C") → TextCell(":D")) ↑ (T(0,1/20),T(1/2,0)U(1.8)Brace() ) ) +cell_colors = (:yellow,:yellow,:blue,:blue) +P = P ↓ foldl(↓, [ + foldl(→, TextCell.(("A","a","c1","d1"),cell_colors)) + foldl(→, TextCell.(("B","b","c2","d2"),cell_colors)) + ]) + +# long format +Q = foldl(→, TextCell.((":A",":B",":var", ":value"))) +cell_colors = (:yellow,:yellow,:red,:blue) +Q = Q ↓ foldl(↓, [ + foldl(→, TextCell.(("A","a","C","c1"),cell_colors)) + foldl(→, TextCell.(("B","b","C","c2"),cell_colors)) + foldl(→, TextCell.(("A","a","D","d1"),cell_colors)) + foldl(→, TextCell.(("B","b","D","d2"),cell_colors)) + ]) +A = Vizagrams.R(3pi/4)Arrow() → Vizagrams.R(-pi/4)*Arrow() +plt = ((T(-.1,0),P) → (T(-.1,0),A) → Q) + +to_quarto(plt; + label="stack-unstack", + caption = "Illustration of wide and long formats. The `stack` and `unstack` functions can reshape data from one to the other." + ) + +![Illustration of wide and long formats. The `stack` and `unstack` functions can reshape data from one to the other.](XXX--stack-unstack.svg) +nothing +``` + +::: + + + + +We illustrate this further with an abbreviated version of an example in the `tidyr` vignette. Consider this data from the Global Historical Climatology Network for one weather station (MX17004) in Mexico for five months in 2010. The full data has 31 days per month, this abbreviated data works with just the first 8: diff --git a/Inference/distributions.qmd b/Inference/distributions.qmd index 1563c37..fa96d60 100644 --- a/Inference/distributions.qmd +++ b/Inference/distributions.qmd @@ -157,7 +157,7 @@ These imprecise statements can be made more precise, as will be seen. Figure @fi ```{julia} #| echo: false #| label: fig-pop-parameters-sample -#| fig-cap: The top-left figure shows a population, the lower left parameters associated with the population. The top-right shows one sample from the population in red, other possible samples in blue with sample means summarized in the bottom most row. A density estimate of these sample means appears in the lower right figure showing how it is centered on the population, but more concentrated. Inference uses the language of probability to characterize a population parameter based on a single random sample. +#| fig-cap: The top-left figure shows a population, the lower left parameters associated with the population. The top-right shows one sample from the population in red, other possible samples in blue with sample means summarized in the bottom most row. A density estimate of these sample means appears in the lower-right figure showing how it is centered on the population, but more concentrated. Inference uses the language of probability to characterize a population parameter based on a single random sample. μ, σ = 0, 1 P = Normal(μ, σ) xs = range(-3, 3, length=251) @@ -482,14 +482,16 @@ In @fig-qqplots-distributions the lower-right graphic is of the uniform distrib #| label: fig-qqplots-distributions #| fig-cap: Quantile-normal plots for different distributions -- $T_3$ is leptokurtic; $T_{100}$ is approximately normal; $U$ is platykurtic; and $E$ is skewed. + probs = range(0.01, 0.99, 40) -Ds = (T₃ = TDist(3), T₁₀₀=TDist(100), U=Uniform(0,1), E=Exponential(1)) -d = DataFrame(D=collect(keys(Ds)), qs = [quantile.(D,probs) for D in Ds]) -d = flatten(d, :qs) -data(d) * visual(QQNorm, qqline=:fit) * mapping(:qs, main=:D, color=:D, layout=:D) |> draw +Ds = (N = Normal(0,1), T₃ = TDist(3), T₁₀₀=TDist(100), + U=Uniform(0,1), E=Exponential(1)) +m = DataFrame([quantile.(D, probs) for D in Ds],collect(keys(Ds))) +d = stack(m, Not(:N)) # need to use QQPlot, so replicate the N +data(d) * mapping(:N, :value, layout=:variable => nonnumeric) * + (visual(QQPlot, qqline=:fit)) |> draw ``` - Consider now two, independent Chi-squared random variables $Y_1$ and $Y_2$ with $\nu_1$ and $\nu_2$ degrees of freedom. The ratio $$ @@ -508,12 +510,18 @@ kurtosis(FDist(5, 10)), kurtosis(FDist(5,100)), kurtosis(FDist(100,100)) #| echo: false #| label: fig-f-distributions #| fig-cap: Quantile-normal plots of the $F$ distribution for different degrees of freedom. As the degrees of freedom values get bigger, the distribution is more normal. -νs = [(1, 10), (5,10), (5, 100), (100, 100)] + probs = range(0.01, 0.99, 40) -d = DataFrame(D = ["F($ν₁,$ν₂)" for (ν₁, ν₂) ∈ νs], - qs = [quantile.(FDist(ν...), probs) for ν ∈ νs]) -d = flatten(d, :qs) -data(d) * visual(QQNorm, qqline=:fit) * mapping(:qs, main=:D, color=:D, layout=:D) |> draw +Ds = (N = Normal(0,1), + var"F(1,10)" = FDist(1,10), + var"F(5,10)" = FDist(5,10), + var"F(5,100)" = FDist(5,100), + var"F(100, 100)" = FDist(100,100) + ) +m = DataFrame([quantile.(D, probs) for D in Ds],collect(keys(Ds))) +d = stack(m, Not(:N)) # need to use QQPlot, so replicate the N +data(d) * mapping(:N, :value, layout=:variable => nonnumeric) * + (visual(QQPlot, qqline=:fit)) |> draw ``` @@ -552,15 +560,17 @@ In general, how large $n$ needs to be depends on the underlying shape of the *po xbar(D, n, N=200) = [mean(rand(D, n)) for _ in 1:N] sxbar(D, n, N=200) = (xbar(D,n,N) .- mean(D)) / (std(D)/sqrt(n)) -Ds = (normal = Normal(0,1), +Ds = (N = Normal(0,1), + normal = Normal(0,1), leptokurtic = TDist(3), skewed = FDist(2,5), platykurtic = Uniform(0,1)) -df = DataFrame(D=collect(keys(Ds)), zs = [sxbar(D, 10) for D in values(Ds)]) -d = data(flatten(df, :zs)) -p = d * visual(QQNorm, qqline=:fit) * - mapping(:zs, layout = :D, main=:D, color=:D) +probs = range(0.01, 0.99, 200) +m = DataFrame([quantile.((sxbar(D,10),), probs) for D in Ds],collect(keys(Ds))) +d = stack(m, Not(:N)) # need to use QQPlot +p = data(d) * mapping(:N, :value, layout=:variable => nonnumeric) * + (visual(QQPlot, qqline=:fit)) draw(p) ``` @@ -663,9 +673,9 @@ Pops = (var"long-tailed"=TDist(3), skewed=Exponential(1)) # use scatter to produce qqplot df = DataFrame([(D="$SDk $Popk", x=quantile(Popv, ps), y = quantile(rand(SDv, N), ps)) for (SDk,SDv) in pairs(SDs) for (Popk, Popv) in pairs(Pops)]) - -p = data(flatten(df, [:x,:y])) * (visual(Scatter) + linear(;interval=nothing)) * - mapping(:x, :y, main=:D, layout=:D, color=:D) +d = data(flatten(df, [:x,:y])) +p = d * (visual(Scatter) + linear(;interval=nothing)) * + mapping(:x, :y, layout=:D, color=:D) draw(p, facet=(; linkxaxes=:none, linkyaxes=:none)) ``` @@ -696,7 +706,7 @@ ds = [combine(DataFrame(;z= [MXbar(D,10) for _ in 1:100]), d = stack(reduce(append!, ds), [:M, :Xbar]) p = data(d) * visual(BoxPlot) * - mapping(:variable, :value, color=:variable, layout=:D, main=:D) + mapping(:variable, :value, color=:variable, layout=:D) draw(p) ``` diff --git a/Inference/inference.qmd b/Inference/inference.qmd index 002f14d..a017214 100644 --- a/Inference/inference.qmd +++ b/Inference/inference.qmd @@ -37,7 +37,7 @@ xs = rand(Y, n); ys = zeros(n); d = data((x=xs, y=ys)) p = deepcopy(p₀) p = p + d * visual(Scatter) * mapping(:x,:y) -p = p + d * visual(Density; color=:black, alpha=0.15) * mapping(:x) +p = p + d * visual(Density; colormap=(:gray10, 0.15)) * mapping(:x) draw(p; axis=(; title="n = $n")) ``` @@ -49,7 +49,7 @@ xs = rand(Y, n); ys = zeros(n); d = data((x=xs, y=ys)) p = deepcopy(p₀) p = p + d * visual(Scatter) * mapping(:x,:y) -p = p + d * visual(Density; color=:black, alpha=0.15) * mapping(:x) +p = p + d * visual(Density; colormap=(:gray10, 0.15)) * mapping(:x) draw(p; axis=(; title="n = $n")) ``` @@ -61,7 +61,7 @@ xs = rand(Y, n); ys = zeros(n); d = data((x=xs, y=ys)) p = deepcopy(p₀) p = p + d * visual(Scatter) * mapping(:x,:y) -p = p + d * visual(Density; color=:black, alpha=0.15) * mapping(:x) +p = p + d * visual(Density; colormap=(:gray10, 0.15)) * mapping(:x) draw(p; axis=(; title="n = $n")) ``` @@ -73,7 +73,7 @@ xs = rand(Y, n); ys = zeros(n); d = data((x=xs, y=ys)) p = deepcopy(p₀) p = p + d * visual(Scatter) * mapping(:x,:y) -p = p + d * visual(Density; color=:black, alpha=0.15) * mapping(:x) +p = p + d * visual(Density; colormap=(:gray10, 0.15)) * mapping(:x) draw(p; axis=(; title="n = $n")) ``` diff --git a/Project.toml b/Project.toml index 2e5f9c0..c77bc10 100644 --- a/Project.toml +++ b/Project.toml @@ -4,15 +4,23 @@ BoxCoxTrans = "4c24c29e-1bf7-59e2-a6c7-e500a6482ce4" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" FreqTables = "da1fdf0e-e0ff-5433-a45f-9bb5ff651cb1" GLM = "38e38edf-8417-5370-95a0-9cbb8c7f171a" GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" +HypothesisTests = "09f84164-cd44-5f33-b23f-e6b0d136a0d5" Loess = "4345ca2d-374a-55d4-8d30-97f9976e7612" +Optim = "429524aa-4258-5aef-a3af-852621145aeb" +OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e" +PairPlots = "43a3c2be-4208-490b-832a-a21dcd55d7da" +PalmerPenguins = "8b842266-38fa-440a-9b57-31493939ab85" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" PowerAnalyses = "fee4b835-1841-44f8-82ea-42813857171a" ProfileLikelihood = "3fca794e-44fa-49a6-b6a0-8cd45572ba0a" RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b" RobustModels = "d6ea1423-9682-4bbd-952f-b1577cbf8c98" +SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" StatsModels = "3eaba693-59b7-5ba5-a881-562e759f1c8d" StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd" +Vizagrams = "8c229dad-8b3a-4031-83d6-73545c88426d" diff --git a/README-quarto.md b/README-quarto.md new file mode 100644 index 0000000..073bfd9 --- /dev/null +++ b/README-quarto.md @@ -0,0 +1,7 @@ +To get this published + +``` +quarto render # check for Errors... +quarto publish gh-pages --no-render + +``` diff --git a/_quarto.yml b/_quarto.yml index 18189b5..66b0df0 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -1,9 +1,11 @@ +version: "0.0.5" +#jupyter: julia-1.10 +engine: julia + project: type: book output-dir: _book -version: "0.0.4" -jupyter: julia-1.10 book: title: "Using Julia for Introductory Statistics" @@ -27,9 +29,10 @@ book: - EDA/makie.qmd - Inference/distributions.qmd - Inference/inference.qmd +# - Inference/permutation_tests.qmd - LinearModels/linear-regression.qmd - - references.qmd # - LinearModels/glm.qmd + - references.qmd bibliography: references.bib csl: the-american-statistician.csl @@ -63,4 +66,4 @@ format: execute: warning: false error: true - freeze: auto + freeze: false #auto From ebcc704162d422fff11617a3e4b916b753f51fe2 Mon Sep 17 00:00:00 2001 From: jverzani Date: Wed, 3 Sep 2025 15:49:50 -0400 Subject: [PATCH 4/4] rm vizagrams attempt --- EDA/tabular-data-julia.qmd | 84 -------------------------------------- _quarto.yml | 6 --- 2 files changed, 90 deletions(-) diff --git a/EDA/tabular-data-julia.qmd b/EDA/tabular-data-julia.qmd index 4a1243a..8eed7b1 100644 --- a/EDA/tabular-data-julia.qmd +++ b/EDA/tabular-data-julia.qmd @@ -1138,90 +1138,6 @@ unstack(long, :variable, :value) ``` -```{julia} -#| echo: false -#| eval: false - -# output: asis -# Picture of stack and unstack -This figure may help visualize the process. - -using Vizagrams - -using Random -# 376x240 ratio for figure -function to_quarto(plt; height=240, pad=10, caption="", label=randstring(5)) - fname = "XXX-" * "-" * label * ".png" - Vizagrams.savefig(plt; filename=fname, height=height, pad=pad) - str = "![$caption]($fname){#fig-$label}" - print(str) -end - -struct Brace <: Mark - y - center -end -Brace() = Brace(1/8, [0,0]) -Brace(y) = Brace(y, [0,0]) -function Vizagrams.ζ(b::Brace) - x = 1/2 - y = b.y - r = y/2 - bpts = [[0,0],[r,r],[x-r,y-r],[x,y]] .- Ref(b.center) .- Ref([1/2, 0]) - cpts = [ [r/2,r], [1/2,r], [x-r/2,y-r]] .- Ref(b.center) .- Ref([1/2, 0]) - xx = QBezier(bpts, cpts) - xx → (Vizagrams.M(pi/2)*xx) -end - -# ----- -struct TextCell <: Mark - txt - fill -end -TextCell(txt) = TextCell(txt, :red) -TextCell() = TextCell("") -function Vizagrams.ζ(r::TextCell) - sq = S(:fill=>r.fill,:fillOpacity=>0.25)*S(:stroke=>:black,:strokeWidth=>3) * Rectangle(; h=1,w=1) - sq + TextMark(;text = r.txt, fontsize=1/4) -end - -# ------ -# wide format -B = TextCell(".") - -P = TextCell(":A") → TextCell(":B") → ((TextCell(":C") → TextCell(":D")) ↑ (T(0,1/20),T(1/2,0)U(1.8)Brace() ) ) -cell_colors = (:yellow,:yellow,:blue,:blue) -P = P ↓ foldl(↓, [ - foldl(→, TextCell.(("A","a","c1","d1"),cell_colors)) - foldl(→, TextCell.(("B","b","c2","d2"),cell_colors)) - ]) - -# long format -Q = foldl(→, TextCell.((":A",":B",":var", ":value"))) -cell_colors = (:yellow,:yellow,:red,:blue) -Q = Q ↓ foldl(↓, [ - foldl(→, TextCell.(("A","a","C","c1"),cell_colors)) - foldl(→, TextCell.(("B","b","C","c2"),cell_colors)) - foldl(→, TextCell.(("A","a","D","d1"),cell_colors)) - foldl(→, TextCell.(("B","b","D","d2"),cell_colors)) - ]) -A = Vizagrams.R(3pi/4)Arrow() → Vizagrams.R(-pi/4)*Arrow() -plt = ((T(-.1,0),P) → (T(-.1,0),A) → Q) - -to_quarto(plt; - label="stack-unstack", - caption = "Illustration of wide and long formats. The `stack` and `unstack` functions can reshape data from one to the other." - ) - -![Illustration of wide and long formats. The `stack` and `unstack` functions can reshape data from one to the other.](XXX--stack-unstack.svg) -nothing -``` - -::: - - - - We illustrate this further with an abbreviated version of an example in the `tidyr` vignette. Consider this data from the Global Historical Climatology Network for one weather station (MX17004) in Mexico for five months in 2010. The full data has 31 days per month, this abbreviated data works with just the first 8: diff --git a/_quarto.yml b/_quarto.yml index 315b9ce..6c29c12 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -1,11 +1,5 @@ -<<<<<<< HEAD -version: "0.0.5" -#jupyter: julia-1.10 -engine: julia -======= version: "0.0.4" engines: ['julia'] ->>>>>>> main project: type: book